text: // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
  // for details. All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  #include <utility>
  
  #include "vm/isolate.h"
  
  #include "include/dart_api.h"
  #include "include/dart_native_api.h"
  #include "platform/assert.h"
  #include "platform/atomic.h"
  #include "platform/text_buffer.h"
  #include "vm/class_finalizer.h"
  #include "vm/code_observers.h"
  #include "vm/compiler/jit/compiler.h"
  #include "vm/dart_api_message.h"
  #include "vm/dart_api_state.h"
  #include "vm/dart_entry.h"
  #include "vm/debugger.h"
  #include "vm/deopt_instructions.h"
  #include "vm/dispatch_table.h"
  #include "vm/flags.h"
  #include "vm/heap/heap.h"
  #include "vm/heap/pointer_block.h"
  #include "vm/heap/safepoint.h"
  #include "vm/heap/verifier.h"
  #include "vm/image_snapshot.h"
  #include "vm/isolate_reload.h"
  #include "vm/kernel_isolate.h"
  #include "vm/lockers.h"
  #include "vm/log.h"
  #include "vm/message_handler.h"
  #include "vm/message_snapshot.h"
  #include "vm/object.h"
  #include "vm/object_id_ring.h"
  #include "vm/object_store.h"
  #include "vm/os_thread.h"
  #include "vm/port.h"
  #include "vm/profiler.h"
  #include "vm/reusable_handles.h"
  #include "vm/reverse_pc_lookup_cache.h"
  #include "vm/service.h"
  #include "vm/service_event.h"
  #include "vm/service_isolate.h"
  #include "vm/simulator.h"
  #include "vm/stack_frame.h"
  #include "vm/stub_code.h"
  #include "vm/symbols.h"
  #include "vm/tags.h"
  #include "vm/thread.h"
  #include "vm/thread_interrupter.h"
  #include "vm/thread_registry.h"
  #include "vm/timeline.h"
  #include "vm/visitor.h"
  
  #if !defined(DART_PRECOMPILED_RUNTIME)
  #include "vm/compiler/assembler/assembler.h"
  #include "vm/compiler/stub_code_compiler.h"
  #endif
  
  namespace dart {
  
  DECLARE_FLAG(bool, print_metrics);
  DECLARE_FLAG(bool, trace_service);
  DECLARE_FLAG(bool, trace_shutdown);
  DECLARE_FLAG(bool, warn_on_pause_with_no_debugger);
  
  // Reload flags.
  DECLARE_FLAG(int, reload_every);
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  DECLARE_FLAG(bool, check_reloaded);
  DECLARE_FLAG(bool, reload_every_back_off);
  DECLARE_FLAG(bool, trace_reload);
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
  static void DeterministicModeHandler(bool value) {
    if (value) {
      FLAG_background_compilation = false;  // Timing dependent.
      FLAG_concurrent_mark = false;         // Timing dependent.
      FLAG_concurrent_sweep = false;        // Timing dependent.
      FLAG_scavenger_tasks = 0;             // Timing dependent.
      FLAG_random_seed = 0x44617274;        // "Dart"
    }
  }
  
  DEFINE_FLAG_HANDLER(DeterministicModeHandler,
                      deterministic,
                      "Enable deterministic mode.");
  
  int FLAG_sound_null_safety = kNullSafetyOptionUnspecified;
  static void SoundNullSafetyHandler(bool value) {
    FLAG_sound_null_safety =
        value ? kNullSafetyOptionStrong : kNullSafetyOptionWeak;
  }
  
  DEFINE_FLAG_HANDLER(SoundNullSafetyHandler,
                      sound_null_safety,
                      "Respect the nullability of types at runtime.");
  
  DEFINE_FLAG(bool,
              disable_thread_pool_limit,
              false,
              "Disables the limit of the thread pool (simulates custom embedder "
              "with custom message handler on unlimited number of threads).");
  
  // Quick access to the locally defined thread() and isolate() methods.
  #define T (thread())
  #define I (isolate())
  #define IG (isolate_group())
  
  #if defined(DEBUG)
  // Helper class to ensure that a live origin_id is never reused
  // and assigned to an isolate.
  class VerifyOriginId : public IsolateVisitor {
   public:
    explicit VerifyOriginId(Dart_Port id) : id_(id) {}
  
    void VisitIsolate(Isolate* isolate) { ASSERT(isolate->origin_id() != id_); }
  
   private:
    Dart_Port id_;
    DISALLOW_COPY_AND_ASSIGN(VerifyOriginId);
  };
  #endif
  
  static std::unique_ptr<Message> SerializeMessage(Dart_Port dest_port,
                                                   const Instance& obj) {
    return WriteMessage(/* can_send_any_object */ false, /* same_group */ false,
                        obj, dest_port, Message::kNormalPriority);
  }
  
  static std::unique_ptr<Message> SerializeMessage(Zone* zone,
                                                   Dart_Port dest_port,
                                                   Dart_CObject* obj) {
    return WriteApiMessage(zone, obj, dest_port, Message::kNormalPriority);
  }
  
  void IsolateGroupSource::add_loaded_blob(
      Zone* zone,
      const ExternalTypedData& external_typed_data) {
    Array& loaded_blobs = Array::Handle();
    bool saved_external_typed_data = false;
    if (loaded_blobs_ != nullptr) {
      loaded_blobs = loaded_blobs_;
  
      // Walk the array, and (if stuff was removed) compact and reuse the space.
      // Note that the space has to be compacted as the ordering is important.
      WeakProperty& weak_property = WeakProperty::Handle();
      WeakProperty& weak_property_tmp = WeakProperty::Handle();
      ExternalTypedData& existing_entry = ExternalTypedData::Handle(zone);
      intptr_t next_entry_index = 0;
      for (intptr_t i = 0; i < loaded_blobs.Length(); i++) {
        weak_property ^= loaded_blobs.At(i);
        if (weak_property.key() != ExternalTypedData::null()) {
          if (i != next_entry_index) {
            existing_entry = ExternalTypedData::RawCast(weak_property.key());
            weak_property_tmp ^= loaded_blobs.At(next_entry_index);
            weak_property_tmp.set_key(existing_entry);
          }
          next_entry_index++;
        }
      }
      if (next_entry_index < loaded_blobs.Length()) {
        // There's now space to re-use.
        weak_property ^= loaded_blobs.At(next_entry_index);
        weak_property.set_key(external_typed_data);
        next_entry_index++;
        saved_external_typed_data = true;
      }
      if (next_entry_index < loaded_blobs.Length()) {
        ExternalTypedData& nullExternalTypedData =
            ExternalTypedData::Handle(zone);
        while (next_entry_index < loaded_blobs.Length()) {
          // Null out any extra spaces.
          weak_property ^= loaded_blobs.At(next_entry_index);
          weak_property.set_key(nullExternalTypedData);
          next_entry_index++;
        }
      }
    }
    if (!saved_external_typed_data) {
      const WeakProperty& weak_property =
          WeakProperty::Handle(WeakProperty::New(Heap::kOld));
      weak_property.set_key(external_typed_data);
  
      intptr_t length = loaded_blobs.IsNull() ? 0 : loaded_blobs.Length();
      Array& new_array =
          Array::Handle(Array::Grow(loaded_blobs, length + 1, Heap::kOld));
      new_array.SetAt(length, weak_property);
      loaded_blobs_ = new_array.ptr();
    }
    num_blob_loads_++;
  }
  
  void IdleTimeHandler::InitializeWithHeap(Heap* heap) {
    MutexLocker ml(&mutex_);
    ASSERT(heap_ == nullptr && heap != nullptr);
    heap_ = heap;
  }
  
  bool IdleTimeHandler::ShouldCheckForIdle() {
    MutexLocker ml(&mutex_);
    return idle_start_time_ > 0 && FLAG_idle_timeout_micros != 0 &&
           disabled_counter_ == 0;
  }
  
  void IdleTimeHandler::UpdateStartIdleTime() {
    MutexLocker ml(&mutex_);
    if (disabled_counter_ == 0) {
      idle_start_time_ = OS::GetCurrentMonotonicMicros();
    }
  }
  
  bool IdleTimeHandler::ShouldNotifyIdle(int64_t* expiry) {
    const int64_t now = OS::GetCurrentMonotonicMicros();
  
    MutexLocker ml(&mutex_);
    if (idle_start_time_ > 0 && disabled_counter_ == 0) {
      const int64_t expiry_time = idle_start_time_ + FLAG_idle_timeout_micros;
      if (expiry_time < now) {
        idle_start_time_ = 0;
        return true;
      }
    }
  
    *expiry = now + FLAG_idle_timeout_micros;
    return false;
  }
  
  void IdleTimeHandler::NotifyIdle(int64_t deadline) {
    {
      MutexLocker ml(&mutex_);
      disabled_counter_++;
    }
    if (heap_ != nullptr) {
      heap_->NotifyIdle(deadline);
    }
    {
      MutexLocker ml(&mutex_);
      disabled_counter_--;
      idle_start_time_ = 0;
    }
  }
  
  void IdleTimeHandler::NotifyIdleUsingDefaultDeadline() {
    const int64_t now = OS::GetCurrentMonotonicMicros();
    NotifyIdle(now + FLAG_idle_timeout_micros);
  }
  
  DisableIdleTimerScope::DisableIdleTimerScope(IdleTimeHandler* handler)
      : handler_(handler) {
    if (handler_ != nullptr) {
      MutexLocker ml(&handler_->mutex_);
      ++handler_->disabled_counter_;
      handler_->idle_start_time_ = 0;
    }
  }
  
  DisableIdleTimerScope::~DisableIdleTimerScope() {
    if (handler_ != nullptr) {
      MutexLocker ml(&handler_->mutex_);
      --handler_->disabled_counter_;
      ASSERT(handler_->disabled_counter_ >= 0);
    }
  }
  
  class FinalizeWeakPersistentHandlesVisitor : public HandleVisitor {
   public:
    explicit FinalizeWeakPersistentHandlesVisitor(IsolateGroup* isolate_group)
        : HandleVisitor(Thread::Current()), isolate_group_(isolate_group) {}
  
    void VisitHandle(uword addr) {
      auto handle = reinterpret_cast<FinalizablePersistentHandle*>(addr);
      handle->UpdateUnreachable(isolate_group_);
    }
  
   private:
    IsolateGroup* isolate_group_;
  
    DISALLOW_COPY_AND_ASSIGN(FinalizeWeakPersistentHandlesVisitor);
  };
  
  void MutatorThreadPool::OnEnterIdleLocked(MonitorLocker* ml) {
    if (FLAG_idle_timeout_micros == 0) return;
  
    // If the isolate has not started running application code yet, we ignore the
    // idle time.
    if (!isolate_group_->initial_spawn_successful()) return;
  
    int64_t idle_expiry = 0;
    // Obtain the idle time we should wait.
    if (isolate_group_->idle_time_handler()->ShouldNotifyIdle(&idle_expiry)) {
      MonitorLeaveScope mls(ml);
      NotifyIdle();
      return;
    }
  
    // Wait for the recommended idle timeout.
    // We can be woken up because of a), b) or c)
    const auto result =
        ml->WaitMicros(idle_expiry - OS::GetCurrentMonotonicMicros());
  
    // a) If there are new tasks we have to run them.
    if (TasksWaitingToRunLocked()) return;
  
    // b) If the thread pool is shutting down we're done.
    if (ShuttingDownLocked()) return;
  
    // c) We timed out and should run the idle notifier.
    if (result == Monitor::kTimedOut &&
        isolate_group_->idle_time_handler()->ShouldNotifyIdle(&idle_expiry)) {
      MonitorLeaveScope mls(ml);
      NotifyIdle();
      return;
    }
  
    // There must've been another thread doing active work in the meantime.
    // If that thread becomes idle and is the last idle thread it will run this
    // code again.
  }
  
  void MutatorThreadPool::NotifyIdle() {
    EnterIsolateGroupScope isolate_group_scope(isolate_group_);
    isolate_group_->idle_time_handler()->NotifyIdleUsingDefaultDeadline();
  }
  
  IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
                             void* embedder_data,
                             ObjectStore* object_store,
                             Dart_IsolateFlags api_flags)
      : shared_class_table_(new SharedClassTable()),
        class_table_(new ClassTable(shared_class_table_.get())),
        cached_class_table_table_(class_table_->table()),
        object_store_(object_store),
        embedder_data_(embedder_data),
        thread_pool_(),
        isolates_lock_(new SafepointRwLock()),
        isolates_(),
        start_time_micros_(OS::GetCurrentMonotonicMicros()),
        is_system_isolate_group_(source->flags.is_system_isolate),
        random_(),
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
        last_reload_timestamp_(OS::GetCurrentTimeMillis()),
        reload_every_n_stack_overflow_checks_(FLAG_reload_every),
  #endif
        source_(std::move(source)),
        api_state_(new ApiState()),
        thread_registry_(new ThreadRegistry()),
        safepoint_handler_(new SafepointHandler(this)),
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
        reload_handler_(new ReloadHandler()),
  #endif
        store_buffer_(new StoreBuffer()),
        heap_(nullptr),
        saved_unlinked_calls_(Array::null()),
        initial_field_table_(new FieldTable(/*isolate=*/nullptr)),
  #if !defined(DART_PRECOMPILED_RUNTIME)
        background_compiler_(new BackgroundCompiler(this)),
  #endif
        symbols_mutex_(NOT_IN_PRODUCT("IsolateGroup::symbols_mutex_")),
        type_canonicalization_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::type_canonicalization_mutex_")),
        type_arguments_canonicalization_mutex_(NOT_IN_PRODUCT(
            "IsolateGroup::type_arguments_canonicalization_mutex_")),
        subtype_test_cache_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::subtype_test_cache_mutex_")),
        megamorphic_table_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::megamorphic_table_mutex_")),
        type_feedback_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::type_feedback_mutex_")),
        patchable_call_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::patchable_call_mutex_")),
        constant_canonicalization_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::constant_canonicalization_mutex_")),
        kernel_data_lib_cache_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::kernel_data_lib_cache_mutex_")),
        kernel_data_class_cache_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::kernel_data_class_cache_mutex_")),
        kernel_constants_mutex_(
            NOT_IN_PRODUCT("IsolateGroup::kernel_constants_mutex_")),
        field_list_mutex_(NOT_IN_PRODUCT("Isolate::field_list_mutex_")),
        boxed_field_list_(GrowableObjectArray::null()),
        program_lock_(new SafepointRwLock()),
        active_mutators_monitor_(new Monitor()),
        max_active_mutators_(Scavenger::MaxMutatorThreadCount())
  #if !defined(PRODUCT)
        ,
        debugger_(new GroupDebugger(this))
  #endif
  {
    FlagsCopyFrom(api_flags);
    const bool is_vm_isolate = Dart::VmIsolateNameEquals(source_->name);
    if (!is_vm_isolate) {
      thread_pool_.reset(
          new MutatorThreadPool(this, FLAG_disable_thread_pool_limit
                                          ? 0
                                          : Scavenger::MaxMutatorThreadCount()));
    }
    {
      WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
      id_ = isolate_group_random_->NextUInt64();
    }
  }
  
  IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
                             void* embedder_data,
                             Dart_IsolateFlags api_flags)
      : IsolateGroup(source, embedder_data, new ObjectStore(), api_flags) {
    if (object_store() != nullptr) {
      object_store()->InitStubs();
    }
  }
  
  IsolateGroup::~IsolateGroup() {
    // 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_;
    }
  
  #if !defined(PRODUCT)
    delete debugger_;
    debugger_ = nullptr;
  #endif
  }
  
  void IsolateGroup::RegisterIsolate(Isolate* isolate) {
    {
      SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
      ASSERT(isolates_lock_->IsCurrentThreadWriter());
      isolates_.Append(isolate);
      isolate_count_++;
    }
    NOT_IN_PRODUCT(NOT_IN_PRECOMPILED(reload_handler()->RegisterIsolate()));
  }
  
  bool IsolateGroup::ContainsOnlyOneIsolate() {
    SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get());
    // We do allow 0 here as well, because the background compiler might call
    // this method while the mutator thread is in shutdown procedure and
    // unregistered itself already.
    return isolate_count_ == 0 || isolate_count_ == 1;
  }
  
  void IsolateGroup::RunWithLockedGroup(std::function<void()> fun) {
    SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
    fun();
  }
  
  void IsolateGroup::UnregisterIsolate(Isolate* isolate) {
    NOT_IN_PRODUCT(NOT_IN_PRECOMPILED(reload_handler()->UnregisterIsolate()));
    {
      SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
      isolates_.Remove(isolate);
    }
  }
  
  bool IsolateGroup::UnregisterIsolateDecrementCount(Isolate* isolate) {
    SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
    isolate_count_--;
    return isolate_count_ == 0;
  }
  
  void IsolateGroup::CreateHeap(bool is_vm_isolate,
                                bool is_service_or_kernel_isolate) {
    Heap::Init(this, is_vm_isolate,
               is_vm_isolate
                   ? 0  // New gen size 0; VM isolate should only allocate in old.
                   : FLAG_new_gen_semi_max_size * MBInWords,
               (is_service_or_kernel_isolate ? kDefaultMaxOldGenHeapSize
                                             : FLAG_old_gen_heap_size) *
                   MBInWords);
  
    is_vm_isolate_heap_ = is_vm_isolate;
  
  #define ISOLATE_GROUP_METRIC_CONSTRUCTORS(type, variable, name, unit)          \
    metric_##variable##_.InitInstance(this, name, nullptr, Metric::unit);
    ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_CONSTRUCTORS)
  #undef ISOLATE_GROUP_METRIC_CONSTRUCTORS
  }
  
  void IsolateGroup::Shutdown() {
    char* name;
    // We retrieve the flag value once to avoid the compiler complaining about the
    // possibly uninitialized value of name, as the compiler is unaware that when
    // the flag variable is non-const, it is set once during VM initialization and
    // never changed after, and that modification never runs concurrently with
    // this method.
    const bool trace_shutdown = FLAG_trace_shutdown;
  
    if (trace_shutdown) {
      name = Utils::StrDup(source()->name);
      OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutdown starting for group %s\n",
                   Dart::UptimeMillis(), name);
    }
    // Ensure to join all threads before waiting for pending GC tasks (the thread
    // pool can trigger idle notification, which can start new GC tasks).
    //
    // (The vm-isolate doesn't have a thread pool.)
    if (!Dart::VmIsolateNameEquals(source()->name)) {
      ASSERT(thread_pool_ != nullptr);
      thread_pool_->Shutdown();
      thread_pool_.reset();
    }
  
    // Wait for any pending GC tasks.
    if (heap_ != nullptr) {
      // Wait for any concurrent GC tasks to finish before shutting down.
      // TODO(rmacnak): Interrupt tasks for faster shutdown.
      PageSpace* old_space = heap_->old_space();
      MonitorLocker ml(old_space->tasks_lock());
      while (old_space->tasks() > 0) {
        ml.Wait();
      }
      // Needs to happen before ~PageSpace so TLS and the thread registery are
      // still valid.
      old_space->AbandonMarkingForShutdown();
    }
  
    UnregisterIsolateGroup(this);
  
    // If the creation of the isolate group (or the first isolate within the
    // isolate group) failed, we do not invoke the cleanup callback (the
    // embedder is responsible for handling the creation error).
    if (initial_spawn_successful_) {
      auto group_shutdown_callback = Isolate::GroupCleanupCallback();
      if (group_shutdown_callback != nullptr) {
        group_shutdown_callback(embedder_data());
      }
    }
  
    delete this;
  
    // After this isolate group has died we might need to notify a pending
    // `Dart_Cleanup()` call.
    {
      if (trace_shutdown) {
        OS::PrintErr("[+%" Pd64
                     "ms] SHUTDOWN: Notifying "
                     "isolate group shutdown (%s)\n",
                     Dart::UptimeMillis(), name);
      }
      MonitorLocker ml(Isolate::isolate_creation_monitor_);
      if (!Isolate::creation_enabled_ &&
          !IsolateGroup::HasApplicationIsolateGroups()) {
        ml.Notify();
      }
      if (trace_shutdown) {
        OS::PrintErr("[+%" Pd64
                     "ms] SHUTDOWN: Done Notifying "
                     "isolate group shutdown (%s)\n",
                     Dart::UptimeMillis(), name);
      }
    }
    if (trace_shutdown) {
      OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done shutdown for group %s\n",
                   Dart::UptimeMillis(), name);
      free(name);
    }
  }
  
  void IsolateGroup::set_heap(std::unique_ptr<Heap> heap) {
    idle_time_handler_.InitializeWithHeap(heap.get());
    heap_ = std::move(heap);
  }
  
  void IsolateGroup::set_saved_unlinked_calls(const Array& saved_unlinked_calls) {
    saved_unlinked_calls_ = saved_unlinked_calls.ptr();
  }
  
  Thread* IsolateGroup::ScheduleThreadLocked(MonitorLocker* ml,
                                             Thread* existing_mutator_thread,
                                             bool is_vm_isolate,
                                             bool is_mutator,
                                             bool bypass_safepoint) {
    ASSERT(threads_lock()->IsOwnedByCurrentThread());
  
    // Schedule the thread into the isolate group by associating
    // a 'Thread' structure with it (this is done while we are holding
    // the thread registry lock).
    Thread* thread = nullptr;
    OSThread* os_thread = OSThread::Current();
    if (os_thread != nullptr) {
      // If a safepoint operation is in progress wait for it to finish before
      // scheduling this thread.
      while (!bypass_safepoint && safepoint_handler()->AnySafepointInProgress()) {
        ml->Wait();
      }
  
      if (is_mutator) {
        if (existing_mutator_thread == nullptr) {
          // Allocate a new [Thread] structure for the mutator thread.
          thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
        } else {
          // Reuse the existing cached [Thread] structure for the mutator thread.,
          // see comment in 'base_isolate.h'.
          thread_registry()->AddToActiveListLocked(existing_mutator_thread);
          thread = existing_mutator_thread;
        }
      } else {
        thread = thread_registry()->GetFreeThreadLocked(is_vm_isolate);
      }
  
      // Now get a free Thread structure.
      ASSERT(thread != nullptr);
  
      // Set up other values and set the TLS value.
      thread->isolate_ = nullptr;
      thread->isolate_group_ = this;
      thread->field_table_values_ = nullptr;
  #if defined(DART_PRECOMPILED_RUNTIME)
      if (object_store() != nullptr) {
  #define INIT_ENTRY_POINT(name)                                                 \
    thread->name##_entry_point_ = Function::EntryPointOf(object_store()->name());
        CACHED_FUNCTION_ENTRY_POINTS_LIST(INIT_ENTRY_POINT)
  #undef INIT_ENTRY_POINT
      }
  #endif  // defined(DART_PRECOMPILED_RUNTIME)
      ASSERT(heap() != nullptr);
      thread->heap_ = heap();
      thread->set_os_thread(os_thread);
      ASSERT(thread->execution_state() == Thread::kThreadInNative);
      thread->set_execution_state(Thread::kThreadInVM);
      thread->set_safepoint_state(
          Thread::SetBypassSafepoints(bypass_safepoint, 0));
      thread->set_vm_tag(VMTag::kVMTagId);
      ASSERT(thread->no_safepoint_scope_depth() == 0);
      os_thread->set_thread(thread);
      Thread::SetCurrent(thread);
      os_thread->EnableThreadInterrupts();
    }
    return thread;
  }
  
  void IsolateGroup::UnscheduleThreadLocked(MonitorLocker* ml,
                                            Thread* thread,
                                            bool is_mutator,
                                            bool bypass_safepoint) {
    thread->heap()->new_space()->AbandonRemainingTLAB(thread);
  
    // Clear since GC will not visit the thread once it is unscheduled. Do this
    // under the thread lock to prevent races with the GC visiting thread roots.
    if (!is_mutator) {
      thread->ClearReusableHandles();
    }
  
    // Disassociate the 'Thread' structure and unschedule the thread
    // from this isolate group.
    if (!is_mutator) {
      ASSERT(thread->api_top_scope_ == nullptr);
      ASSERT(thread->zone() == nullptr);
      ASSERT(thread->sticky_error() == Error::null());
    }
    if (!bypass_safepoint) {
      // Ensure that the thread reports itself as being at a safepoint.
      thread->EnterSafepoint();
    }
    OSThread* os_thread = thread->os_thread();
    ASSERT(os_thread != nullptr);
    os_thread->DisableThreadInterrupts();
    os_thread->set_thread(nullptr);
    OSThread::SetCurrent(os_thread);
  
    // Even if we unschedule the mutator thread, e.g. via calling
    // `Dart_ExitIsolate()` inside a native, we might still have one or more Dart
    // stacks active, which e.g. GC marker threads want to visit.  So we don't
    // clear out the isolate pointer if we are on the mutator thread.
    //
    // The [thread] structure for the mutator thread is kept alive in the thread
    // registry even if the mutator thread is temporarily unscheduled.
    //
    // All other threads are not allowed to unschedule themselves and schedule
    // again later on.
    if (!is_mutator) {
      ASSERT(thread->isolate_ == nullptr);
      thread->isolate_group_ = nullptr;
    }
    thread->heap_ = nullptr;
    thread->set_os_thread(nullptr);
    thread->set_execution_state(Thread::kThreadInNative);
    thread->set_safepoint_state(Thread::AtSafepointField::encode(true) |
                                Thread::AtDeoptSafepointField::encode(true));
    ASSERT(thread->no_safepoint_scope_depth() == 0);
    if (is_mutator) {
      // The mutator thread structure stays alive and attached to the isolate as
      // long as the isolate lives. So we simply remove the thread from the list
      // of scheduled threads.
      thread_registry()->RemoveFromActiveListLocked(thread);
    } else {
      // Return thread structure.
      thread_registry()->ReturnThreadLocked(thread);
    }
  }
  
  Thread* IsolateGroup::ScheduleThread(bool bypass_safepoint) {
    // We are about to associate the thread with an isolate group and it would
    // not be possible to correctly track no_safepoint_scope_depth for the
    // thread in the constructor/destructor of MonitorLocker,
    // so we create a MonitorLocker object which does not do any
    // no_safepoint_scope_depth increments/decrements.
    MonitorLocker ml(threads_lock(), false);
  
    const bool is_vm_isolate = false;
  
    // Schedule the thread into the isolate by associating
    // a 'Thread' structure with it (this is done while we are holding
    // the thread registry lock).
    return ScheduleThreadLocked(&ml, /*existing_mutator_thread=*/nullptr,
                                is_vm_isolate, /*is_mutator=*/false,
                                bypass_safepoint);
  }
  
  void IsolateGroup::UnscheduleThread(Thread* thread,
                                      bool is_mutator,
                                      bool bypass_safepoint) {
    // Disassociate the 'Thread' structure and unschedule the thread
    // from this isolate group.
    //
    // We are disassociating the thread from an isolate and it would
    // not be possible to correctly track no_safepoint_scope_depth for the
    // thread in the constructor/destructor of MonitorLocker,
    // so we create a MonitorLocker object which does not do any
    // no_safepoint_scope_depth increments/decrements.
    MonitorLocker ml(threads_lock(), false);
    UnscheduleThreadLocked(&ml, thread, is_mutator, bypass_safepoint);
  }
  
  void IsolateGroup::IncreaseMutatorCount(Isolate* mutator,
                                          bool is_nested_reenter) {
    ASSERT(mutator->group() == this);
  
    // If the mutator was temporarily blocked on a worker thread, we have to
    // unblock the worker thread again.
    if (is_nested_reenter) {
      ASSERT(mutator->mutator_thread() != nullptr);
      thread_pool()->MarkCurrentWorkerAsUnBlocked();
    }
  
    // Prevent too many mutators from entering the isolate group to avoid
    // pathological behavior where many threads are fighting for obtaining TLABs.
    {
      // NOTE: This is performance critical code, we should avoid monitors and use
      // std::atomics in the fast case (where active_mutators <
      // max_active_mutators) and only use montiors in the uncommon case.
      MonitorLocker ml(active_mutators_monitor_.get());
      ASSERT(active_mutators_ <= max_active_mutators_);
      while (active_mutators_ == max_active_mutators_) {
        waiting_mutators_++;
        ml.Wait();
        waiting_mutators_--;
      }
      active_mutators_++;
    }
  }
  
  void IsolateGroup::DecreaseMutatorCount(Isolate* mutator, bool is_nested_exit) {
    ASSERT(mutator->group() == this);
  
    // If the mutator thread has an active stack and runs on our thread pool we
    // will mark the worker as blocked, thereby possibly spawning a new worker for
    // pending tasks (if there are any).
    ASSERT(mutator->mutator_thread() != nullptr);
    if (is_nested_exit) {
      thread_pool()->MarkCurrentWorkerAsBlocked();
    }
  
    {
      // NOTE: This is performance critical code, we should avoid monitors and use
      // std::atomics in the fast case (where active_mutators <
      // max_active_mutators) and only use montiors in the uncommon case.
      MonitorLocker ml(active_mutators_monitor_.get());
      ASSERT(active_mutators_ <= max_active_mutators_);
      active_mutators_--;
      if (waiting_mutators_ > 0) {
        ml.Notify();
      }
    }
  }
  
  #ifndef PRODUCT
  void IsolateGroup::PrintJSON(JSONStream* stream, bool ref) {
    JSONObject jsobj(stream);
    PrintToJSONObject(&jsobj, ref);
  }
  
  void IsolateGroup::PrintToJSONObject(JSONObject* jsobj, bool ref) {
    jsobj->AddProperty("type", (ref ? "@IsolateGroup" : "IsolateGroup"));
    jsobj->AddServiceId(ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, id());
  
    jsobj->AddProperty("name", source()->script_uri);
    jsobj->AddPropertyF("number", "%" Pu64 "", id());
    jsobj->AddProperty("isSystemIsolateGroup", is_system_isolate_group());
    if (ref) {
      return;
    }
  
    {
      JSONArray isolate_array(jsobj, "isolates");
      for (auto it = isolates_.Begin(); it != isolates_.End(); ++it) {
        Isolate* isolate = *it;
        isolate_array.AddValue(isolate, /*ref=*/true);
      }
    }
  }
  
  void IsolateGroup::PrintMemoryUsageJSON(JSONStream* stream) {
    int64_t used = heap()->TotalUsedInWords();
    int64_t capacity = heap()->TotalCapacityInWords();
    int64_t external_used = heap()->TotalExternalInWords();
  
    JSONObject jsobj(stream);
    // This is the same "MemoryUsage" that the isolate-specific "getMemoryUsage"
    // rpc method returns.
    jsobj.AddProperty("type", "MemoryUsage");
    jsobj.AddProperty64("heapUsage", used * kWordSize);
    jsobj.AddProperty64("heapCapacity", capacity * kWordSize);
    jsobj.AddProperty64("externalUsage", external_used * kWordSize);
  }
  #endif
  
  void IsolateGroup::ForEach(std::function<void(IsolateGroup*)> action) {
    ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_);
    for (auto isolate_group : *isolate_groups_) {
      action(isolate_group);
    }
  }
  
  void IsolateGroup::RunWithIsolateGroup(
      uint64_t id,
      std::function<void(IsolateGroup*)> action,
      std::function<void()> not_found) {
    ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_);
    for (auto isolate_group : *isolate_groups_) {
      if (isolate_group->id() == id) {
        action(isolate_group);
        return;
      }
    }
    not_found();
  }
  
  void IsolateGroup::RegisterIsolateGroup(IsolateGroup* isolate_group) {
    WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
    isolate_groups_->Append(isolate_group);
  }
  
  void IsolateGroup::UnregisterIsolateGroup(IsolateGroup* isolate_group) {
    WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
    isolate_groups_->Remove(isolate_group);
  }
  
  bool IsolateGroup::HasApplicationIsolateGroups() {
    ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
    for (auto group : *isolate_groups_) {
      if (!IsolateGroup::IsSystemIsolateGroup(group)) {
        return true;
      }
    }
    return false;
  }
  
  bool IsolateGroup::HasOnlyVMIsolateGroup() {
    ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
    for (auto group : *isolate_groups_) {
      if (!Dart::VmIsolateNameEquals(group->source()->name)) {
        return false;
      }
    }
    return true;
  }
  
  void IsolateGroup::Init() {
    ASSERT(isolate_groups_rwlock_ == nullptr);
    isolate_groups_rwlock_ = new RwLock();
    ASSERT(isolate_groups_ == nullptr);
    isolate_groups_ = new IntrusiveDList<IsolateGroup>();
    isolate_group_random_ = new Random();
  }
  
  void IsolateGroup::Cleanup() {
    delete isolate_group_random_;
    isolate_group_random_ = nullptr;
    delete isolate_groups_rwlock_;
    isolate_groups_rwlock_ = nullptr;
    ASSERT(isolate_groups_->IsEmpty());
    delete isolate_groups_;
    isolate_groups_ = nullptr;
  }
  
  bool IsolateVisitor::IsSystemIsolate(Isolate* isolate) const {
    return Isolate::IsSystemIsolate(isolate);
  }
  
  Bequest::~Bequest() {
    if (handle_ == nullptr) {
      return;
    }
  
    IsolateGroup* isolate_group = IsolateGroup::Current();
    CHECK_ISOLATE_GROUP(isolate_group);
    NoSafepointScope no_safepoint_scope;
    ApiState* state = isolate_group->api_state();
    ASSERT(state != nullptr);
    state->FreePersistentHandle(handle_);
  }
  
  void IsolateGroup::RegisterClass(const Class& cls) {
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    if (IsReloading()) {
      program_reload_context()->RegisterClass(cls);
      return;
    }
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    if (cls.IsTopLevel()) {
      class_table()->RegisterTopLevel(cls);
    } else {
      class_table()->Register(cls);
    }
  }
  
  #if defined(DEBUG)
  void IsolateGroup::ValidateClassTable() {
    class_table()->Validate();
  }
  #endif  // DEBUG
  
  void IsolateGroup::RegisterStaticField(const Field& field,
                                         const Object& initial_value) {
    ASSERT(program_lock()->IsCurrentThreadWriter());
  
    ASSERT(field.is_static());
    const bool need_to_grow_backing_store =
        initial_field_table()->Register(field);
    const intptr_t field_id = field.field_id();
    initial_field_table()->SetAt(field_id, initial_value.ptr());
  
    SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get());
    if (need_to_grow_backing_store) {
      // We have to stop other isolates from accessing their field state, since
      // we'll have to grow the backing store.
      GcSafepointOperationScope scope(Thread::Current());
      for (auto isolate : isolates_) {
        auto field_table = isolate->field_table();
        if (field_table->IsReadyToUse()) {
          field_table->Register(field, field_id);
          field_table->SetAt(field_id, initial_value.ptr());
        }
      }
    } else {
      for (auto isolate : isolates_) {
        auto field_table = isolate->field_table();
        if (field_table->IsReadyToUse()) {
          field_table->Register(field, field_id);
          field_table->SetAt(field_id, initial_value.ptr());
        }
      }
    }
  }
  
  void IsolateGroup::FreeStaticField(const Field& field) {
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    // This can only be called during hot-reload.
    ASSERT(program_reload_context() != nullptr);
  #endif
  
    const intptr_t field_id = field.field_id();
    initial_field_table()->Free(field_id);
    ForEachIsolate([&](Isolate* isolate) {
      auto field_table = isolate->field_table();
      // The isolate might've just been created and is now participating in
      // the reload request inside `IsolateGroup::RegisterIsolate()`.
      // At that point it doesn't have the field table setup yet.
      if (field_table->IsReadyToUse()) {
        field_table->Free(field_id);
      }
    });
  }
  
  void IsolateGroup::RehashConstants() {
    Thread* thread = Thread::Current();
    StackZone stack_zone(thread);
    Zone* zone = stack_zone.GetZone();
  
    thread->heap()->ResetCanonicalHashTable();
  
    Class& cls = Class::Handle(zone);
    intptr_t top = class_table()->NumCids();
    for (intptr_t cid = kInstanceCid; cid < top; cid++) {
      if (!class_table()->IsValidIndex(cid) ||
          !class_table()->HasValidClassAt(cid)) {
        continue;
      }
      if ((cid == kTypeArgumentsCid) || IsStringClassId(cid)) {
        // TypeArguments and Symbols have special tables for canonical objects
        // that aren't based on address.
        continue;
      }
      cls = class_table()->At(cid);
      cls.RehashConstants(zone);
    }
  }
  
  #if defined(DEBUG)
  void IsolateGroup::ValidateConstants() {
    if (FLAG_precompiled_mode) {
      // TODO(27003)
      return;
    }
    // Issue(https://dartbug.com/44862): Figure out why hot-reload causes
    // existence of non-canonical constants.
    if (HasAttemptedReload()) {
      return;
    }
  
    // Verify that all canonical instances are correctly setup in the
    // corresponding canonical tables.
    NoBackgroundCompilerScope no_bg_compiler(Thread::Current());
    heap()->CollectAllGarbage(GCReason::kDebugging);
    Thread* thread = Thread::Current();
    SafepointMutexLocker ml(
        thread->isolate_group()->constant_canonicalization_mutex());
    HeapIterationScope iteration(thread);
    VerifyCanonicalVisitor check_canonical(thread);
    iteration.IterateObjects(&check_canonical);
  }
  #endif  // DEBUG
  
  void Isolate::SendInternalLibMessage(LibMsgId msg_id, uint64_t capability) {
    const Array& msg = Array::Handle(Array::New(3));
    Object& element = Object::Handle();
  
    element = Smi::New(Message::kIsolateLibOOBMsg);
    msg.SetAt(0, element);
    element = Smi::New(msg_id);
    msg.SetAt(1, element);
    element = Capability::New(capability);
    msg.SetAt(2, element);
  
    PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
                                      /* same_group */ false, msg, main_port(),
                                      Message::kOOBPriority));
  }
  
  void IsolateGroup::set_object_store(ObjectStore* object_store) {
    object_store_.reset(object_store);
  }
  
  class IsolateMessageHandler : public MessageHandler {
   public:
    explicit IsolateMessageHandler(Isolate* isolate);
    ~IsolateMessageHandler();
  
    const char* name() const;
    void MessageNotify(Message::Priority priority);
    MessageStatus HandleMessage(std::unique_ptr<Message> message);
  #ifndef PRODUCT
    void NotifyPauseOnStart();
    void NotifyPauseOnExit();
  #endif  // !PRODUCT
  
  #if defined(DEBUG)
    // Check that it is safe to access this handler.
    void CheckAccess();
  #endif
    bool IsCurrentIsolate() const;
    virtual Isolate* isolate() const { return isolate_; }
    virtual IsolateGroup* isolate_group() const { return isolate_->group(); }
  
   private:
    // A result of false indicates that the isolate should terminate the
    // processing of further events.
    ErrorPtr HandleLibMessage(const Array& message);
  
    MessageStatus ProcessUnhandledException(const Error& result);
    Isolate* isolate_;
  };
  
  IsolateMessageHandler::IsolateMessageHandler(Isolate* isolate)
      : isolate_(isolate) {}
  
  IsolateMessageHandler::~IsolateMessageHandler() {}
  
  const char* IsolateMessageHandler::name() const {
    return isolate_->name();
  }
  
  // Isolate library OOB messages are fixed sized arrays which have the
  // following format:
  // [ OOB dispatch, Isolate library dispatch, <message specific data> ]
  ErrorPtr IsolateMessageHandler::HandleLibMessage(const Array& message) {
    if (message.Length() < 2) return Error::null();
    Zone* zone = T->zone();
    const Object& type = Object::Handle(zone, message.At(1));
    if (!type.IsSmi()) return Error::null();
    const intptr_t msg_type = Smi::Cast(type).Value();
    switch (msg_type) {
      case Isolate::kPauseMsg: {
        // [ OOB, kPauseMsg, pause capability, resume capability ]
        if (message.Length() != 4) return Error::null();
        Object& obj = Object::Handle(zone, message.At(2));
        if (!I->VerifyPauseCapability(obj)) return Error::null();
        obj = message.At(3);
        if (!obj.IsCapability()) return Error::null();
        if (I->AddResumeCapability(Capability::Cast(obj))) {
          increment_paused();
        }
        break;
      }
      case Isolate::kResumeMsg: {
        // [ OOB, kResumeMsg, pause capability, resume capability ]
        if (message.Length() != 4) return Error::null();
        Object& obj = Object::Handle(zone, message.At(2));
        if (!I->VerifyPauseCapability(obj)) return Error::null();
        obj = message.At(3);
        if (!obj.IsCapability()) return Error::null();
        if (I->RemoveResumeCapability(Capability::Cast(obj))) {
          decrement_paused();
        }
        break;
      }
      case Isolate::kPingMsg: {
        // [ OOB, kPingMsg, responsePort, priority, response ]
        if (message.Length() != 5) return Error::null();
        const Object& obj2 = Object::Handle(zone, message.At(2));
        if (!obj2.IsSendPort()) return Error::null();
        const SendPort& send_port = SendPort::Cast(obj2);
        const Object& obj3 = Object::Handle(zone, message.At(3));
        if (!obj3.IsSmi()) return Error::null();
        const intptr_t priority = Smi::Cast(obj3).Value();
        const Object& obj4 = Object::Handle(zone, message.At(4));
        if (!obj4.IsInstance() && !obj4.IsNull()) return Error::null();
        const Instance& response =
            obj4.IsNull() ? Instance::null_instance() : Instance::Cast(obj4);
        if (priority == Isolate::kImmediateAction) {
          PortMap::PostMessage(SerializeMessage(send_port.Id(), response));
        } else {
          ASSERT((priority == Isolate::kBeforeNextEventAction) ||
                 (priority == Isolate::kAsEventAction));
          // Update the message so that it will be handled immediately when it
          // is picked up from the message queue the next time.
          message.SetAt(
              0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg)));
          message.SetAt(3,
                        Smi::Handle(zone, Smi::New(Isolate::kImmediateAction)));
          this->PostMessage(
              SerializeMessage(Message::kIllegalPort, message),
              priority == Isolate::kBeforeNextEventAction /* at_head */);
        }
        break;
      }
      case Isolate::kKillMsg:
      case Isolate::kInternalKillMsg: {
        // [ OOB, kKillMsg, terminate capability, priority ]
        if (message.Length() != 4) return Error::null();
        Object& obj = Object::Handle(zone, message.At(3));
        if (!obj.IsSmi()) return Error::null();
        const intptr_t priority = Smi::Cast(obj).Value();
        if (priority == Isolate::kImmediateAction) {
          Thread::Current()->StartUnwindError();
          obj = message.At(2);
          if (I->VerifyTerminateCapability(obj)) {
            // We will kill the current isolate by returning an UnwindError.
            if (msg_type == Isolate::kKillMsg) {
              const String& msg = String::Handle(
                  String::New("isolate terminated by Isolate.kill"));
              const UnwindError& error =
                  UnwindError::Handle(UnwindError::New(msg));
              error.set_is_user_initiated(true);
              return error.ptr();
            } else if (msg_type == Isolate::kInternalKillMsg) {
              const String& msg =
                  String::Handle(String::New("isolate terminated by vm"));
              return UnwindError::New(msg);
            } else {
              UNREACHABLE();
            }
          } else {
            return Error::null();
          }
        } else {
          ASSERT((priority == Isolate::kBeforeNextEventAction) ||
                 (priority == Isolate::kAsEventAction));
          // Update the message so that it will be handled immediately when it
          // is picked up from the message queue the next time.
          message.SetAt(
              0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg)));
          message.SetAt(3,
                        Smi::Handle(zone, Smi::New(Isolate::kImmediateAction)));
          this->PostMessage(
              SerializeMessage(Message::kIllegalPort, message),
              priority == Isolate::kBeforeNextEventAction /* at_head */);
        }
        break;
      }
      case Isolate::kInterruptMsg: {
        // [ OOB, kInterruptMsg, pause capability ]
        if (message.Length() != 3) return Error::null();
        Object& obj = Object::Handle(zone, message.At(2));
        if (!I->VerifyPauseCapability(obj)) return Error::null();
  
  #if !defined(PRODUCT)
        // If we are already paused, don't pause again.
        if (I->debugger()->PauseEvent() == NULL) {
          return I->debugger()->PauseInterrupted();
        }
  #endif
        break;
      }
      case Isolate::kDrainServiceExtensionsMsg: {
  #ifndef PRODUCT
        Object& obj = Object::Handle(zone, message.At(2));
        if (!obj.IsSmi()) return Error::null();
        const intptr_t priority = Smi::Cast(obj).Value();
        if (priority == Isolate::kImmediateAction) {
          return I->InvokePendingServiceExtensionCalls();
        } else {
          ASSERT((priority == Isolate::kBeforeNextEventAction) ||
                 (priority == Isolate::kAsEventAction));
          // Update the message so that it will be handled immediately when it
          // is picked up from the message queue the next time.
          message.SetAt(
              0, Smi::Handle(zone, Smi::New(Message::kDelayedIsolateLibOOBMsg)));
          message.SetAt(2,
                        Smi::Handle(zone, Smi::New(Isolate::kImmediateAction)));
          this->PostMessage(
              SerializeMessage(Message::kIllegalPort, message),
              priority == Isolate::kBeforeNextEventAction /* at_head */);
        }
  #else
        UNREACHABLE();
  #endif  // !PRODUCT
        break;
      }
  
      case Isolate::kAddExitMsg:
      case Isolate::kDelExitMsg:
      case Isolate::kAddErrorMsg:
      case Isolate::kDelErrorMsg: {
        // [ OOB, msg, listener port ]
        if (message.Length() < 3) return Error::null();
        const Object& obj = Object::Handle(zone, message.At(2));
        if (!obj.IsSendPort()) return Error::null();
        const SendPort& listener = SendPort::Cast(obj);
        switch (msg_type) {
          case Isolate::kAddExitMsg: {
            if (message.Length() != 4) return Error::null();
            // [ OOB, msg, listener port, response object ]
            const Object& response = Object::Handle(zone, message.At(3));
            if (!response.IsInstance() && !response.IsNull()) {
              return Error::null();
            }
            I->AddExitListener(listener, response.IsNull()
                                             ? Instance::null_instance()
                                             : Instance::Cast(response));
            break;
          }
          case Isolate::kDelExitMsg:
            if (message.Length() != 3) return Error::null();
            I->RemoveExitListener(listener);
            break;
          case Isolate::kAddErrorMsg:
            if (message.Length() != 3) return Error::null();
            I->AddErrorListener(listener);
            break;
          case Isolate::kDelErrorMsg:
            if (message.Length() != 3) return Error::null();
            I->RemoveErrorListener(listener);
            break;
          default:
            UNREACHABLE();
        }
        break;
      }
      case Isolate::kErrorFatalMsg: {
        // [ OOB, kErrorFatalMsg, terminate capability, val ]
        if (message.Length() != 4) return Error::null();
        // Check that the terminate capability has been passed correctly.
        Object& obj = Object::Handle(zone, message.At(2));
        if (!I->VerifyTerminateCapability(obj)) return Error::null();
        // Get the value to be set.
        obj = message.At(3);
        if (!obj.IsBool()) return Error::null();
        I->SetErrorsFatal(Bool::Cast(obj).value());
        break;
      }
      case Isolate::kCheckForReload: {
        // [ OOB, kCheckForReload, ignored ]
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
        IG->reload_handler()->CheckForReload();
  #else
        UNREACHABLE();
  #endif
        break;
      }
  #if defined(DEBUG)
      // Malformed OOB messages are silently ignored in release builds.
      default:
        FATAL1("Unknown OOB message type: %" Pd "\n", msg_type);
        break;
  #endif  // defined(DEBUG)
    }
    return Error::null();
  }
  
  void IsolateMessageHandler::MessageNotify(Message::Priority priority) {
    if (priority >= Message::kOOBPriority) {
      // Handle out of band messages even if the mutator thread is busy.
      I->ScheduleInterrupts(Thread::kMessageInterrupt);
    }
    Dart_MessageNotifyCallback callback = I->message_notify_callback();
    if (callback != nullptr) {
      // Allow the embedder to handle message notification.
      (*callback)(Api::CastIsolate(I));
    }
  }
  
  bool Isolate::HasPendingMessages() {
    return message_handler_->HasMessages() || message_handler_->HasOOBMessages();
  }
  
  MessageHandler::MessageStatus IsolateMessageHandler::HandleMessage(
      std::unique_ptr<Message> message) {
    ASSERT(IsCurrentIsolate());
    Thread* thread = Thread::Current();
    StackZone stack_zone(thread);
    Zone* zone = stack_zone.GetZone();
    HandleScope handle_scope(thread);
  #if defined(SUPPORT_TIMELINE)
    TimelineBeginEndScope tbes(
        thread, Timeline::GetIsolateStream(),
        message->IsOOB() ? "HandleOOBMessage" : "HandleMessage");
    tbes.SetNumArguments(1);
    tbes.CopyArgument(0, "isolateName", I->name());
  #endif
  
    // Parse the message.
    Object& msg_obj = Object::Handle(zone, ReadMessage(thread, message.get()));
    if (msg_obj.IsError()) {
      // An error occurred while reading the message.
      return ProcessUnhandledException(Error::Cast(msg_obj));
    }
    if (!msg_obj.IsNull() && !msg_obj.IsInstance()) {
      // TODO(turnidge): We need to decide what an isolate does with
      // malformed messages.  If they (eventually) come from a remote
      // machine, then it might make sense to drop the message entirely.
      // In the case that the message originated locally, which is
      // always true for now, then this should never occur.
      UNREACHABLE();
    }
    Instance& msg = Instance::Handle(zone);
    msg ^= msg_obj.ptr();  // Can't use Instance::Cast because may be null.
  
    MessageStatus status = kOK;
    if (message->IsOOB()) {
      // OOB messages are expected to be fixed length arrays where the first
      // element is a Smi describing the OOB destination. Messages that do not
      // confirm to this layout are silently ignored.
      if (msg.IsArray()) {
        const Array& oob_msg = Array::Cast(msg);
        if (oob_msg.Length() > 0) {
          const Object& oob_tag = Object::Handle(zone, oob_msg.At(0));
          if (oob_tag.IsSmi()) {
            switch (Smi::Cast(oob_tag).Value()) {
              case Message::kServiceOOBMsg: {
  #ifndef PRODUCT
                const Error& error =
                    Error::Handle(Service::HandleIsolateMessage(I, oob_msg));
                if (!error.IsNull()) {
                  status = ProcessUnhandledException(error);
                }
  #else
                UNREACHABLE();
  #endif
                break;
              }
              case Message::kIsolateLibOOBMsg: {
                const Error& error = Error::Handle(HandleLibMessage(oob_msg));
                if (!error.IsNull()) {
                  status = ProcessUnhandledException(error);
                }
                break;
              }
  #if defined(DEBUG)
              // Malformed OOB messages are silently ignored in release builds.
              default: {
                UNREACHABLE();
                break;
              }
  #endif  // defined(DEBUG)
            }
          }
        }
      }
    } else if (message->IsFinalizerInvocationRequest()) {
      const Object& msg_handler = Object::Handle(
          zone,
          DartLibraryCalls::HandleFinalizerMessage(FinalizerBase::Cast(msg)));
      if (msg_handler.IsError()) {
        status = ProcessUnhandledException(Error::Cast(msg_handler));
      } else {
        // The handler closure which was used to successfully handle the message.
      }
    } else if (message->dest_port() == Message::kIllegalPort) {
      // Check whether this is a delayed OOB message which needed handling as
      // part of the regular message dispatch. All other messages are dropped on
      // the floor.
      if (msg.IsArray()) {
        const Array& msg_arr = Array::Cast(msg);
        if (msg_arr.Length() > 0) {
          const Object& oob_tag = Object::Handle(zone, msg_arr.At(0));
          if (oob_tag.IsSmi() &&
              (Smi::Cast(oob_tag).Value() == Message::kDelayedIsolateLibOOBMsg)) {
            const Error& error = Error::Handle(HandleLibMessage(msg_arr));
            if (!error.IsNull()) {
              status = ProcessUnhandledException(error);
            }
          }
        }
      }
    } else {
      const Object& msg_handler = Object::Handle(
          zone, DartLibraryCalls::HandleMessage(message->dest_port(), msg));
      if (msg_handler.IsError()) {
        status = ProcessUnhandledException(Error::Cast(msg_handler));
      } else if (msg_handler.IsNull()) {
        // If the port has been closed then the message will be dropped at this
        // point. Make sure to post to the delivery failure port in that case.
      } else {
        // The handler closure which was used to successfully handle the message.
      }
    }
    return status;
  }
  
  #ifndef PRODUCT
  void IsolateMessageHandler::NotifyPauseOnStart() {
    if (Isolate::IsSystemIsolate(I)) {
      return;
    }
    if (Service::debug_stream.enabled() || FLAG_warn_on_pause_with_no_debugger) {
      StartIsolateScope start_isolate(I);
      StackZone zone(T);
      HandleScope handle_scope(T);
      ServiceEvent pause_event(I, ServiceEvent::kPauseStart);
      Service::HandleEvent(&pause_event);
    } else if (FLAG_trace_service) {
      OS::PrintErr("vm-service: Dropping event of type PauseStart (%s)\n",
                   I->name());
    }
  }
  
  void IsolateMessageHandler::NotifyPauseOnExit() {
    if (Isolate::IsSystemIsolate(I)) {
      return;
    }
    if (Service::debug_stream.enabled() || FLAG_warn_on_pause_with_no_debugger) {
      StartIsolateScope start_isolate(I);
      StackZone zone(T);
      HandleScope handle_scope(T);
      ServiceEvent pause_event(I, ServiceEvent::kPauseExit);
      Service::HandleEvent(&pause_event);
    } else if (FLAG_trace_service) {
      OS::PrintErr("vm-service: Dropping event of type PauseExit (%s)\n",
                   I->name());
    }
  }
  #endif  // !PRODUCT
  
  #if defined(DEBUG)
  void IsolateMessageHandler::CheckAccess() {
    ASSERT(IsCurrentIsolate());
  }
  #endif
  
  bool IsolateMessageHandler::IsCurrentIsolate() const {
    return (I == Isolate::Current());
  }
  
  static MessageHandler::MessageStatus StoreError(Thread* thread,
                                                  const Error& error) {
    thread->set_sticky_error(error);
    if (error.IsUnwindError()) {
      const UnwindError& unwind = UnwindError::Cast(error);
      if (!unwind.is_user_initiated()) {
        return MessageHandler::kShutdown;
      }
    }
    return MessageHandler::kError;
  }
  
  MessageHandler::MessageStatus IsolateMessageHandler::ProcessUnhandledException(
      const Error& result) {
    if (FLAG_trace_isolates) {
      OS::PrintErr(
          "[!] Unhandled exception in %s:\n"
          "         exception: %s\n",
          T->isolate()->name(), result.ToErrorCString());
    }
  
    NoReloadScope no_reload(T);
    // Generate the error and stacktrace strings for the error message.
    const char* exception_cstr = nullptr;
    const char* stacktrace_cstr = nullptr;
    if (result.IsUnhandledException()) {
      Zone* zone = T->zone();
      const UnhandledException& uhe = UnhandledException::Cast(result);
      const Instance& exception = Instance::Handle(zone, uhe.exception());
      if (exception.ptr() == IG->object_store()->out_of_memory()) {
        exception_cstr = "Out of Memory";  // Cf. OutOfMemoryError.toString().
      } else if (exception.ptr() == IG->object_store()->stack_overflow()) {
        exception_cstr = "Stack Overflow";  // Cf. StackOverflowError.toString().
      } else {
        const Object& exception_str =
            Object::Handle(zone, DartLibraryCalls::ToString(exception));
        if (!exception_str.IsString()) {
          exception_cstr = exception.ToCString();
        } else {
          exception_cstr = exception_str.ToCString();
        }
      }
  
      const Instance& stacktrace = Instance::Handle(zone, uhe.stacktrace());
      stacktrace_cstr = stacktrace.ToCString();
    } else {
      exception_cstr = result.ToErrorCString();
    }
    if (result.IsUnwindError()) {
      // When unwinding we don't notify error listeners and we ignore
      // whether errors are fatal for the current isolate.
      return StoreError(T, result);
    } else {
      bool has_listener =
          I->NotifyErrorListeners(exception_cstr, stacktrace_cstr);
      if (I->ErrorsFatal()) {
        if (has_listener) {
          T->ClearStickyError();
        } else {
          T->set_sticky_error(result);
        }
  #if !defined(PRODUCT)
        // Notify the debugger about specific unhandled exceptions which are
        // withheld when being thrown. Do this after setting the sticky error
        // so the isolate has an error set when paused with the unhandled
        // exception.
        if (result.IsUnhandledException()) {
          const UnhandledException& error = UnhandledException::Cast(result);
          InstancePtr exception = error.exception();
          if ((exception == IG->object_store()->out_of_memory()) ||
              (exception == IG->object_store()->stack_overflow())) {
            // We didn't notify the debugger when the stack was full. Do it now.
            I->debugger()->PauseException(Instance::Handle(exception));
          }
        }
  #endif  // !defined(PRODUCT)
        return kError;
      }
    }
    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) {
    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->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->copy_parent_code = false;
  }
  
  void Isolate::FlagsCopyFrom(const Dart_IsolateFlags& api_flags) {
    const bool copy_parent_code_ = copy_parent_code();
  #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_flags_ = bitname##Bit::update(                       \
                        api_flags.isolate_flag, isolate_flags_));
  
    BOOL_ISOLATE_FLAG_LIST(SET_FROM_FLAG)
    isolate_flags_ = CopyParentCodeBit::update(copy_parent_code_, isolate_flags_);
  #undef FLAG_FOR_NONPRODUCT
  #undef FLAG_FOR_PRECOMPILER
  #undef FLAG_FOR_PRODUCT
  #undef SET_FROM_FLAG
  }
  
  #if defined(DEBUG)
  // static
  void BaseIsolate::AssertCurrent(BaseIsolate* isolate) {
    ASSERT(isolate == Isolate::Current());
  }
  #endif  // defined(DEBUG)
  
  #if defined(DEBUG)
  #define REUSABLE_HANDLE_SCOPE_INIT(object)                                     \
    reusable_##object##_handle_scope_active_(false),
  #else
  #define REUSABLE_HANDLE_SCOPE_INIT(object)
  #endif  // defined(DEBUG)
  
  #define REUSABLE_HANDLE_INITIALIZERS(object) object##_handle_(nullptr),
  
  class LibraryPrefixMapTraits {
   public:
    static bool ReportStats() { return false; }
    static const char* Name() { return "LibraryPrefixMapTraits"; }
  
    static bool IsMatch(const Object& a, const Object& b) {
      if (!a.IsLibraryPrefix() || !b.IsLibraryPrefix()) {
        return false;
      }
      return a.ptr() == b.ptr();
    }
  
    static uword Hash(const Object& obj) {
      auto& prefix = LibraryPrefix::Cast(obj);
      return String::Hash(prefix.name());
    }
  };
  
  // TODO(srdjan): Some Isolate monitors can be shared. Replace their usage with
  // that shared monitor.
  Isolate::Isolate(IsolateGroup* isolate_group,
                   const Dart_IsolateFlags& api_flags)
      : BaseIsolate(),
        current_tag_(UserTag::null()),
        default_tag_(UserTag::null()),
        ic_miss_code_(Code::null()),
        field_table_(new FieldTable(/*isolate=*/this)),
        finalizers_(GrowableObjectArray::null()),
        isolate_group_(isolate_group),
        isolate_object_store_(new IsolateObjectStore()),
  #if !defined(DART_PRECOMPILED_RUNTIME)
        native_callback_trampolines_(),
  #endif
        isolate_flags_(0),
  #if !defined(PRODUCT)
        last_resume_timestamp_(OS::GetCurrentTimeMillis()),
        vm_tag_counters_(),
        pending_service_extension_calls_(GrowableObjectArray::null()),
        registered_service_extension_handlers_(GrowableObjectArray::null()),
  #define ISOLATE_METRIC_CONSTRUCTORS(type, variable, name, unit)                \
    metric_##variable##_(),
        ISOLATE_METRIC_LIST(ISOLATE_METRIC_CONSTRUCTORS)
  #undef ISOLATE_METRIC_CONSTRUCTORS
  #endif  // !defined(PRODUCT)
            start_time_micros_(OS::GetCurrentMonotonicMicros()),
        message_notify_callback_(nullptr),
        on_shutdown_callback_(Isolate::ShutdownCallback()),
        on_cleanup_callback_(Isolate::CleanupCallback()),
        random_(),
        mutex_(NOT_IN_PRODUCT("Isolate::mutex_")),
        tag_table_(GrowableObjectArray::null()),
        sticky_error_(Error::null()),
        spawn_count_monitor_(),
        handler_info_cache_(),
        catch_entry_moves_cache_(),
        loaded_prefixes_set_storage_(nullptr) {
    FlagsCopyFrom(api_flags);
    SetErrorsFatal(true);
    // TODO(asiva): A Thread is not available here, need to figure out
    // how the vm_tag (kEmbedderTagId) can be set, these tags need to
    // move to the OSThread structure.
    set_user_tag(UserTags::kDefaultUserTag);
  
    if (group()->obfuscate()) {
      OS::PrintErr(
          "Warning: This VM has been configured to obfuscate symbol information "
          "which violates the Dart standard.\n"
          "         See dartbug.com/30524 for more information.\n");
    }
  }
  
  #undef REUSABLE_HANDLE_SCOPE_INIT
  #undef REUSABLE_HANDLE_INITIALIZERS
  
  Isolate::~Isolate() {
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    // TODO(32796): Re-enable assertion.
    // RELEASE_ASSERT(program_reload_context_ == NULL);
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
  #if !defined(PRODUCT)
    delete debugger_;
    debugger_ = nullptr;
    delete object_id_ring_;
    object_id_ring_ = nullptr;
    delete pause_loop_monitor_;
    pause_loop_monitor_ = nullptr;
  #endif  // !defined(PRODUCT)
  
    free(name_);
    delete field_table_;
  #if defined(USING_SIMULATOR)
    delete simulator_;
  #endif
    delete message_handler_;
    message_handler_ =
        nullptr;  // Fail fast if we send messages to a dead isolate.
    ASSERT(deopt_context_ ==
           nullptr);  // No deopt in progress when isolate deleted.
    ASSERT(spawn_count_ == 0);
  
    // We have cached the mutator thread, delete it.
    ASSERT(scheduled_mutator_thread_ == nullptr);
    mutator_thread_->isolate_ = nullptr;
    delete mutator_thread_;
    mutator_thread_ = nullptr;
  }
  
  void Isolate::InitVM() {
    create_group_callback_ = nullptr;
    initialize_callback_ = nullptr;
    shutdown_callback_ = nullptr;
    cleanup_callback_ = nullptr;
    cleanup_group_callback_ = nullptr;
    register_kernel_blob_callback_ = nullptr;
    unregister_kernel_blob_callback_ = nullptr;
    if (isolate_creation_monitor_ == nullptr) {
      isolate_creation_monitor_ = new Monitor();
    }
    ASSERT(isolate_creation_monitor_ != nullptr);
    EnableIsolateCreation();
  }
  
  Isolate* Isolate::InitIsolate(const char* name_prefix,
                                IsolateGroup* isolate_group,
                                const Dart_IsolateFlags& api_flags,
                                bool is_vm_isolate) {
    Isolate* result = new Isolate(isolate_group, api_flags);
    result->BuildName(name_prefix);
    if (!is_vm_isolate) {
      // vm isolate object store is initialized later, after null instance
      // is created (in Dart::Init).
      // Non-vm isolates need to have isolate object store initialized is that
      // exit_listeners have to be null-initialized as they will be used if
      // we fail to create isolate below, have to do low level shutdown.
      ASSERT(result->group()->object_store() != nullptr);
      result->isolate_object_store()->Init();
    }
  
    ASSERT(result != nullptr);
  
  #if !defined(PRODUCT)
  // Initialize metrics.
  #define ISOLATE_METRIC_INIT(type, variable, name, unit)                        \
    result->metric_##variable##_.InitInstance(result, name, NULL, Metric::unit);
    ISOLATE_METRIC_LIST(ISOLATE_METRIC_INIT);
  #undef ISOLATE_METRIC_INIT
  #endif  // !defined(PRODUCT)
  
    // First we ensure we enter the isolate. This will ensure we're participating
    // in any safepointing requests from this point on. Other threads requesting a
    // safepoint operation will therefore wait until we've stopped.
    //
    // Though the [result] isolate is still in a state where no memory has been
    // allocated, which means it's safe to GC the isolate group until here.
    if (!Thread::EnterIsolate(result)) {
      delete result;
      return nullptr;
    }
  
    // Setup the isolate message handler.
    MessageHandler* handler = new IsolateMessageHandler(result);
    ASSERT(handler != nullptr);
    result->set_message_handler(handler);
  
    result->set_main_port(PortMap::CreatePort(result->message_handler()));
  #if defined(DEBUG)
    // Verify that we are never reusing a live origin id.
    VerifyOriginId id_verifier(result->main_port());
    Isolate::VisitIsolates(&id_verifier);
  #endif
    result->set_origin_id(result->main_port());
    result->set_pause_capability(result->random()->NextUInt64());
    result->set_terminate_capability(result->random()->NextUInt64());
  
  #if !defined(PRODUCT)
    result->debugger_ = new Debugger(result);
  #endif
  
    // Now we register the isolate in the group. From this point on any GC would
    // traverse the isolate roots (before this point, the roots are only pointing
    // to vm-isolate objects, e.g. null)
    isolate_group->RegisterIsolate(result);
  
    if (ServiceIsolate::NameEquals(name_prefix)) {
      ASSERT(!ServiceIsolate::Exists());
      ServiceIsolate::SetServiceIsolate(result);
  #if !defined(DART_PRECOMPILED_RUNTIME)
    } else if (KernelIsolate::NameEquals(name_prefix)) {
      ASSERT(!KernelIsolate::Exists());
      KernelIsolate::SetKernelIsolate(result);
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
    }
  
    if (FLAG_trace_isolates) {
      if (name_prefix == nullptr || strcmp(name_prefix, "vm-isolate") != 0) {
        OS::PrintErr(
            "[+] Starting isolate:\n"
            "\tisolate:    %s\n",
            result->name());
      }
    }
  
    // Add to isolate list. Shutdown and delete the isolate on failure.
    if (!TryMarkIsolateReady(result)) {
      result->LowLevelShutdown();
      Isolate::LowLevelCleanup(result);
      return nullptr;
    }
  
    return result;
  }
  
  Thread* Isolate::mutator_thread() const {
    ASSERT(thread_registry() != nullptr);
    return mutator_thread_;
  }
  
  ObjectPtr IsolateGroup::CallTagHandler(Dart_LibraryTag tag,
                                         const Object& arg1,
                                         const Object& arg2) {
    Thread* thread = Thread::Current();
    Api::Scope api_scope(thread);
    Dart_Handle api_arg1 = Api::NewHandle(thread, arg1.ptr());
    Dart_Handle api_arg2 = Api::NewHandle(thread, arg2.ptr());
    Dart_Handle api_result;
    {
      TransitionVMToNative transition(thread);
      ASSERT(HasTagHandler());
      api_result = library_tag_handler()(tag, api_arg1, api_arg2);
    }
    return Api::UnwrapHandle(api_result);
  }
  
  ObjectPtr Isolate::CallDeferredLoadHandler(intptr_t id) {
    Thread* thread = Thread::Current();
    Api::Scope api_scope(thread);
    Dart_Handle api_result;
    {
      TransitionVMToNative transition(thread);
      RELEASE_ASSERT(HasDeferredLoadHandler());
      api_result = group()->deferred_load_handler()(id);
    }
    return Api::UnwrapHandle(api_result);
  }
  
  void IsolateGroup::SetupImagePage(const uint8_t* image_buffer,
                                    bool is_executable) {
    Image image(image_buffer);
    heap()->SetupImagePage(image.object_start(), image.object_size(),
                           is_executable);
  }
  
  void Isolate::ScheduleInterrupts(uword interrupt_bits) {
    // We take the threads lock here to ensure that the mutator thread does not
    // exit the isolate while we are trying to schedule interrupts on it.
    MonitorLocker ml(group()->threads_lock());
    Thread* mthread = mutator_thread();
    if (mthread != nullptr) {
      mthread->ScheduleInterrupts(interrupt_bits);
    }
  }
  
  void Isolate::set_name(const char* name) {
    free(name_);
    name_ = Utils::StrDup(name);
  }
  
  int64_t IsolateGroup::UptimeMicros() const {
    return OS::GetCurrentMonotonicMicros() - start_time_micros_;
  }
  
  int64_t Isolate::UptimeMicros() const {
    return OS::GetCurrentMonotonicMicros() - start_time_micros_;
  }
  
  Dart_Port Isolate::origin_id() {
    MutexLocker ml(&origin_id_mutex_);
    return origin_id_;
  }
  
  void Isolate::set_origin_id(Dart_Port id) {
    MutexLocker ml(&origin_id_mutex_);
    ASSERT((id == main_port_ && origin_id_ == 0) || (origin_id_ == main_port_));
    origin_id_ = id;
  }
  
  void Isolate::set_finalizers(const GrowableObjectArray& value) {
    finalizers_ = value.ptr();
  }
  
  bool Isolate::IsPaused() const {
  #if defined(PRODUCT)
    return false;
  #else
    return (debugger_ != nullptr) && (debugger_->PauseEvent() != nullptr);
  #endif  // !defined(PRODUCT)
  }
  
  ErrorPtr Isolate::PausePostRequest() {
  #if !defined(PRODUCT)
    if (debugger_ == nullptr) {
      return Error::null();
    }
    ASSERT(!IsPaused());
    const Error& error = Error::Handle(debugger_->PausePostRequest());
    if (!error.IsNull()) {
      if (Thread::Current()->top_exit_frame_info() == 0) {
        return error.ptr();
      } else {
        Exceptions::PropagateError(error);
        UNREACHABLE();
      }
    }
  #endif
    return Error::null();
  }
  
  void Isolate::BuildName(const char* name_prefix) {
    ASSERT(name_ == nullptr);
    if (name_prefix == nullptr) {
      name_ = OS::SCreate(nullptr, "isolate-%" Pd64 "", main_port());
    } else {
      name_ = Utils::StrDup(name_prefix);
    }
  }
  
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  bool IsolateGroup::CanReload() {
    // We only call this method on the mutator thread. Normally the caller is
    // inside of the "reloadSources" service OOB message handler. Though
    // we also use it in the slow path of StackOverflowCheck in the artificial
    // --hot-reload-test-mode like flags.
    //
    // During reload itself we don't process OOB messages and don't execute Dart
    // code, so the caller should implicitly have a guarantee we're not reloading
    // already.
    RELEASE_ASSERT(!IsReloading());
  
    // We only allow reload to take place from the point on where the first
    // isolate within an isolate group has setup it's root library. From that
    // point on it's safe to perform hot-reload.
    auto thread = Thread::Current();
    if (object_store()->root_library() == Library::null()) {
      return false;
    }
  
    // We only care about the current thread's [NoReloadScope]. If we're inside
    // one we cannot reload right now. Though if another isolate's mutator
    // thread is inside such a scope, the multi-isolate reload will simply wait
    // until it's out of that scope again.
    if (thread->no_reload_scope_depth_ != 0) {
      return false;
    }
  
    return !IsolateGroup::IsSystemIsolateGroup(this) &&
           OSThread::Current()->HasStackHeadroom(64 * KB);
  }
  
  bool IsolateGroup::ReloadSources(JSONStream* js,
                                   bool force_reload,
                                   const char* root_script_url,
                                   const char* packages_url,
                                   bool dont_delete_reload_context) {
    // Ensure all isolates inside the isolate group are paused at a place where we
    // can safely do a reload.
    ReloadOperationScope reload_operation(Thread::Current());
  
    ASSERT(!IsReloading());
  
    auto shared_class_table = IsolateGroup::Current()->shared_class_table();
    std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
        new IsolateGroupReloadContext(this, shared_class_table, js));
    group_reload_context_ = group_reload_context;
  
    SetHasAttemptedReload(true);
    program_reload_context_ =
        new ProgramReloadContext(group_reload_context_, this);
    const bool success =
        group_reload_context_->Reload(force_reload, root_script_url, packages_url,
                                      /*kernel_buffer=*/nullptr,
                                      /*kernel_buffer_size=*/0);
    if (!dont_delete_reload_context) {
      DeleteReloadContext();
    }
    return success;
  }
  
  bool IsolateGroup::ReloadKernel(JSONStream* js,
                                  bool force_reload,
                                  const uint8_t* kernel_buffer,
                                  intptr_t kernel_buffer_size,
                                  bool dont_delete_reload_context) {
    // Ensure all isolates inside the isolate group are paused at a place where we
    // can safely do a reload.
    ReloadOperationScope reload_operation(Thread::Current());
  
    ASSERT(!IsReloading());
  
    auto shared_class_table = IsolateGroup::Current()->shared_class_table();
    std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
        new IsolateGroupReloadContext(this, shared_class_table, js));
    group_reload_context_ = group_reload_context;
  
    SetHasAttemptedReload(true);
    program_reload_context_ =
        new ProgramReloadContext(group_reload_context_, this);
    const bool success = group_reload_context_->Reload(
        force_reload,
        /*root_script_url=*/nullptr,
        /*packages_url=*/nullptr, kernel_buffer, kernel_buffer_size);
    if (!dont_delete_reload_context) {
      DeleteReloadContext();
    }
    return success;
  }
  
  void IsolateGroup::DeleteReloadContext() {
    // Another thread may be in the middle of GetClassForHeapWalkAt.
    GcSafepointOperationScope safepoint_scope(Thread::Current());
    group_reload_context_.reset();
  
    delete program_reload_context_;
    program_reload_context_ = nullptr;
  }
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
  const char* Isolate::MakeRunnable() {
    MutexLocker ml(&mutex_);
    // Check if we are in a valid state to make the isolate runnable.
    if (is_runnable() == true) {
      return "Isolate is already runnable";
    }
    if (group()->object_store()->root_library() == Library::null()) {
      return "The embedder has to ensure there is a root library (e.g. by "
             "calling Dart_LoadScriptFromKernel ).";
    }
    MakeRunnableLocked();
    return nullptr;
  }
  
  void Isolate::MakeRunnableLocked() {
    ASSERT(mutex_.IsOwnedByCurrentThread());
    ASSERT(!is_runnable());
    ASSERT(group()->object_store()->root_library() != Library::null());
  
    // Set the isolate as runnable and if we are being spawned schedule
    // isolate on thread pool for execution.
    set_is_runnable(true);
  #ifndef PRODUCT
    if (!Isolate::IsSystemIsolate(this)) {
      if (FLAG_pause_isolates_on_unhandled_exceptions) {
        debugger()->SetExceptionPauseInfo(kPauseOnUnhandledExceptions);
      }
    }
  #endif  // !PRODUCT
  #if defined(SUPPORT_TIMELINE)
    TimelineStream* stream = Timeline::GetIsolateStream();
    ASSERT(stream != nullptr);
    TimelineEvent* event = stream->StartEvent();
    if (event != nullptr) {
      event->Instant("Runnable");
      event->Complete();
    }
  #endif
  #ifndef PRODUCT
    if (!Isolate::IsSystemIsolate(this) && Service::isolate_stream.enabled()) {
      ServiceEvent runnableEvent(this, ServiceEvent::kIsolateRunnable);
      Service::HandleEvent(&runnableEvent, /* enter_safepoint */ false);
    }
    GetRunnableLatencyMetric()->set_value(UptimeMicros());
  #endif  // !PRODUCT
  }
  
  bool Isolate::VerifyPauseCapability(const Object& capability) const {
    return !capability.IsNull() && capability.IsCapability() &&
           (pause_capability() == Capability::Cast(capability).Id());
  }
  
  bool Isolate::VerifyTerminateCapability(const Object& capability) const {
    return !capability.IsNull() && capability.IsCapability() &&
           (terminate_capability() == Capability::Cast(capability).Id());
  }
  
  bool Isolate::AddResumeCapability(const Capability& capability) {
    // Ensure a limit for the number of resume capabilities remembered.
    static const intptr_t kMaxResumeCapabilities =
        compiler::target::kSmiMax / (6 * kWordSize);
  
    const GrowableObjectArray& caps = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->resume_capabilities());
    Capability& current = Capability::Handle(current_zone());
    intptr_t insertion_index = -1;
    for (intptr_t i = 0; i < caps.Length(); i++) {
      current ^= caps.At(i);
      if (current.IsNull()) {
        if (insertion_index < 0) {
          insertion_index = i;
        }
      } else if (current.Id() == capability.Id()) {
        return false;
      }
    }
    if (insertion_index < 0) {
      if (caps.Length() >= kMaxResumeCapabilities) {
        // Cannot grow the array of resume capabilities beyond its max. Additional
        // pause requests are ignored. In practice will never happen as we will
        // run out of memory beforehand.
        return false;
      }
      caps.Add(capability);
    } else {
      caps.SetAt(insertion_index, capability);
    }
    return true;
  }
  
  bool Isolate::RemoveResumeCapability(const Capability& capability) {
    const GrowableObjectArray& caps = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->resume_capabilities());
    Capability& current = Capability::Handle(current_zone());
    for (intptr_t i = 0; i < caps.Length(); i++) {
      current ^= caps.At(i);
      if (!current.IsNull() && (current.Id() == capability.Id())) {
        // Remove the matching capability from the list.
        current = Capability::null();
        caps.SetAt(i, current);
        return true;
      }
    }
    return false;
  }
  
  // TODO(iposva): Remove duplicated code and start using some hash based
  // structure instead of these linear lookups.
  void Isolate::AddExitListener(const SendPort& listener,
                                const Instance& response) {
    // Ensure a limit for the number of listeners remembered.
    static const intptr_t kMaxListeners =
        compiler::target::kSmiMax / (12 * kWordSize);
  
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->exit_listeners());
    SendPort& current = SendPort::Handle(current_zone());
    intptr_t insertion_index = -1;
    for (intptr_t i = 0; i < listeners.Length(); i += 2) {
      current ^= listeners.At(i);
      if (current.IsNull()) {
        if (insertion_index < 0) {
          insertion_index = i;
        }
      } else if (current.Id() == listener.Id()) {
        listeners.SetAt(i + 1, response);
        return;
      }
    }
    if (insertion_index < 0) {
      if (listeners.Length() >= kMaxListeners) {
        // Cannot grow the array of listeners beyond its max. Additional
        // listeners are ignored. In practice will never happen as we will
        // run out of memory beforehand.
        return;
      }
      listeners.Add(listener);
      listeners.Add(response);
    } else {
      listeners.SetAt(insertion_index, listener);
      listeners.SetAt(insertion_index + 1, response);
    }
  }
  
  void Isolate::RemoveExitListener(const SendPort& listener) {
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->exit_listeners());
    SendPort& current = SendPort::Handle(current_zone());
    for (intptr_t i = 0; i < listeners.Length(); i += 2) {
      current ^= listeners.At(i);
      if (!current.IsNull() && (current.Id() == listener.Id())) {
        // Remove the matching listener from the list.
        current = SendPort::null();
        listeners.SetAt(i, current);
        listeners.SetAt(i + 1, Object::null_instance());
        return;
      }
    }
  }
  
  void Isolate::NotifyExitListeners() {
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->exit_listeners());
    if (listeners.IsNull()) return;
  
    SendPort& listener = SendPort::Handle(current_zone());
    Instance& response = Instance::Handle(current_zone());
    for (intptr_t i = 0; i < listeners.Length(); i += 2) {
      listener ^= listeners.At(i);
      if (!listener.IsNull()) {
        Dart_Port port_id = listener.Id();
        response ^= listeners.At(i + 1);
        PortMap::PostMessage(SerializeMessage(port_id, response));
      }
    }
  }
  
  void Isolate::AddErrorListener(const SendPort& listener) {
    // Ensure a limit for the number of listeners remembered.
    static const intptr_t kMaxListeners =
        compiler::target::kSmiMax / (6 * kWordSize);
  
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->error_listeners());
    SendPort& current = SendPort::Handle(current_zone());
    intptr_t insertion_index = -1;
    for (intptr_t i = 0; i < listeners.Length(); i++) {
      current ^= listeners.At(i);
      if (current.IsNull()) {
        if (insertion_index < 0) {
          insertion_index = i;
        }
      } else if (current.Id() == listener.Id()) {
        return;
      }
    }
    if (insertion_index < 0) {
      if (listeners.Length() >= kMaxListeners) {
        // Cannot grow the array of listeners beyond its max. Additional
        // listeners are ignored. In practice will never happen as we will
        // run out of memory beforehand.
        return;
      }
      listeners.Add(listener);
    } else {
      listeners.SetAt(insertion_index, listener);
    }
  }
  
  void Isolate::RemoveErrorListener(const SendPort& listener) {
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->error_listeners());
    SendPort& current = SendPort::Handle(current_zone());
    for (intptr_t i = 0; i < listeners.Length(); i++) {
      current ^= listeners.At(i);
      if (!current.IsNull() && (current.Id() == listener.Id())) {
        // Remove the matching listener from the list.
        current = SendPort::null();
        listeners.SetAt(i, current);
        return;
      }
    }
  }
  
  bool Isolate::NotifyErrorListeners(const char* message,
                                     const char* stacktrace) {
    const GrowableObjectArray& listeners = GrowableObjectArray::Handle(
        current_zone(), isolate_object_store()->error_listeners());
    if (listeners.IsNull()) return false;
  
    Dart_CObject arr;
    Dart_CObject* arr_values[2];
    arr.type = Dart_CObject_kArray;
    arr.value.as_array.length = 2;
    arr.value.as_array.values = arr_values;
    Dart_CObject msg;
    msg.type = Dart_CObject_kString;
    msg.value.as_string = const_cast<char*>(message);
    arr_values[0] = &msg;
    Dart_CObject stack;
    if (stacktrace == NULL) {
      stack.type = Dart_CObject_kNull;
    } else {
      stack.type = Dart_CObject_kString;
      stack.value.as_string = const_cast<char*>(stacktrace);
    }
    arr_values[1] = &stack;
  
    SendPort& listener = SendPort::Handle(current_zone());
    for (intptr_t i = 0; i < listeners.Length(); i++) {
      listener ^= listeners.At(i);
      if (!listener.IsNull()) {
        Dart_Port port_id = listener.Id();
        PortMap::PostMessage(SerializeMessage(current_zone(), port_id, &arr));
      }
    }
    return listeners.Length() > 0;
  }
  
  static void ShutdownIsolate(uword parameter) {
    Dart_EnterIsolate(reinterpret_cast<Dart_Isolate>(parameter));
    Dart_ShutdownIsolate();
  }
  
  void Isolate::SetStickyError(ErrorPtr sticky_error) {
    ASSERT(
        ((sticky_error_ == Error::null()) || (sticky_error == Error::null())) &&
        (sticky_error != sticky_error_));
    sticky_error_ = sticky_error;
  }
  
  void Isolate::Run() {
    message_handler()->Run(group()->thread_pool(), nullptr, ShutdownIsolate,
                           reinterpret_cast<uword>(this));
  }
  
  #if !defined(PRODUCT)
  void Isolate::set_current_sample_block(SampleBlock* current) {
    ASSERT(current_sample_block_lock_.IsOwnedByCurrentThread());
    if (current != nullptr) {
      current->set_is_allocation_block(false);
      current->set_owner(this);
    }
    current_sample_block_ = current;
  }
  
  void Isolate::set_current_allocation_sample_block(SampleBlock* current) {
    if (current != nullptr) {
      current->set_is_allocation_block(true);
      current->set_owner(this);
    }
    current_allocation_sample_block_ = current;
  }
  
  void Isolate::FreeSampleBlock(SampleBlock* block) {
    if (block == nullptr) {
      return;
    }
    SampleBlock* head;
    // We're pushing the freed sample block to the front of the free_block_list_,
    // which means the last element of the list will be the oldest freed sample.
    do {
      head = free_block_list_.load(std::memory_order_acquire);
      block->next_free_ = head;
    } while (!free_block_list_.compare_exchange_weak(head, block,
                                                     std::memory_order_release));
  }
  
  class StreamableSampleFilter : public SampleFilter {
   public:
    explicit StreamableSampleFilter(Dart_Port port)
        : SampleFilter(port, kNoTaskFilter, -1, -1) {}
  
    bool FilterSample(Sample* sample) override {
      const UserTag& tag =
          UserTag::Handle(UserTag::FindTagById(sample->user_tag()));
      return tag.streamable();
    }
  };
  
  void Isolate::ProcessFreeSampleBlocks(Thread* thread) {
    SampleBlock* head = free_block_list_.exchange(nullptr);
    if (head == nullptr) {
      // No sample blocks to process.
      return;
    }
    // Reverse the list before processing so older blocks are streamed and reused
    // first.
    SampleBlock* reversed_head = nullptr;
    do {
      SampleBlock* next = head->next_free_;
      if (reversed_head == nullptr) {
        reversed_head = head;
        reversed_head->next_free_ = nullptr;
      } else {
        head->next_free_ = reversed_head;
        reversed_head = head;
      }
      head = next;
    } while (head != nullptr);
    head = reversed_head;
    if (Service::profiler_stream.enabled() && !IsSystemIsolate(this)) {
      StackZone zone(thread);
      SampleBlockListProcessor buffer(head);
      if (buffer.HasStreamableSamples(thread)) {
        HandleScope handle_scope(thread);
        StreamableSampleFilter filter(main_port());
        Profile profile;
        profile.Build(thread, &filter, &buffer);
        ASSERT(profile.sample_count() > 0);
        ServiceEvent event(this, ServiceEvent::kCpuSamples);
        event.set_cpu_profile(&profile);
        Service::HandleEvent(&event);
      }
    }
  
    do {
      SampleBlock* next = head->next_free_;
      head->next_free_ = nullptr;
      head->evictable_ = true;
      Profiler::sample_block_buffer()->FreeBlock(head);
      head = next;
      thread->CheckForSafepoint();
    } while (head != nullptr);
  }
  #endif  // !defined(PRODUCT)
  
  void Isolate::RunAndCleanupFinalizersOnShutdown() {
    if (finalizers_ == GrowableObjectArray::null()) return;
  
    // Ensure we have a zone and handle scope so that we can call VM functions,
    // but we no longer allocate new heap objects.
    Thread* thread = Thread::Current();
    StackZone stack_zone(thread);
    HandleScope handle_scope(thread);
    NoSafepointScope no_safepoint_scope;
  
    // Set live finalizers isolate to null, before deleting the message handler.
    const auto& finalizers =
        GrowableObjectArray::Handle(stack_zone.GetZone(), finalizers_);
    if (!finalizers.IsNull()) {
      const intptr_t num_finalizers = finalizers.Length();
      auto& weak_reference = WeakReference::Handle(stack_zone.GetZone());
      auto& finalizer = FinalizerBase::Handle(stack_zone.GetZone());
      auto& current_entry = FinalizerEntry::Handle(stack_zone.GetZone());
      auto& all_entries = LinkedHashSet::Handle(stack_zone.GetZone());
      for (int i = 0; i < num_finalizers; i++) {
        weak_reference ^= finalizers.At(i);
        finalizer ^= weak_reference.target();
        if (!finalizer.IsNull()) {
          if (finalizer.isolate() == this) {
            if (FLAG_trace_finalizers) {
              THR_Print("Isolate %p Setting finalizer %p isolate to null\n", this,
                        finalizer.ptr()->untag());
            }
            // Finalizer was not sent to another isolate with send and exit.
            finalizer.set_isolate(nullptr);
          } else {
            // TODO(http://dartbug.com/47777): Send and exit support.
            UNREACHABLE();
          }
  
          if (finalizer.IsNativeFinalizer()) {
            // Immediately call native callback.
            const auto& native_finalizer = NativeFinalizer::Cast(finalizer);
            all_entries = finalizer.all_entries();
            LinkedHashSet::Iterator iterator(all_entries);
            while (iterator.MoveNext()) {
              current_entry ^= iterator.CurrentKey();
              native_finalizer.RunCallback(current_entry, "Isolate shutdown");
            }
          }
        }
      }
    }
  }
  
  void Isolate::LowLevelShutdown() {
    // Ensure we have a zone and handle scope so that we can call VM functions,
    // but we no longer allocate new heap objects.
    Thread* thread = Thread::Current();
    StackZone stack_zone(thread);
    HandleScope handle_scope(thread);
    NoSafepointScope no_safepoint_scope;
  
    // Notify exit listeners that this isolate is shutting down.
    if (group()->object_store() != nullptr) {
      const Error& error = Error::Handle(thread->sticky_error());
      if (error.IsNull() || !error.IsUnwindError() ||
          UnwindError::Cast(error).is_user_initiated()) {
        NotifyExitListeners();
      }
    }
  
    // Close all the ports owned by this isolate.
    PortMap::ClosePorts(message_handler());
  
    // Fail fast if anybody tries to post any more messages to this isolate.
    delete message_handler();
    set_message_handler(nullptr);
  
  #if !defined(PRODUCT)
    if (FLAG_dump_megamorphic_stats) {
      MegamorphicCacheTable::PrintSizes(this);
    }
    if (FLAG_dump_symbol_stats) {
      Symbols::DumpStats(group());
    }
    if (FLAG_trace_isolates) {
      group()->heap()->PrintSizes();
      OS::PrintErr(
          "[-] Stopping isolate:\n"
          "\tisolate:    %s\n",
          name());
    }
    if (FLAG_print_metrics) {
      LogBlock lb;
      OS::PrintErr("Printing metrics for %s\n", name());
  #define ISOLATE_GROUP_METRIC_PRINT(type, variable, name, unit)                 \
    OS::PrintErr("%s\n", isolate_group_->Get##variable##Metric()->ToString());
      ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_PRINT)
  #undef ISOLATE_GROUP_METRIC_PRINT
  #define ISOLATE_METRIC_PRINT(type, variable, name, unit)                       \
    OS::PrintErr("%s\n", metric_##variable##_.ToString());
      ISOLATE_METRIC_LIST(ISOLATE_METRIC_PRINT)
  #undef ISOLATE_METRIC_PRINT
      OS::PrintErr("\n");
    }
  #endif  // !defined(PRODUCT)
  }
  
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  void IsolateGroup::MaybeIncreaseReloadEveryNStackOverflowChecks() {
    if (FLAG_reload_every_back_off) {
      if (reload_every_n_stack_overflow_checks_ < 5000) {
        reload_every_n_stack_overflow_checks_ += 99;
      } else {
        const auto old_value = reload_every_n_stack_overflow_checks_;
        reload_every_n_stack_overflow_checks_ = old_value * old_value;
      }
      // Cap the value.
      if (reload_every_n_stack_overflow_checks_ > 1000000) {
        reload_every_n_stack_overflow_checks_ = 1000000;
      }
    }
  }
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
  void Isolate::set_forward_table_new(WeakTable* table) {
    std::unique_ptr<WeakTable> value(table);
    forward_table_new_ = std::move(value);
  }
  void Isolate::set_forward_table_old(WeakTable* table) {
    std::unique_ptr<WeakTable> value(table);
    forward_table_old_ = std::move(value);
  }
  
  void Isolate::Shutdown() {
    Thread* thread = Thread::Current();
    ASSERT(this == thread->isolate());
  
    // Don't allow anymore dart code to execution on this isolate.
    thread->ClearStackLimit();
  
    {
      StackZone zone(thread);
      HandleScope handle_scope(thread);
      ServiceIsolate::SendIsolateShutdownMessage();
  #if !defined(PRODUCT)
      debugger()->Shutdown();
      // Cleanup profiler state.
      SampleBlock* cpu_block = current_sample_block();
      if (cpu_block != nullptr) {
        cpu_block->release_block();
      }
      SampleBlock* allocation_block = current_allocation_sample_block();
      if (allocation_block != nullptr) {
        allocation_block->release_block();
      }
  
      // Process the previously assigned sample blocks if we're using the
      // profiler's sample buffer. Some tests create their own SampleBlockBuffer
      // and handle block processing themselves.
      if ((cpu_block != nullptr || allocation_block != nullptr) &&
          Profiler::sample_block_buffer() != nullptr) {
        StackZone zone(thread);
        HandleScope handle_scope(thread);
        Profiler::sample_block_buffer()->ProcessCompletedBlocks();
      }
  #endif
    }
  
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    if (FLAG_check_reloaded && is_runnable() && !Isolate::IsSystemIsolate(this)) {
      if (!group()->HasAttemptedReload()) {
        FATAL(
            "Isolate did not reload before exiting and "
            "--check-reloaded is enabled.\n");
      }
    }
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
    // Then, proceed with low-level teardown.
    Isolate::UnMarkIsolateReady(this);
  
    // Ensure native finalizers are run before isolate has shutdown message is
    // sent. This way users can rely on the exit message that an isolate will not
    // run any Dart code anymore _and_ will not run any native finalizers anymore.
    RunAndCleanupFinalizersOnShutdown();
  
    // Post message before LowLevelShutdown that sends onExit message.
    // This ensures that exit message comes last.
    if (bequest_ != nullptr) {
      auto beneficiary = bequest_->beneficiary();
      auto handle = bequest_->TakeHandle();
      PortMap::PostMessage(
          Message::New(beneficiary, handle, Message::kNormalPriority));
      bequest_.reset();
    }
  
    LowLevelShutdown();
  
    // Now we can unregister from the thread, invoke cleanup callback, delete the
    // isolate (and possibly the isolate group).
    Isolate::LowLevelCleanup(this);
  }
  
  void Isolate::LowLevelCleanup(Isolate* isolate) {
  #if !defined(DART_PRECOMPILED_RUNTIME)
    if (KernelIsolate::IsKernelIsolate(isolate)) {
      KernelIsolate::SetKernelIsolate(nullptr);
    }
  #endif
    if (ServiceIsolate::IsServiceIsolate(isolate)) {
      ServiceIsolate::SetServiceIsolate(nullptr);
    }
  
    // Cache these two fields, since they are no longer available after the
    // `delete isolate` further down.
    IsolateGroup* isolate_group = isolate->isolate_group_;
    Dart_IsolateCleanupCallback cleanup = isolate->on_cleanup_callback();
    auto callback_data = isolate->init_callback_data_;
  
    // From this point on the isolate is no longer visited by GC (which is ok,
    // since we're just going to delete it anyway).
    isolate_group->UnregisterIsolate(isolate);
  
    // From this point on the isolate doesn't participate in safepointing
    // requests anymore.
    Thread::ExitIsolate();
  
    // Now it's safe to delete the isolate.
    delete isolate;
  
    // Run isolate specific cleanup function for all non "vm-isolate's.
    const bool is_vm_isolate = Dart::vm_isolate() == isolate;
    if (!is_vm_isolate) {
      if (cleanup != nullptr) {
        cleanup(isolate_group->embedder_data(), callback_data);
      }
    }
  
    const bool shutdown_group =
        isolate_group->UnregisterIsolateDecrementCount(isolate);
    if (shutdown_group) {
      KernelIsolate::NotifyAboutIsolateGroupShutdown(isolate_group);
  
      if (!is_vm_isolate) {
        Thread::EnterIsolateGroupAsHelper(isolate_group, Thread::kUnknownTask,
                                          /*bypass_safepoint=*/false);
  #if !defined(DART_PRECOMPILED_RUNTIME)
        BackgroundCompiler::Stop(isolate_group);
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
  
        // Finalize any weak persistent handles with a non-null referent with
        // isolate group still being available.
        FinalizeWeakPersistentHandlesVisitor visitor(isolate_group);
        isolate_group->api_state()->VisitWeakHandlesUnlocked(&visitor);
  
        Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
      }
  
      // The "vm-isolate" does not have a thread pool.
      ASSERT(is_vm_isolate == (isolate_group->thread_pool() == nullptr));
      if (is_vm_isolate ||
          !isolate_group->thread_pool()->CurrentThreadIsWorker()) {
        isolate_group->Shutdown();
      } else {
        class ShutdownGroupTask : public ThreadPool::Task {
         public:
          explicit ShutdownGroupTask(IsolateGroup* isolate_group)
              : isolate_group_(isolate_group) {}
  
          virtual void Run() { isolate_group_->Shutdown(); }
  
         private:
          IsolateGroup* isolate_group_;
        };
        // The current thread is running on the isolate group's thread pool.
        // So we cannot safely delete the isolate group (and it's pool).
        // Instead we will destroy the isolate group on the VM-global pool.
        if (FLAG_trace_shutdown) {
          OS::PrintErr("[+%" Pd64 "ms] : Scheduling shutdown on VM pool %s\n",
                       Dart::UptimeMillis(), isolate_group->source()->name);
        }
        Dart::thread_pool()->Run<ShutdownGroupTask>(isolate_group);
      }
    } else {
      // TODO(dartbug.com/36097): An isolate just died. A significant amount of
      // memory might have become unreachable. We should evaluate how to best
      // inform the GC about this situation.
    }
  }  // namespace dart
  
  Dart_InitializeIsolateCallback Isolate::initialize_callback_ = nullptr;
  Dart_IsolateGroupCreateCallback Isolate::create_group_callback_ = nullptr;
  Dart_IsolateShutdownCallback Isolate::shutdown_callback_ = nullptr;
  Dart_IsolateCleanupCallback Isolate::cleanup_callback_ = nullptr;
  Dart_IsolateGroupCleanupCallback Isolate::cleanup_group_callback_ = nullptr;
  Dart_RegisterKernelBlobCallback Isolate::register_kernel_blob_callback_ =
      nullptr;
  Dart_UnregisterKernelBlobCallback Isolate::unregister_kernel_blob_callback_ =
      nullptr;
  
  Random* IsolateGroup::isolate_group_random_ = nullptr;
  Monitor* Isolate::isolate_creation_monitor_ = nullptr;
  bool Isolate::creation_enabled_ = false;
  
  RwLock* IsolateGroup::isolate_groups_rwlock_ = nullptr;
  IntrusiveDList<IsolateGroup>* IsolateGroup::isolate_groups_ = nullptr;
  
  void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
                                    ValidationPolicy validate_frames) {
    ASSERT(visitor != nullptr);
  
    // Visit objects in the field table.
    // N.B.: The heap snapshot writer requires visiting the field table first, so
    // that the pointer visitation order aligns with order of field name metadata.
    if (!visitor->trace_values_through_fields()) {
      field_table()->VisitObjectPointers(visitor);
    }
  
    // Visit objects in the isolate object store.
    if (isolate_object_store() != nullptr) {
      isolate_object_store()->VisitObjectPointers(visitor);
    }
  
    visitor->clear_gc_root_type();
    // Visit the objects directly referenced from the isolate structure.
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&current_tag_));
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&default_tag_));
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&ic_miss_code_));
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&tag_table_));
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&sticky_error_));
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&finalizers_));
  #if !defined(PRODUCT)
    visitor->VisitPointer(
        reinterpret_cast<ObjectPtr*>(&pending_service_extension_calls_));
    visitor->VisitPointer(
        reinterpret_cast<ObjectPtr*>(&registered_service_extension_handlers_));
  #endif  // !defined(PRODUCT)
  
  #if !defined(PRODUCT)
    // Visit objects in the debugger.
    if (debugger() != nullptr) {
      debugger()->VisitObjectPointers(visitor);
    }
    if (ServiceIsolate::IsServiceIsolate(this)) {
      ServiceIsolate::VisitObjectPointers(visitor);
    }
  #endif  // !defined(PRODUCT)
  
  #if !defined(DART_PRECOMPILED_RUNTIME)
    // Visit objects that are being used for deoptimization.
    if (deopt_context() != nullptr) {
      deopt_context()->VisitObjectPointers(visitor);
    }
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
  
    visitor->VisitPointer(
        reinterpret_cast<ObjectPtr*>(&loaded_prefixes_set_storage_));
  
    if (pointers_to_verify_at_exit_.length() != 0) {
      visitor->VisitPointers(&pointers_to_verify_at_exit_[0],
                             pointers_to_verify_at_exit_.length());
    }
  }
  
  void IsolateGroup::ReleaseStoreBuffers() {
    thread_registry()->ReleaseStoreBuffers();
  }
  
  void Isolate::RememberLiveTemporaries() {
    if (mutator_thread_ != nullptr) {
      mutator_thread_->RememberLiveTemporaries();
    }
  }
  
  void Isolate::DeferredMarkLiveTemporaries() {
    if (mutator_thread_ != nullptr) {
      mutator_thread_->DeferredMarkLiveTemporaries();
    }
  }
  
  void Isolate::init_loaded_prefixes_set_storage() {
    ASSERT(loaded_prefixes_set_storage_ == nullptr);
    loaded_prefixes_set_storage_ =
        HashTables::New<UnorderedHashSet<LibraryPrefixMapTraits> >(4);
  }
  
  bool Isolate::IsPrefixLoaded(const LibraryPrefix& prefix) const {
    UnorderedHashSet<LibraryPrefixMapTraits> loaded_prefixes_set(
        loaded_prefixes_set_storage_);
    bool result = loaded_prefixes_set.GetOrNull(prefix) != Object::null();
    loaded_prefixes_set.Release();
    return result;
  }
  
  void Isolate::SetPrefixIsLoaded(const LibraryPrefix& prefix) {
    UnorderedHashSet<LibraryPrefixMapTraits> loaded_prefixes_set(
        loaded_prefixes_set_storage_);
    loaded_prefixes_set.InsertOrGet(prefix);
    loaded_prefixes_set_storage_ = loaded_prefixes_set.Release().ptr();
  }
  
  void IsolateGroup::EnableIncrementalBarrier(
      MarkingStack* marking_stack,
      MarkingStack* deferred_marking_stack) {
    ASSERT(marking_stack_ == nullptr);
    marking_stack_ = marking_stack;
    deferred_marking_stack_ = deferred_marking_stack;
    thread_registry()->AcquireMarkingStacks();
    ASSERT(Thread::Current()->is_marking());
  }
  
  void IsolateGroup::DisableIncrementalBarrier() {
    thread_registry()->ReleaseMarkingStacks();
    ASSERT(marking_stack_ != nullptr);
    marking_stack_ = nullptr;
    deferred_marking_stack_ = nullptr;
  }
  
  void IsolateGroup::ForEachIsolate(
      std::function<void(Isolate* isolate)> function,
      bool at_safepoint) {
    auto thread = Thread::Current();
    if (at_safepoint) {
      ASSERT(thread->IsAtSafepoint() ||
             (thread->task_kind() == Thread::kMutatorTask) ||
             (thread->task_kind() == Thread::kMarkerTask) ||
             (thread->task_kind() == Thread::kCompactorTask) ||
             (thread->task_kind() == Thread::kScavengerTask));
      for (Isolate* isolate : isolates_) {
        function(isolate);
      }
      return;
    }
    if (thread != nullptr && thread->IsAtSafepoint()) {
      for (Isolate* isolate : isolates_) {
        function(isolate);
      }
      return;
    }
    SafepointReadRwLocker ml(thread, isolates_lock_.get());
    for (Isolate* isolate : isolates_) {
      function(isolate);
    }
  }
  
  Isolate* IsolateGroup::FirstIsolate() const {
    SafepointReadRwLocker ml(Thread::Current(), isolates_lock_.get());
    return FirstIsolateLocked();
  }
  
  Isolate* IsolateGroup::FirstIsolateLocked() const {
    return isolates_.IsEmpty() ? nullptr : isolates_.First();
  }
  
  void IsolateGroup::RunWithStoppedMutatorsCallable(
      Callable* single_current_mutator,
      Callable* otherwise,
      bool use_force_growth_in_otherwise) {
    auto thread = Thread::Current();
    StoppedMutatorsScope stopped_mutators_scope(thread);
  
    if (thread->IsAtSafepoint()) {
      RELEASE_ASSERT(safepoint_handler()->IsOwnedByTheThread(thread));
      single_current_mutator->Call();
      return;
    }
  
    {
      SafepointReadRwLocker ml(thread, isolates_lock_.get());
      if (thread->IsMutatorThread() && ContainsOnlyOneIsolate()) {
        single_current_mutator->Call();
        return;
      }
    }
  
    // We use the more strict safepoint operation scope here (which ensures that
    // all other threads, including auxiliary threads are at a safepoint), even
    // though we only need to ensure that the mutator threads are stopped.
    if (use_force_growth_in_otherwise) {
      ForceGrowthSafepointOperationScope safepoint_scope(
          thread, SafepointLevel::kGCAndDeopt);
      otherwise->Call();
    } else {
      DeoptSafepointOperationScope safepoint_scope(thread);
      otherwise->Call();
    }
  }
  
  void IsolateGroup::VisitObjectPointers(ObjectPointerVisitor* visitor,
                                         ValidationPolicy validate_frames) {
    VisitSharedPointers(visitor);
    for (Isolate* isolate : isolates_) {
      isolate->VisitObjectPointers(visitor, validate_frames);
    }
    VisitStackPointers(visitor, validate_frames);
  }
  
  void IsolateGroup::VisitSharedPointers(ObjectPointerVisitor* visitor) {
    // if class table is shared, it's stored on isolate group
    if (class_table() != nullptr) {
      // Visit objects in the class table.
      class_table()->VisitObjectPointers(visitor);
    }
    api_state()->VisitObjectPointersUnlocked(visitor);
    // Visit objects in the object store.
    if (object_store() != nullptr) {
      object_store()->VisitObjectPointers(visitor);
    }
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&saved_unlinked_calls_));
    initial_field_table()->VisitObjectPointers(visitor);
  
    // Visit the boxed_field_list_.
    // 'boxed_field_list_' access via mutator and background compilation threads
    // is guarded with a monitor. This means that we can visit it only
    // when at safepoint or the field_list_mutex_ lock has been taken.
    visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&boxed_field_list_));
  
    NOT_IN_PRECOMPILED(background_compiler()->VisitPointers(visitor));
  
  #if !defined(PRODUCT)
    if (debugger() != nullptr) {
      debugger()->VisitObjectPointers(visitor);
    }
  #endif
  
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    // Visit objects that are being used for isolate reload.
    if (program_reload_context() != nullptr) {
      program_reload_context()->VisitObjectPointers(visitor);
      program_reload_context()->group_reload_context()->VisitObjectPointers(
          visitor);
    }
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  
    if (source()->loaded_blobs_ != nullptr) {
      visitor->VisitPointer(
          reinterpret_cast<ObjectPtr*>(&(source()->loaded_blobs_)));
    }
  
    if (become() != nullptr) {
      become()->VisitObjectPointers(visitor);
    }
  }
  
  void IsolateGroup::VisitStackPointers(ObjectPointerVisitor* visitor,
                                        ValidationPolicy validate_frames) {
    visitor->set_gc_root_type("stack");
  
    // Visit objects in all threads (e.g. Dart stack, handles in zones), except
    // for the mutator threads themselves.
    thread_registry()->VisitObjectPointers(this, visitor, validate_frames);
  
    for (Isolate* isolate : isolates_) {
      // Visit mutator thread, even if the isolate isn't entered/scheduled
      // (there might be live API handles to visit).
      if (isolate->mutator_thread_ != nullptr) {
        isolate->mutator_thread_->VisitObjectPointers(visitor, validate_frames);
      }
    }
  
    visitor->clear_gc_root_type();
  }
  
  void IsolateGroup::VisitObjectIdRingPointers(ObjectPointerVisitor* visitor) {
  #if !defined(PRODUCT)
    for (Isolate* isolate : isolates_) {
      ObjectIdRing* ring = isolate->object_id_ring();
      if (ring != nullptr) {
        ring->VisitPointers(visitor);
      }
    }
  #endif  // !defined(PRODUCT)
  }
  
  void IsolateGroup::VisitWeakPersistentHandles(HandleVisitor* visitor) {
    api_state()->VisitWeakHandlesUnlocked(visitor);
  }
  
  void IsolateGroup::DeferredMarkLiveTemporaries() {
    ForEachIsolate(
        [&](Isolate* isolate) { isolate->DeferredMarkLiveTemporaries(); },
        /*at_safepoint=*/true);
  }
  
  void IsolateGroup::RememberLiveTemporaries() {
    ForEachIsolate([&](Isolate* isolate) { isolate->RememberLiveTemporaries(); },
                   /*at_safepoint=*/true);
  }
  
  ClassPtr IsolateGroup::GetClassForHeapWalkAt(intptr_t cid) {
    ClassPtr raw_class = nullptr;
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    if (IsReloading()) {
      raw_class = program_reload_context()->GetClassForHeapWalkAt(cid);
    } else {
      raw_class = class_table()->At(cid);
    }
  #else
    raw_class = class_table()->At(cid);
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    ASSERT(raw_class != nullptr);
    ASSERT(remapping_cids() || raw_class->untag()->id_ == cid);
    return raw_class;
  }
  
  intptr_t IsolateGroup::GetClassSizeForHeapWalkAt(intptr_t cid) {
  #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
    if (IsReloading()) {
      return group_reload_context_->GetClassSizeForHeapWalkAt(cid);
    } else {
      return shared_class_table()->SizeAt(cid);
    }
  #else
    return shared_class_table()->SizeAt(cid);
  #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  }
  
  #if !defined(PRODUCT)
  ObjectIdRing* Isolate::EnsureObjectIdRing() {
    if (object_id_ring_ == nullptr) {
      object_id_ring_ = new ObjectIdRing();
    }
    return object_id_ring_;
  }
  #endif  // !defined(PRODUCT)
  
  #ifndef PRODUCT
  static const char* ExceptionPauseInfoToServiceEnum(Dart_ExceptionPauseInfo pi) {
    switch (pi) {
      case kPauseOnAllExceptions:
        return "All";
      case kNoPauseOnExceptions:
        return "None";
      case kPauseOnUnhandledExceptions:
        return "Unhandled";
      default:
        UNIMPLEMENTED();
        return nullptr;
    }
  }
  
  void Isolate::PrintJSON(JSONStream* stream, bool ref) {
    JSONObject jsobj(stream);
    jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate"));
    jsobj.AddServiceId(ISOLATE_SERVICE_ID_FORMAT_STRING,
                       static_cast<int64_t>(main_port()));
  
    jsobj.AddProperty("name", name());
    jsobj.AddPropertyF("number", "%" Pd64 "", static_cast<int64_t>(main_port()));
    jsobj.AddProperty("isSystemIsolate", is_system_isolate());
    if (ref) {
      return;
    }
    jsobj.AddPropertyF("_originNumber", "%" Pd64 "",
                       static_cast<int64_t>(origin_id()));
    int64_t uptime_millis = UptimeMicros() / kMicrosecondsPerMillisecond;
    int64_t start_time = OS::GetCurrentTimeMillis() - uptime_millis;
    jsobj.AddPropertyTimeMillis("startTime", start_time);
    {
      JSONObject jsheap(&jsobj, "_heaps");
      group()->heap()->PrintToJSONObject(Heap::kNew, &jsheap);
      group()->heap()->PrintToJSONObject(Heap::kOld, &jsheap);
    }
  
    {
  // Stringification macros
  // See https://gcc.gnu.org/onlinedocs/gcc-4.8.5/cpp/Stringification.html
  #define TO_STRING(s) STR(s)
  #define STR(s) #s
  
  #define ADD_ISOLATE_FLAGS(when, name, bitname, isolate_flag_name, flag_name)   \
    {                                                                            \
      JSONObject jsflag(&jsflags);                                               \
      jsflag.AddProperty("name", TO_STRING(name));                               \
      jsflag.AddProperty("valueAsString", name() ? "true" : "false");            \
    }
      JSONArray jsflags(&jsobj, "isolateFlags");
      BOOL_ISOLATE_FLAG_LIST(ADD_ISOLATE_FLAGS)
  #undef ADD_ISOLATE_FLAGS
  #undef TO_STRING
  #undef STR
    }
  
    jsobj.AddProperty("runnable", is_runnable());
    jsobj.AddProperty("livePorts", message_handler()->live_ports());
    jsobj.AddProperty("pauseOnExit", message_handler()->should_pause_on_exit());
  #if !defined(DART_PRECOMPILED_RUNTIME)
    jsobj.AddProperty("_isReloading", group()->IsReloading());
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
  
    if (!is_runnable()) {
      // Isolate is not yet runnable.
      ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
      ServiceEvent pause_event(this, ServiceEvent::kNone);
      jsobj.AddProperty("pauseEvent", &pause_event);
    } else if (message_handler()->should_pause_on_start()) {
      if (message_handler()->is_paused_on_start()) {
        ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
        ServiceEvent pause_event(this, ServiceEvent::kPauseStart);
        jsobj.AddProperty("pauseEvent", &pause_event);
      } else {
        // Isolate is runnable but not paused on start.
        // Some service clients get confused if they see:
        // NotRunnable -> Runnable -> PausedAtStart
        // Treat Runnable+ShouldPauseOnStart as NotRunnable so they see:
        // NonRunnable -> PausedAtStart
        // The should_pause_on_start flag is set to false after resume.
        ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
        ServiceEvent pause_event(this, ServiceEvent::kNone);
        jsobj.AddProperty("pauseEvent", &pause_event);
      }
    } else if (message_handler()->is_paused_on_exit() &&
               ((debugger() == nullptr) ||
                (debugger()->PauseEvent() == nullptr))) {
      ServiceEvent pause_event(this, ServiceEvent::kPauseExit);
      jsobj.AddProperty("pauseEvent", &pause_event);
    } else if ((debugger() != nullptr) && (debugger()->PauseEvent() != nullptr) &&
               !ResumeRequest()) {
      jsobj.AddProperty("pauseEvent", debugger()->PauseEvent());
    } else {
      ServiceEvent pause_event(this, ServiceEvent::kResume);
  
      if (debugger() != nullptr) {
        // TODO(turnidge): Don't compute a full stack trace.
        DebuggerStackTrace* stack = debugger()->StackTrace();
        if (stack->Length() > 0) {
          pause_event.set_top_frame(stack->FrameAt(0));
        }
      }
      jsobj.AddProperty("pauseEvent", &pause_event);
    }
  
    const Library& lib = Library::Handle(group()->object_store()->root_library());
    if (!lib.IsNull()) {
      jsobj.AddProperty("rootLib", lib);
    }
  
    if (FLAG_profiler) {
      JSONObject tagCounters(&jsobj, "_tagCounters");
      vm_tag_counters()->PrintToJSONObject(&tagCounters);
    }
    if (Thread::Current()->sticky_error() != Object::null()) {
      Error& error = Error::Handle(Thread::Current()->sticky_error());
      ASSERT(!error.IsNull());
      jsobj.AddProperty("error", error, false);
    } else if (sticky_error() != Object::null()) {
      Error& error = Error::Handle(sticky_error());
      ASSERT(!error.IsNull());
      jsobj.AddProperty("error", error, false);
    }
  
    {
      const GrowableObjectArray& libs =
          GrowableObjectArray::Handle(group()->object_store()->libraries());
      intptr_t num_libs = libs.Length();
      Library& lib = Library::Handle();
  
      JSONArray lib_array(&jsobj, "libraries");
      for (intptr_t i = 0; i < num_libs; i++) {
        lib ^= libs.At(i);
        ASSERT(!lib.IsNull());
        lib_array.AddValue(lib);
      }
    }
  
    {
      JSONArray breakpoints(&jsobj, "breakpoints");
      if (debugger() != nullptr) {
        debugger()->PrintBreakpointsToJSONArray(&breakpoints);
      }
    }
  
    Dart_ExceptionPauseInfo pause_info = (debugger() != nullptr)
                                             ? debugger()->GetExceptionPauseInfo()
                                             : kNoPauseOnExceptions;
    jsobj.AddProperty("exceptionPauseMode",
                      ExceptionPauseInfoToServiceEnum(pause_info));
  
    if (debugger() != nullptr) {
      JSONObject settings(&jsobj, "_debuggerSettings");
      debugger()->PrintSettingsToJSONObject(&settings);
    }
  
    {
      GrowableObjectArray& handlers =
          GrowableObjectArray::Handle(registered_service_extension_handlers());
      if (!handlers.IsNull()) {
        JSONArray extensions(&jsobj, "extensionRPCs");
        String& handler_name = String::Handle();
        for (intptr_t i = 0; i < handlers.Length(); i += kRegisteredEntrySize) {
          handler_name ^= handlers.At(i + kRegisteredNameIndex);
          extensions.AddValue(handler_name.ToCString());
        }
      }
    }
  
    {
      JSONObject isolate_group(&jsobj, "isolate_group");
      group()->PrintToJSONObject(&isolate_group, /*ref=*/true);
    }
  }
  
  void Isolate::PrintMemoryUsageJSON(JSONStream* stream) {
    group()->heap()->PrintMemoryUsageJSON(stream);
  }
  
  #endif
  
  void Isolate::set_tag_table(const GrowableObjectArray& value) {
    tag_table_ = value.ptr();
  }
  
  void Isolate::set_current_tag(const UserTag& tag) {
    uword user_tag = tag.tag();
    ASSERT(user_tag < kUwordMax);
    set_user_tag(user_tag);
    current_tag_ = tag.ptr();
  }
  
  void Isolate::set_default_tag(const UserTag& tag) {
    default_tag_ = tag.ptr();
  }
  
  void Isolate::set_ic_miss_code(const Code& code) {
    ic_miss_code_ = code.ptr();
  }
  
  ErrorPtr Isolate::StealStickyError() {
    NoSafepointScope no_safepoint;
    ErrorPtr return_value = sticky_error_;
    sticky_error_ = Error::null();
    return return_value;
  }
  
  #if !defined(PRODUCT)
  void Isolate::set_pending_service_extension_calls(
      const GrowableObjectArray& value) {
    pending_service_extension_calls_ = value.ptr();
  }
  
  void Isolate::set_registered_service_extension_handlers(
      const GrowableObjectArray& value) {
    registered_service_extension_handlers_ = value.ptr();
  }
  #endif  // !defined(PRODUCT)
  
  #ifndef PRODUCT
  ErrorPtr Isolate::InvokePendingServiceExtensionCalls() {
    GrowableObjectArray& calls =
        GrowableObjectArray::Handle(GetAndClearPendingServiceExtensionCalls());
    if (calls.IsNull()) {
      return Error::null();
    }
    // Grab run function.
    const Library& developer_lib = Library::Handle(Library::DeveloperLibrary());
    ASSERT(!developer_lib.IsNull());
    const Function& run_extension = Function::Handle(
        developer_lib.LookupLocalFunction(Symbols::_runExtension()));
    ASSERT(!run_extension.IsNull());
  
    const Array& arguments =
        Array::Handle(Array::New(kPendingEntrySize + 1, Heap::kNew));
    Object& result = Object::Handle();
    String& method_name = String::Handle();
    Instance& closure = Instance::Handle();
    Array& parameter_keys = Array::Handle();
    Array& parameter_values = Array::Handle();
    Instance& reply_port = Instance::Handle();
    Instance& id = Instance::Handle();
    for (intptr_t i = 0; i < calls.Length(); i += kPendingEntrySize) {
      // Grab arguments for call.
      closure ^= calls.At(i + kPendingHandlerIndex);
      ASSERT(!closure.IsNull());
      arguments.SetAt(kPendingHandlerIndex, closure);
      method_name ^= calls.At(i + kPendingMethodNameIndex);
      ASSERT(!method_name.IsNull());
      arguments.SetAt(kPendingMethodNameIndex, method_name);
      parameter_keys ^= calls.At(i + kPendingKeysIndex);
      ASSERT(!parameter_keys.IsNull());
      arguments.SetAt(kPendingKeysIndex, parameter_keys);
      parameter_values ^= calls.At(i + kPendingValuesIndex);
      ASSERT(!parameter_values.IsNull());
      arguments.SetAt(kPendingValuesIndex, parameter_values);
      reply_port ^= calls.At(i + kPendingReplyPortIndex);
      ASSERT(!reply_port.IsNull());
      arguments.SetAt(kPendingReplyPortIndex, reply_port);
      id ^= calls.At(i + kPendingIdIndex);
      arguments.SetAt(kPendingIdIndex, id);
      arguments.SetAt(kPendingEntrySize, Bool::Get(FLAG_trace_service));
  
      if (FLAG_trace_service) {
        OS::PrintErr("[+%" Pd64 "ms] Isolate %s invoking _runExtension for %s\n",
                     Dart::UptimeMillis(), name(), method_name.ToCString());
      }
      result = DartEntry::InvokeFunction(run_extension, arguments);
      if (FLAG_trace_service) {
        OS::PrintErr("[+%" Pd64 "ms] Isolate %s _runExtension complete for %s\n",
                     Dart::UptimeMillis(), name(), method_name.ToCString());
      }
      // Propagate the error.
      if (result.IsError()) {
        // Remaining service extension calls are dropped.
        if (!result.IsUnwindError()) {
          // Send error back over the protocol.
          Service::PostError(method_name, parameter_keys, parameter_values,
                             reply_port, id, Error::Cast(result));
        }
        return Error::Cast(result).ptr();
      }
      // Drain the microtask queue.
      result = DartLibraryCalls::DrainMicrotaskQueue();
      // Propagate the error.
      if (result.IsError()) {
        // Remaining service extension calls are dropped.
        return Error::Cast(result).ptr();
      }
    }
    return Error::null();
  }
  
  GrowableObjectArrayPtr Isolate::GetAndClearPendingServiceExtensionCalls() {
    GrowableObjectArrayPtr r = pending_service_extension_calls_;
    pending_service_extension_calls_ = GrowableObjectArray::null();
    return r;
  }
  
  void Isolate::AppendServiceExtensionCall(const Instance& closure,
                                           const String& method_name,
                                           const Array& parameter_keys,
                                           const Array& parameter_values,
                                           const Instance& reply_port,
                                           const Instance& id) {
    if (FLAG_trace_service) {
      OS::PrintErr("[+%" Pd64
                   "ms] Isolate %s ENQUEUING request for extension %s\n",
                   Dart::UptimeMillis(), name(), method_name.ToCString());
    }
    GrowableObjectArray& calls =
        GrowableObjectArray::Handle(pending_service_extension_calls());
    bool schedule_drain = false;
    if (calls.IsNull()) {
      calls = GrowableObjectArray::New();
      ASSERT(!calls.IsNull());
      set_pending_service_extension_calls(calls);
      schedule_drain = true;
    }
    ASSERT(kPendingHandlerIndex == 0);
    calls.Add(closure);
    ASSERT(kPendingMethodNameIndex == 1);
    calls.Add(method_name);
    ASSERT(kPendingKeysIndex == 2);
    calls.Add(parameter_keys);
    ASSERT(kPendingValuesIndex == 3);
    calls.Add(parameter_values);
    ASSERT(kPendingReplyPortIndex == 4);
    calls.Add(reply_port);
    ASSERT(kPendingIdIndex == 5);
    calls.Add(id);
  
    if (schedule_drain) {
      const Array& msg = Array::Handle(Array::New(3));
      Object& element = Object::Handle();
      element = Smi::New(Message::kIsolateLibOOBMsg);
      msg.SetAt(0, element);
      element = Smi::New(Isolate::kDrainServiceExtensionsMsg);
      msg.SetAt(1, element);
      element = Smi::New(Isolate::kBeforeNextEventAction);
      msg.SetAt(2, element);
      std::unique_ptr<Message> message = WriteMessage(
          /* can_send_any_object */ false, /* same_group */ false, msg,
          main_port(), Message::kOOBPriority);
      bool posted = PortMap::PostMessage(std::move(message));
      ASSERT(posted);
    }
  }
  
  // This function is written in C++ and not Dart because we must do this
  // operation atomically in the face of random OOB messages. Do not port
  // to Dart code unless you can ensure that the operations will can be
  // done atomically.
  void Isolate::RegisterServiceExtensionHandler(const String& name,
                                                const Instance& closure) {
    // Don't allow for service extensions to be registered for internal isolates.
    if (Isolate::IsVMInternalIsolate(this)) {
      return;
    }
    GrowableObjectArray& handlers =
        GrowableObjectArray::Handle(registered_service_extension_handlers());
    if (handlers.IsNull()) {
      handlers = GrowableObjectArray::New(Heap::kOld);
      set_registered_service_extension_handlers(handlers);
    }
  #if defined(DEBUG)
    {
      // Sanity check.
      const Instance& existing_handler =
          Instance::Handle(LookupServiceExtensionHandler(name));
      ASSERT(existing_handler.IsNull());
    }
  #endif
    ASSERT(kRegisteredNameIndex == 0);
    handlers.Add(name, Heap::kOld);
    ASSERT(kRegisteredHandlerIndex == 1);
    handlers.Add(closure, Heap::kOld);
    {
      // Fire off an event.
      ServiceEvent event(this, ServiceEvent::kServiceExtensionAdded);
      event.set_extension_rpc(&name);
      Service::HandleEvent(&event);
    }
  }
  
  // This function is written in C++ and not Dart because we must do this
  // operation atomically in the face of random OOB messages. Do not port
  // to Dart code unless you can ensure that the operations will can be
  // done atomically.
  InstancePtr Isolate::LookupServiceExtensionHandler(const String& name) {
    const GrowableObjectArray& handlers =
        GrowableObjectArray::Handle(registered_service_extension_handlers());
    if (handlers.IsNull()) {
      return Instance::null();
    }
    String& handler_name = String::Handle();
    for (intptr_t i = 0; i < handlers.Length(); i += kRegisteredEntrySize) {
      handler_name ^= handlers.At(i + kRegisteredNameIndex);
      ASSERT(!handler_name.IsNull());
      if (handler_name.Equals(name)) {
        return Instance::RawCast(handlers.At(i + kRegisteredHandlerIndex));
      }
    }
    return Instance::null();
  }
  
  void Isolate::WakePauseEventHandler(Dart_Isolate isolate) {
    Isolate* iso = reinterpret_cast<Isolate*>(isolate);
    MonitorLocker ml(iso->pause_loop_monitor_);
    ml.Notify();
  }
  
  void Isolate::PauseEventHandler() {
    // We are stealing a pause event (like a breakpoint) from the
    // embedder.  We don't know what kind of thread we are on -- it
    // could be from our thread pool or it could be a thread from the
    // embedder.  Sit on the current thread handling service events
    // until we are told to resume.
    if (pause_loop_monitor_ == nullptr) {
      pause_loop_monitor_ = new Monitor();
    }
    Dart_EnterScope();
    MonitorLocker ml(pause_loop_monitor_, false);
  
    Dart_MessageNotifyCallback saved_notify_callback = message_notify_callback();
    set_message_notify_callback(Isolate::WakePauseEventHandler);
  
  #if !defined(DART_PRECOMPILED_RUNTIME)
    const bool had_program_reload_context =
        group()->program_reload_context() != nullptr;
    const int64_t start_time_micros = !had_program_reload_context
                                          ? 0
                                          : group()
                                                ->program_reload_context()
                                                ->group_reload_context()
                                                ->start_time_micros();
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
    bool resume = false;
    bool handle_non_service_messages = false;
    while (true) {
      // Handle all available vm service messages, up to a resume
      // request.
      while (!resume && Dart_HasServiceMessages()) {
        ml.Exit();
        resume = Dart_HandleServiceMessages();
        ml.Enter();
      }
      if (resume) {
        break;
      } else {
        handle_non_service_messages = true;
      }
  
  #if !defined(DART_PRECOMPILED_RUNTIME)
      if (had_program_reload_context &&
          (group()->program_reload_context() == nullptr)) {
        if (FLAG_trace_reload) {
          const int64_t reload_time_micros =
              OS::GetCurrentMonotonicMicros() - start_time_micros;
          double reload_millis = MicrosecondsToMilliseconds(reload_time_micros);
          OS::PrintErr("Reloading has finished! (%.2f ms)\n", reload_millis);
        }
        break;
      }
  #endif  // !defined(DART_PRECOMPILED_RUNTIME)
  
      // Wait for more service messages.
      Monitor::WaitResult res = ml.Wait();
      ASSERT(res == Monitor::kNotified);
    }
    // If any non-service messages came in, we need to notify the registered
    // message notify callback to check for unhandled messages. Otherwise, events
    // may be left unhandled until the next event comes in. See
    // https://github.com/dart-lang/sdk/issues/37312.
    if ((saved_notify_callback != nullptr) && handle_non_service_messages) {
      saved_notify_callback(Api::CastIsolate(this));
    }
    set_message_notify_callback(saved_notify_callback);
    Dart_ExitScope();
  }
  #endif  // !PRODUCT
  
  void Isolate::VisitIsolates(IsolateVisitor* visitor) {
    if (visitor == nullptr) {
      return;
    }
    IsolateGroup::ForEach([&](IsolateGroup* group) {
      group->ForEachIsolate(
          [&](Isolate* isolate) { visitor->VisitIsolate(isolate); });
    });
  }
  
  intptr_t Isolate::IsolateListLength() {
    intptr_t count = 0;
    IsolateGroup::ForEach([&](IsolateGroup* group) {
      group->ForEachIsolate([&](Isolate* isolate) { count++; });
    });
    return count;
  }
  
  Isolate* Isolate::LookupIsolateByPort(Dart_Port port) {
    Isolate* match = nullptr;
    IsolateGroup::ForEach([&](IsolateGroup* group) {
      group->ForEachIsolate([&](Isolate* isolate) {
        if (isolate->main_port() == port) {
          match = isolate;
        }
      });
    });
    return match;
  }
  
  std::unique_ptr<char[]> Isolate::LookupIsolateNameByPort(Dart_Port port) {
    MonitorLocker ml(isolate_creation_monitor_);
    std::unique_ptr<char[]> result;
    IsolateGroup::ForEach([&](IsolateGroup* group) {
      group->ForEachIsolate([&](Isolate* isolate) {
        if (isolate->main_port() == port) {
          const size_t len = strlen(isolate->name()) + 1;
          result = std::unique_ptr<char[]>(new char[len]);
          strncpy(result.get(), isolate->name(), len);
        }
      });
    });
    return result;
  }
  
  bool Isolate::TryMarkIsolateReady(Isolate* isolate) {
    MonitorLocker ml(isolate_creation_monitor_);
    if (!creation_enabled_) {
      return false;
    }
    isolate->accepts_messages_ = true;
    return true;
  }
  
  void Isolate::UnMarkIsolateReady(Isolate* isolate) {
    MonitorLocker ml(isolate_creation_monitor_);
    isolate->accepts_messages_ = false;
  }
  
  void Isolate::DisableIsolateCreation() {
    MonitorLocker ml(isolate_creation_monitor_);
    creation_enabled_ = false;
  }
  
  void Isolate::EnableIsolateCreation() {
    MonitorLocker ml(isolate_creation_monitor_);
    creation_enabled_ = true;
  }
  
  bool Isolate::IsolateCreationEnabled() {
    MonitorLocker ml(isolate_creation_monitor_);
    return creation_enabled_;
  }
  
  bool IsolateGroup::IsSystemIsolateGroup(const IsolateGroup* group) {
    return group->source()->flags.is_system_isolate;
  }
  
  bool Isolate::IsVMInternalIsolate(const Isolate* isolate) {
    return isolate->is_kernel_isolate() || isolate->is_service_isolate() ||
           (Dart::vm_isolate() == isolate);
  }
  
  void Isolate::KillLocked(LibMsgId msg_id) {
    Dart_CObject kill_msg;
    Dart_CObject* list_values[4];
    kill_msg.type = Dart_CObject_kArray;
    kill_msg.value.as_array.length = 4;
    kill_msg.value.as_array.values = list_values;
  
    Dart_CObject oob;
    oob.type = Dart_CObject_kInt32;
    oob.value.as_int32 = Message::kIsolateLibOOBMsg;
    list_values[0] = &oob;
  
    Dart_CObject msg_type;
    msg_type.type = Dart_CObject_kInt32;
    msg_type.value.as_int32 = msg_id;
    list_values[1] = &msg_type;
  
    Dart_CObject cap;
    cap.type = Dart_CObject_kCapability;
    cap.value.as_capability.id = terminate_capability();
    list_values[2] = &cap;
  
    Dart_CObject imm;
    imm.type = Dart_CObject_kInt32;
    imm.value.as_int32 = Isolate::kImmediateAction;
    list_values[3] = &imm;
  
    {
      AllocOnlyStackZone zone;
      std::unique_ptr<Message> message = WriteApiMessage(
          zone.GetZone(), &kill_msg, main_port(), Message::kOOBPriority);
      ASSERT(message != nullptr);
  
      // Post the message at the given port.
      bool success = PortMap::PostMessage(std::move(message));
      ASSERT(success);
    }
  }
  
  class IsolateKillerVisitor : public IsolateVisitor {
   public:
    explicit IsolateKillerVisitor(Isolate::LibMsgId msg_id)
        : target_(nullptr), msg_id_(msg_id) {}
  
    IsolateKillerVisitor(Isolate* isolate, Isolate::LibMsgId msg_id)
        : target_(isolate), msg_id_(msg_id) {
      ASSERT(isolate != Dart::vm_isolate());
    }
  
    virtual ~IsolateKillerVisitor() {}
  
    void VisitIsolate(Isolate* isolate) {
      MonitorLocker ml(Isolate::isolate_creation_monitor_);
      ASSERT(isolate != nullptr);
      if (ShouldKill(isolate)) {
        if (isolate->AcceptsMessagesLocked()) {
          isolate->KillLocked(msg_id_);
        }
      }
    }
  
   private:
    bool ShouldKill(Isolate* isolate) {
      // If a target_ is specified, then only kill the target_.
      // Otherwise, don't kill the service isolate or vm isolate.
      return (((target_ != nullptr) && (isolate == target_)) ||
              ((target_ == nullptr) && !IsSystemIsolate(isolate)));
    }
  
    Isolate* target_;
    Isolate::LibMsgId msg_id_;
  };
  
  void Isolate::KillAllIsolates(LibMsgId msg_id) {
    IsolateKillerVisitor visitor(msg_id);
    VisitIsolates(&visitor);
  }
  
  void Isolate::KillIfExists(Isolate* isolate, LibMsgId msg_id) {
    IsolateKillerVisitor visitor(isolate, msg_id);
    VisitIsolates(&visitor);
  }
  
  void Isolate::IncrementSpawnCount() {
    MonitorLocker ml(&spawn_count_monitor_);
    spawn_count_++;
  }
  
  void Isolate::DecrementSpawnCount() {
    MonitorLocker ml(&spawn_count_monitor_);
    ASSERT(spawn_count_ > 0);
    spawn_count_--;
    ml.Notify();
  }
  
  void Isolate::WaitForOutstandingSpawns() {
    Thread* thread = Thread::Current();
    ASSERT(thread != NULL);
    MonitorLocker ml(&spawn_count_monitor_);
    while (spawn_count_ > 0) {
      ml.WaitWithSafepointCheck(thread);
    }
  }
  
  Monitor* IsolateGroup::threads_lock() const {
    return thread_registry_->threads_lock();
  }
  
  Thread* Isolate::ScheduleThread(bool is_mutator,
                                  bool is_nested_reenter,
                                  bool bypass_safepoint) {
    if (is_mutator) {
      group()->IncreaseMutatorCount(this, is_nested_reenter);
    }
  
    // We are about to associate the thread with an isolate group and it would
    // not be possible to correctly track no_safepoint_scope_depth for the
    // thread in the constructor/destructor of MonitorLocker,
    // so we create a MonitorLocker object which does not do any
    // no_safepoint_scope_depth increments/decrements.
    MonitorLocker ml(group()->threads_lock(), false);
  
    // Check to make sure we don't already have a mutator thread.
    if (is_mutator && scheduled_mutator_thread_ != nullptr) {
      return nullptr;
    }
  
    // NOTE: We cannot just use `Dart::vm_isolate() == this` here, since during
    // VM startup it might not have been set at this point.
    const bool is_vm_isolate =
        Dart::vm_isolate() == nullptr || Dart::vm_isolate() == this;
  
    // We lazily create a [Thread] structure for the mutator thread, but we'll
    // reuse it until the death of the isolate.
    Thread* existing_mutator_thread = is_mutator ? mutator_thread_ : nullptr;
    if (existing_mutator_thread != nullptr) {
      ASSERT(existing_mutator_thread->is_mutator_thread_);
    }
  
    // Schedule the thread into the isolate by associating a 'Thread' structure
    // with it (this is done while we are holding the thread registry lock).
    Thread* thread =
        group()->ScheduleThreadLocked(&ml, existing_mutator_thread, is_vm_isolate,
                                      is_mutator, bypass_safepoint);
    if (is_mutator) {
      ASSERT(mutator_thread_ == nullptr || mutator_thread_ == thread);
      mutator_thread_ = thread;
      scheduled_mutator_thread_ = thread;
      thread->is_mutator_thread_ = true;
    }
    thread->isolate_ = this;
    thread->field_table_values_ = field_table_->table();
  
    return thread;
  }
  
  void Isolate::UnscheduleThread(Thread* thread,
                                 bool is_mutator,
                                 bool is_nested_exit,
                                 bool bypass_safepoint) {
    {
      // Disassociate the 'Thread' structure and unschedule the thread
      // from this isolate.
      // We are disassociating the thread from an isolate and it would
      // not be possible to correctly track no_safepoint_scope_depth for the
      // thread in the constructor/destructor of MonitorLocker,
      // so we create a MonitorLocker object which does not do any
      // no_safepoint_scope_depth increments/decrements.
      MonitorLocker ml(group()->threads_lock(), false);
  
      if (is_mutator) {
        if (thread->sticky_error() != Error::null()) {
          ASSERT(sticky_error_ == Error::null());
          sticky_error_ = thread->StealStickyError();
        }
        ASSERT(mutator_thread_ == thread);
        ASSERT(mutator_thread_ == scheduled_mutator_thread_);
        scheduled_mutator_thread_ = nullptr;
      } else {
        // We only reset the isolate pointer for non-mutator threads, since
        // mutator threads can still be visited during GC even if unscheduled.
        // See also IsolateGroup::UnscheduleThreadLocked`
        thread->isolate_ = nullptr;
      }
      thread->field_table_values_ = nullptr;
      group()->UnscheduleThreadLocked(&ml, thread, is_mutator, bypass_safepoint);
    }
    if (is_mutator) {
      group()->DecreaseMutatorCount(this, is_nested_exit);
    }
  }
  
  }  // namespace dart
  