// 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/growable_array.h"
#include "platform/text_buffer.h"
#include "vm/canonical_tables.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/dispatch_table.h"
#include "vm/ffi_callback_metadata.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/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);
DECLARE_FLAG(int, old_gen_growth_time_ratio);

// 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_old_gen_growth_time_ratio = 0;   // Timing dependent.
    FLAG_random_seed = 0x44617274;        // "Dart"
  }
}

DEFINE_FLAG_HANDLER(DeterministicModeHandler,
                    deterministic,
                    "Enable deterministic mode.");

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())

static std::unique_ptr<Message> SerializeMessage(Dart_Port dest_port,
                                                 const Instance& obj) {
  return WriteMessage(/* 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;
}

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_duration_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(), isolate_group_(isolate_group) {}

  void VisitHandle(uword addr) override {
    auto handle = reinterpret_cast<FinalizablePersistentHandle*>(addr);
    handle->UpdateUnreachable(isolate_group_);
  }

 private:
  IsolateGroup* isolate_group_;

  DISALLOW_COPY_AND_ASSIGN(FinalizeWeakPersistentHandlesVisitor);
};

void MutatorThreadPool::OnEnterIdleLocked(MutexLocker* ml, Worker* worker) {
  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)) {
    MutexUnlocker mls(ml);
    NotifyIdle();
    return;
  }

  // Avoid shutdown having to wait for the timeout to expire.
  if (ShuttingDownLocked()) return;

  // Wait for the recommended idle timeout.
  // We can be woken up because of a), b) or c)
  const auto result =
      worker->Sleep(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)) {
    MutexUnlocker 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)
    : class_table_(nullptr),
      cached_class_table_table_(nullptr),
      object_store_(object_store),
      class_table_allocator_(),
      embedder_data_(embedder_data),
      thread_pool_(),
      isolates_lock_(new SafepointRwLock()),
      isolates_(),
      mutators_(),
      start_time_micros_(OS::GetCurrentMonotonicMicros()),
      is_system_isolate_group_(source->flags.is_system_isolate),
#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)),
      store_buffer_(new StoreBuffer()),
      heap_(nullptr),
      initial_field_table_(new FieldTable(/*isolate=*/nullptr)),
      sentinel_field_table_(new FieldTable(/*isolate=*/nullptr)),
      shared_initial_field_table_(new FieldTable(/*isolate=*/nullptr,
                                                 /*isolate_group=*/nullptr)),
      shared_field_table_(new FieldTable(/*isolate=*/nullptr, this)),
      isolate_group_flags_(),
#if !defined(DART_PRECOMPILED_RUNTIME)
      background_compiler_(new BackgroundCompiler(this)),
#endif
      callback_metadata_(new FfiCallbackMetadata()),
      symbols_mutex_(),
      type_canonicalization_mutex_(),
      type_arguments_canonicalization_mutex_(),
      subtype_test_cache_mutex_(),
      megamorphic_table_mutex_(),
      type_feedback_mutex_(),
      patchable_call_mutex_(),
      constant_canonicalization_mutex_(),
      kernel_data_lib_cache_mutex_(),
      kernel_data_class_cache_mutex_(),
      kernel_constants_mutex_(),
      shared_field_initializer_rwlock_(),
      program_lock_(new SafepointRwLock(SafepointLevel::kGCAndDeopt)),
      active_mutators_monitor_(new Monitor()),
      max_active_mutators_(Scavenger::MaxMutatorThreadCount()),
#if !defined(PRODUCT)
      debugger_(new GroupDebugger(this)),
#endif
      cache_mutex_(),
      handler_info_cache_(),
      catch_entry_moves_cache_(),
      tag_table_lock_(),
      roots_(new Roots()) {
  FlagsCopyFrom(api_flags);
  intptr_t max_worker_threads;
  if (FLAG_disable_thread_pool_limit) {
    max_worker_threads = 0;
  } else {
    // There needs to be at least one more thread than active mutators slots
    // so that there is a thread waiting in IncreaseMutatorCount (instead of
    // unscheduled task sitting in the thread pool's queue) to eventually
    // timeout and trigger StealActiveMutators.
    max_worker_threads = Scavenger::MaxMutatorThreadCount() + 2;
  }
  thread_pool_.reset(new MutatorThreadPool(this, max_worker_threads));
  {
    WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
    // Keep isolate IDs less than 2^53 so web clients of the service
    // protocol can process it properly.
    //
    // See https://github.com/dart-lang/sdk/issues/53081.
    id_ = isolate_group_random_->NextJSInt();
  }
  heap_walk_class_table_ = class_table_ =
      new ClassTable(&class_table_allocator_);
  cached_class_table_table_.store(class_table_->table());
  memset(&native_assets_api_, 0, sizeof(NativeAssetsApi));

  Roots::SetCurrent(roots());
}

IsolateGroup::IsolateGroup(std::shared_ptr<IsolateGroupSource> source,
                           void* embedder_data,
                           Dart_IsolateFlags api_flags)
    : IsolateGroup(source, embedder_data, nullptr, api_flags) {}

IsolateGroup::~IsolateGroup() {
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
  RELEASE_ASSERT(group_reload_context_ == nullptr);
  RELEASE_ASSERT(program_reload_context_ == nullptr);
#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)

  // Ensure we destroy the heap before the other members.
  heap_ = nullptr;
  ASSERT(old_marking_stack_ == nullptr);
  ASSERT(new_marking_stack_ == nullptr);
  ASSERT(deferred_marking_stack_ == nullptr);

  if (obfuscation_map_ != nullptr) {
    for (intptr_t i = 0; obfuscation_map_[i] != nullptr; i++) {
      delete[] obfuscation_map_[i];
    }
    delete[] obfuscation_map_;
  }

  class_table_allocator_.Free(class_table_);
  if (heap_walk_class_table_ != class_table_) {
    class_table_allocator_.Free(heap_walk_class_table_);
  }

#if !defined(PRODUCT)
  delete debugger_;
  debugger_ = nullptr;
#endif
}

void IsolateGroup::RegisterIsolate(Isolate* isolate) {
  SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
  ASSERT(isolates_lock_->IsCurrentThreadWriter());
  if (isolates_.IsEmpty()) {
    interrupt_port_ = isolate->main_port();
  }
  isolates_.Append(isolate);
  isolate_count_++;
}

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) &&
         group_mutator_count_ == 0;
}

void IsolateGroup::UnregisterIsolate(Isolate* isolate) {
  SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
  isolates_.Remove(isolate);
  if (isolates_.IsEmpty()) {
    interrupt_port_ = ILLEGAL_PORT;
  } else {
    interrupt_port_ = isolates_.First()->main_port();
  }
}

bool IsolateGroup::UnregisterIsolateDecrementCount() {
  SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
  isolate_count_--;
  return isolate_count_ == 0;
}

void IsolateGroup::IncrementIsolateGroupMutatorCount() {
  SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
  group_mutator_count_++;
}

void IsolateGroup::DecrementIsolateGroupMutatorCount() {
  SafepointWriteRwLocker ml(Thread::Current(), isolates_lock_.get());
  group_mutator_count_--;
}

void IsolateGroup::RegisterIsolateGroupMutator(Thread* mutator) {
  mutators_.Append(mutator);
}

void IsolateGroup::UnregisterIsolateGroupMutator(Thread* mutator) {
  mutators_.Remove(mutator);
}

void IsolateGroup::CreateHeap(bool is_service_or_kernel_isolate) {
  Heap::Init(this, FLAG_new_gen_semi_max_size * MBInWords,
             (is_service_or_kernel_isolate ? kDefaultMaxOldGenHeapSize
                                           : FLAG_old_gen_heap_size) *
                 MBInWords);

#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() {
  Roots::SetCurrent(roots());

  char* name = nullptr;
  // 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).
  ASSERT(thread_pool_ != nullptr);
  thread_pool_->Shutdown();
  thread_pool_.reset();

  {
    MonitorLocker ml(Isolate::isolate_creation_monitor_);
    Isolate::pending_shutdowns_++;
  }

  // Needs to happen before starting to destroy the heap so helper tasks like
  // the SampleBlockProcessor don't try to enter the group during this
  // tear-down.
  UnregisterIsolateGroup(this);

  // 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 registry are
    // still valid.
    old_space->AbandonMarkingForShutdown();
  }

  // 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;
  Roots::ClearCurrent();

  // 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_);
    Isolate::pending_shutdowns_--;
    if (Isolate::pending_shutdowns_ == 0) {
      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);
}

static constexpr intptr_t kActiveMutatorPreemptionTimeout = 120;

void IsolateGroup::IncreaseMutatorCount(Thread* thread,
                                        bool is_nested_reenter,
                                        bool was_stolen) {
  // If the mutator was temporarily blocked on a worker thread, we have to
  // unblock the worker thread again.
  if (is_nested_reenter || was_stolen) {
    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 monitors in the uncommon case.
    MonitorLocker ml(active_mutators_monitor_.get());
    ASSERT(active_mutators_ <= max_active_mutators_);
    while (active_mutators_ == max_active_mutators_) {
      waiting_mutators_++;
      bool timed_out = false;
      if (has_timeout_waiter_) {
        if (was_stolen) {
          ml.WaitWithSafepointCheck(thread);
        } else {
          ml.Wait();
        }
      } else {
        has_timeout_waiter_ = true;
        if (was_stolen) {
          timed_out = ml.WaitWithSafepointCheck(
                          thread, kActiveMutatorPreemptionTimeout) ==
                      Monitor::kTimedOut;
        } else {
          timed_out =
              ml.Wait(kActiveMutatorPreemptionTimeout) == Monitor::kTimedOut;
        }
        has_timeout_waiter_ = false;
      }
      waiting_mutators_--;

      if (timed_out) {
        active_mutators_ -=
            thread_registry()->StealActiveMutators(thread_pool());
        ASSERT(active_mutators_ >= 0);
      }
    }
    active_mutators_++;

    // StealActiveMutators may cause multiple slots to become available, but
    // does not do a NotifyAll to prevent the case of thousands of threads
    // waking up to claim a ~dozen slots, so we keep notifying while there are
    // both available slots and waiters.
    if ((waiting_mutators_ > 0) &&
        ((!has_timeout_waiter_) ||
         (active_mutators_ != max_active_mutators_))) {
      ml.Notify();
    }
  }
}

void IsolateGroup::DecreaseMutatorCount(bool is_nested_exit) {
  // 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).
  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 monitors in the uncommon case.
    MonitorLocker ml(active_mutators_monitor_.get());
    ASSERT(active_mutators_ <= max_active_mutators_);
    ASSERT(active_mutators_ > 0);
    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(
    Dart_Port 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::HasIsolateGroups() {
  ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
  for (auto group : *isolate_groups_) {
    USE(group);
    return true;
  }
  return false;
}

bool IsolateGroup::HasApplicationIsolateGroups() {
  ReadRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
  for (auto group : *isolate_groups_) {
    if (!IsolateGroup::IsSystemIsolateGroup(group)) {
      return true;
    }
  }
  return false;
}

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::RegisterSharedStaticField(const Field& field,
                                             const Object& initial_value) {
  const bool need_to_grow_backing_store =
      shared_initial_field_table()->Register(field);
  const intptr_t field_id = field.field_id();
  shared_initial_field_table()->SetAt(field_id, initial_value.ptr());

  if (need_to_grow_backing_store) {
    // We have to stop other isolates from accessing shared isolate group
    // field state, since we'll have to grow the backing store.
    GcSafepointOperationScope scope(Thread::Current());
    const bool need_to_grow_other_backing_store =
        shared_field_table()->Register(field, field_id);
    ASSERT(need_to_grow_other_backing_store);
  } else {
    const bool need_to_grow_other_backing_store =
        shared_field_table()->Register(field, field_id);
    ASSERT(!need_to_grow_other_backing_store);
  }
  shared_field_table()->SetAt(field_id, initial_value.ptr());
}

void IsolateGroup::RegisterStaticField(const Field& field,
                                       const Object& initial_value) {
  ASSERT(program_lock()->IsCurrentThreadWriter());

  ASSERT(field.is_static());
  if (field.is_shared()) {
    RegisterSharedStaticField(field, initial_value);
    return;
  }
  const bool need_to_grow_backing_store =
      initial_field_table()->Register(field);
  const intptr_t field_id = field.field_id();
  if (need_to_grow_backing_store) {
    sentinel_field_table()->AllocateIndex(field_id);
  }
  initial_field_table()->SetAt(field_id, initial_value.ptr());
  sentinel_field_table()->SetAt(field_id, Object::sentinel().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();
  if (field.is_shared()) {
    shared_field_table()->Free(field_id);
    shared_initial_field_table()->Free(field_id);
  } else {
    initial_field_table()->Free(field_id);
    sentinel_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);
      }
    });
  }
}

Isolate* IsolateGroup::EnterTemporaryIsolate() {
  Roots::SetCurrent(roots());
  Dart_IsolateFlags flags;
  Isolate::FlagsInitialize(&flags);
  Isolate* const isolate = Isolate::InitIsolate("temp", this, flags);
  RELEASE_ASSERT(isolate != nullptr);
  ASSERT(Isolate::Current() == isolate);
  return isolate;
}

void IsolateGroup::ExitTemporaryIsolate() {
  Thread* thread = Thread::Current();
  ASSERT(thread != nullptr);
  thread->set_execution_state(Thread::kThreadInVM);
  Dart::ShutdownIsolate(thread);
  Roots::ClearCurrent();
}

void IsolateGroup::RunWithCachedCatchEntryMoves(
    const Code& code,
    intptr_t pc,
    std::function<void(const CatchEntryMoves&)> action) {
  SafepointMutexLocker ml(&cache_mutex_);
  const CatchEntryMovesRefPtr* ref = catch_entry_moves_cache_.Lookup(pc);
  if (ref != nullptr) {
    action(ref->moves());
  } else {
    const intptr_t pc_offset = pc - code.PayloadStart();
    const auto& td = TypedData::Handle(code.catch_entry_moves_maps());

    CatchEntryMovesMapReader reader(td);
    const CatchEntryMoves* moves = reader.ReadMovesForPcOffset(pc_offset);
    catch_entry_moves_cache_.Insert(pc, CatchEntryMovesRefPtr(moves));
    action(*moves);
  }
}

void IsolateGroup::ClearCatchEntryMovesCacheLocked() {
  auto thread = Thread::Current();
  ASSERT(thread->OwnsSafepoint() ||
         (thread->task_kind() == Thread::kMutatorTask) ||
         (thread->task_kind() == Thread::kMarkerTask) ||
         (thread->task_kind() == Thread::kCompactorTask) ||
         (thread->task_kind() == Thread::kScavengerTask) ||
         (thread->task_kind() == Thread::kIncrementalCompactorTask));
  catch_entry_moves_cache_.Clear();
}

void IsolateGroup::RehashConstants(Become* become) {
  // Even though no individual constant contains a cycle, there can be "cycles"
  // between the canonical tables if some const instances of A have fields that
  // are const instance of B and vice versa. So set all the old tables to the
  // side and clear all the tables attached to the classes before rehashing
  // instead of resetting and rehash one class at a time.

  Thread* thread = Thread::Current();
  StackZone stack_zone(thread);
  Zone* zone = stack_zone.GetZone();

  intptr_t num_cids = class_table()->NumCids();
  Array** old_constant_tables = zone->Alloc<Array*>(num_cids);
  for (intptr_t i = 0; i < num_cids; i++) {
    old_constant_tables[i] = nullptr;
  }

  Class& cls = Class::Handle(zone);
  for (intptr_t cid = kInstanceCid; cid < num_cids; 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;
    }
    if ((cid == kMintCid) || (cid == kDoubleCid)) {
      // Constants stored as a plain list or in a hashset with a stable
      // hashcode, which only depends on the actual value of the constant.
      continue;
    }

    cls = class_table()->At(cid);
    if (cls.constants() == Array::null()) continue;
    old_constant_tables[cid] = &Array::Handle(zone, cls.constants());
    cls.set_constants(Object::null_array());
  }

  // Clear invalid hashes.
  heap()->ResetCanonicalHashTable();

  Instance& constant = Instance::Handle(zone);
  Field& field = Field::Handle(zone);
  String& name = String::Handle(zone);
  Array& new_values = Array::Handle(zone);
  Instance& old_value = Instance::Handle(zone);
  Instance& new_value = Instance::Handle(zone);
  Instance& deleted = Instance::Handle(zone);

  if (become != nullptr) {
    for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) {
      Array* old_constants = old_constant_tables[cid];
      if (old_constants == nullptr) continue;

      cls = class_table()->At(cid);
      CanonicalInstancesSet set(zone, old_constants->ptr());
      CanonicalInstancesSet::Iterator it(&set);
      while (it.MoveNext()) {
        constant ^= set.GetKey(it.Current());
        ASSERT(!constant.IsNull());
        constant.ClearCanonical();
      }
      set.Release();
    }
  }

  for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) {
    Array* old_constants = old_constant_tables[cid];
    if (old_constants == nullptr) continue;

    cls = class_table()->At(cid);
    CanonicalInstancesSet set(zone, old_constants->ptr());
    CanonicalInstancesSet::Iterator it(&set);

    if (cls.is_enum_class() && (become != nullptr)) {
      field = cls.LookupStaticField(Symbols::_DeletedEnumSentinel());
      deleted ^= field.StaticConstFieldValue();
      if (deleted.IsNull()) {
        deleted = Instance::New(cls, Heap::kOld);
        field = object_store()->enum_name_field();
        name = cls.ScrubbedName();
        name = Symbols::FromConcat(thread, Symbols::_DeletedEnumPrefix(), name);
        deleted.SetField(field, name);
        field = object_store()->enum_index_field();
        new_value = Smi::New(-1);
        deleted.SetField(field, new_value);
        field = cls.LookupStaticField(Symbols::_DeletedEnumSentinel());
        // The static const field contains `Object::null()` instead of
        // `Object::sentinel()` - so it's not considered an initializing store.
        field.SetStaticConstFieldValue(deleted,
                                       /*assert_initializing_store*/ false);
      }

      field = cls.LookupField(Symbols::Values());
      new_values ^= field.StaticConstFieldValue();

      field = object_store()->enum_name_field();
      while (it.MoveNext()) {
        old_value ^= set.GetKey(it.Current());
        ASSERT(old_value.GetClassId() == cid);
        bool found = false;
        for (intptr_t j = 0; j < new_values.Length(); j++) {
          new_value ^= new_values.At(j);
          ASSERT(new_value.GetClassId() == cid);
          if (old_value.GetField(field) == new_value.GetField(field)) {
            found = true;
            break;
          }
        }
        if (!found) {
          new_value = deleted.ptr();
        }

        if (old_value.ptr() != new_value.ptr()) {
          become->Add(old_value, new_value);
        }
        if (new_value.IsCanonical()) {
          cls.InsertCanonicalConstant(zone, new_value);
        }
      }
    } else {
      while (it.MoveNext()) {
        old_value ^= set.GetKey(it.Current());
        ASSERT(!old_value.IsNull());

        if (become == nullptr) {
          if (old_value.IsCanonical()) {
            cls.InsertCanonicalConstant(zone, old_value);
          } else {
            // The deleted enum value sentinel is not marked canonical.
            ASSERT(cls.is_enum_class());
          }
        } else {
          new_value = old_value.Canonicalize(thread);
          if (old_value.ptr() != new_value.ptr()) {
            become->Add(old_value, new_value);
          }
        }
      }
    }
    set.Release();
  }

  // Save memory.
  heap()->ResetCanonicalHashTable();
}

void Isolate::SendInternalLibMessage(LibMsgId msg_id, uint64_t capability) {
  const bool ok = SendInternalLibMessage(main_port(), msg_id, capability);
  if (!ok) UNREACHABLE();
}

bool Isolate::SendInternalLibMessage(Dart_Port main_port,
                                     LibMsgId msg_id,
                                     uint64_t capability) {
  Dart_CObject array_entry_msg_kind;
  array_entry_msg_kind.type = Dart_CObject_kInt64;
  array_entry_msg_kind.value.as_int64 = Message::kIsolateLibOOBMsg;

  Dart_CObject array_entry_msg_id;
  array_entry_msg_id.type = Dart_CObject_kInt64;
  array_entry_msg_id.value.as_int64 = msg_id;

  Dart_CObject array_entry_capability;
  array_entry_capability.type = Dart_CObject_kCapability;
  array_entry_capability.value.as_capability.id = capability;

  Dart_CObject* array_entries[3] = {
      &array_entry_msg_kind,
      &array_entry_msg_id,
      &array_entry_capability,
  };

  Dart_CObject message;
  message.type = Dart_CObject_kArray;
  message.value.as_array.values = array_entries;
  message.value.as_array.length = ARRAY_SIZE(array_entries);

  AllocOnlyStackZone zone;
  std::unique_ptr<Message> msg = WriteApiMessage(
      zone.GetZone(), &message, main_port, Message::kOOBPriority);
  if (msg == nullptr) UNREACHABLE();

  return PortMap::PostMessage(std::move(msg));
}

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 override;
  void MessageNotify(Message::Priority priority) override;
  MessageStatus HandleMessage(std::unique_ptr<Message> message) override;
#ifndef PRODUCT
  void NotifyPauseOnStart() override;
  void NotifyPauseOnExit() override;
#endif  // !PRODUCT

#if defined(DEBUG)
  // Check that it is safe to access this handler.
  void CheckAccess() const override;
#endif

  Isolate* isolate() const override { return isolate_; }
  IsolateGroup* isolate_group() const { return isolate_->group(); }

  bool KeepAliveLocked() override {
    // Otherwise we only stay alive as long as there's active receive ports, or
    // there are FFI callbacks keeping the isolate alive.
    return isolate_->HasLivePorts() || isolate_->HasOpenNativeCallables();
  }

 private:
  ErrorPtr HandleLibMessage(const Array& message);

  MessageStatus ProcessUnhandledException(const Error& result);

  void set_is_scheduled() override {
    ASSERT(isolate_ != nullptr);
    isolate_->set_is_not_acquirable();
  }
  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) {
        obj = message.At(2);
        if (I->VerifyTerminateCapability(obj)) {
          Thread::Current()->StartUnwindError();
          // 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() == nullptr) {
        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)
      {
        ReloadParticipationScope allow_reload(T);
        T->CheckForSafepoint();
      }
#else
      UNREACHABLE();
#endif
      break;
    }
#if defined(DEBUG)
    // Malformed OOB messages are silently ignored in release builds.
    default:
      FATAL("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) {
#ifdef DEBUG
  CheckAccess();
#endif
  Thread* thread = Thread::Current();
  StackZone stack_zone(thread);
  Zone* zone = stack_zone.GetZone();
#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 (msg.IsSmi()) {
      uword interrupt_bits = Smi::Cast(msg).Value();
      const Error& error =
          Error::Handle(thread->HandleInterrupts(interrupt_bits));
      if (!error.IsNull()) {
        status = ProcessUnhandledException(error);
      }
    }
  } 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 {
    Object& msg_handler = Object::Handle(
        zone, DartLibraryCalls::HandleMessage(message->dest_port(), msg));
    while (msg_handler.IsError()) {
      status = ProcessUnhandledException(Error::Cast(msg_handler));
      if (status == kOK) {
        msg_handler = DartLibraryCalls::DrainMicrotaskQueue();
      } else {
        break;
      }
    }
    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);
    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);
    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() const {
  ASSERT(isolate() == Isolate::Current());
}
#endif

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.IsUnwindError()) {
        return StoreError(T, UnwindError::Cast(exception_str));
      } else if (!exception_str.IsString()) {
        exception_cstr = exception.ToCString();
      } else {
        exception_cstr = exception_str.ToCString();
      }
    }

    const Instance& stacktrace = Instance::Handle(zone, uhe.stacktrace());
    const Object& stacktrace_str =
        Object::Handle(zone, DartLibraryCalls::ToString(stacktrace));
    if (stacktrace_str.IsUnwindError()) {
      return StoreError(T, UnwindError::Cast(stacktrace_str));
    } else if (!stacktrace_str.IsString()) {
      stacktrace_cstr = stacktrace.ToCString();
    } else {
      stacktrace_cstr = stacktrace_str.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->is_service_isolate = false;
  api_flags->is_kernel_isolate = false;
  api_flags->null_safety = true;
}

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->is_service_isolate = false;
  api_flags->is_kernel_isolate = false;
  api_flags->null_safety = true;
}

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)
#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->is_service_isolate = false;
  api_flags->is_kernel_isolate = false;
  api_flags->null_safety = true;
}

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->is_service_isolate = false;
  api_flags->is_kernel_isolate = false;
  api_flags->null_safety = true;
}

void Isolate::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_flags_ = bitname##Bit::update(                       \
                      api_flags.isolate_flag, isolate_flags_));

  BOOL_ISOLATE_FLAG_LIST(SET_FROM_FLAG)
#undef FLAG_FOR_NONPRODUCT
#undef FLAG_FOR_PRECOMPILER
#undef FLAG_FOR_PRODUCT
#undef SET_FROM_FLAG
}

#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)
    : field_table_(new FieldTable(/*isolate=*/this)),
      finalizers_(GrowableObjectArray::null()),
      isolate_group_(isolate_group),
      isolate_object_store_(new IsolateObjectStore()),
      isolate_flags_(),
#if !defined(PRODUCT)
      last_resume_timestamp_(OS::GetCurrentTimeMillis()),
      vm_tag_counters_(),
      pending_service_extension_calls_(GrowableObjectArray::null()),
      registered_service_extension_handlers_(GrowableObjectArray::null()),
      service_id_zones_(nullptr),
      next_service_id_zone_id_(1),
#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()),
      mutex_(),
      owner_thread_(OSThread::kInvalidThreadId),
      sticky_error_(Error::null()),
      spawn_count_monitor_(),
      wake_pause_event_handler_count_(0),
      loaded_prefixes_set_storage_(nullptr) {
  FlagsCopyFrom(api_flags);
  SetErrorsFatal(true);
}

#undef REUSABLE_HANDLE_SCOPE_INIT
#undef REUSABLE_HANDLE_INITIALIZERS

Isolate::~Isolate() {
#if !defined(PRODUCT)
  delete debugger_;
  debugger_ = nullptr;
  if (service_id_zones_ != nullptr) {
    auto it = service_id_zones_->GetIterator();
    while (auto* pair = it.Next()) {
      delete pair->value;
    }
    delete service_id_zones_;
    service_id_zones_ = nullptr;
  }
  delete pause_loop_monitor_;
  pause_loop_monitor_ = nullptr;
#endif  // !defined(PRODUCT)

  free(name_);
  delete field_table_;
#if defined(DART_INCLUDE_SIMULATOR)
  delete simulator_;
#endif
  delete message_handler_;
  message_handler_ =
      nullptr;  // Fail fast if we send messages to a dead isolate.
  ASSERT(spawn_count_ == 0);

  // The [Thread] object should've been released on the last
  // `Thread::ExitIsolate()` call.
  ASSERT(scheduled_mutator_thread_ == nullptr);
  ASSERT(mutator_thread_ == nullptr);
}

void Isolate::InitVM() {
  create_group_callback_ = nullptr;
  initialize_callback_ = nullptr;
  shutdown_callback_ = nullptr;
  cleanup_callback_ = nullptr;
  cleanup_group_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) {
  Isolate* result = new Isolate(isolate_group, api_flags);
  result->BuildName(name_prefix);
  if (!isolate_group->is_bootstrapping()) {
    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, nullptr,             \
                                            Metric::unit);
  ISOLATE_METRIC_LIST(ISOLATE_METRIC_INIT);
#undef ISOLATE_METRIC_INIT
#endif  // !defined(PRODUCT)

  // Setup the isolate message handler.
  result->message_handler_ = new IsolateMessageHandler(result);

  result->set_main_port(PortMap::CreatePort(result->message_handler()));

  // 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.
  Thread::EnterIsolate(result);

  // Keep capability IDs less than 2^53 so web clients of the service
  // protocol can process it properly.
  //
  // See https://github.com/dart-lang/sdk/issues/53081.
  result->set_pause_capability(Thread::Current()->random()->NextJSInt());
  result->set_terminate_capability(Thread::Current()->random()->NextJSInt());

#if !defined(PRODUCT)
  result->debugger_ = new Debugger(result);
#endif  // !defined(PRODUCT)

  // 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 (api_flags.is_service_isolate) {
    ASSERT(!ServiceIsolate::Exists());
    ServiceIsolate::SetServiceIsolate(result);
#if !defined(DART_PRECOMPILED_RUNTIME)
  } else if (api_flags.is_kernel_isolate) {
    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;
}

void Isolate::FixInitiallyNullFields() {
#if !defined(PRODUCT)
  pending_service_extension_calls_ = GrowableObjectArray::null();
  registered_service_extension_handlers_ = GrowableObjectArray::null();
#endif
  finalizers_ = GrowableObjectArray::null();
  sticky_error_ = Error::null();
  isolate_object_store_->Init();
}

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()->thread_registry()->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_;
}

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(!Thread::Current()->OwnsReloadSafepoint());

  // 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) {
  ASSERT(!IsReloading());

  // Ensure all isolates inside the isolate group are paused at a place where we
  // can safely do a reload.
  RELOAD_OPERATION_SCOPE(Thread::Current());

  auto class_table = IsolateGroup::Current()->class_table();
  std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
      new IsolateGroupReloadContext(this, 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) {
  ASSERT(!IsReloading());

  // Ensure all isolates inside the isolate group are paused at a place where we
  // can safely do a reload.
  RELOAD_OPERATION_SCOPE(Thread::Current());

  auto class_table = IsolateGroup::Current()->class_table();
  std::shared_ptr<IsolateGroupReloadContext> group_reload_context(
      new IsolateGroupReloadContext(this, 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() {
  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.
  const intptr_t kMaxResumeCapabilities =
      compiler::target::kSmiMax / (6 * kWordSize);

  const GrowableObjectArray& caps = GrowableObjectArray::Handle(
      isolate_object_store()->resume_capabilities());
  Capability& current = Capability::Handle();
  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(
      isolate_object_store()->resume_capabilities());
  Capability& current = Capability::Handle();
  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.
  const intptr_t kMaxListeners = compiler::target::kSmiMax / (12 * kWordSize);

  const GrowableObjectArray& listeners =
      GrowableObjectArray::Handle(isolate_object_store()->exit_listeners());
  SendPort& current = SendPort::Handle();
  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(isolate_object_store()->exit_listeners());
  SendPort& current = SendPort::Handle();
  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(isolate_object_store()->exit_listeners());
  if (listeners.IsNull()) return;

  SendPort& listener = SendPort::Handle();
  Instance& response = Instance::Handle();
  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.
  const intptr_t kMaxListeners = compiler::target::kSmiMax / (6 * kWordSize);

  const GrowableObjectArray& listeners =
      GrowableObjectArray::Handle(isolate_object_store()->error_listeners());
  SendPort& current = SendPort::Handle();
  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(isolate_object_store()->error_listeners());
  SendPort& current = SendPort::Handle();
  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(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 == nullptr) {
    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();
  bool was_somebody_notified = false;
  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(Thread::Current()->zone(), port_id, &arr));
      was_somebody_notified = true;
    }
  }
  return was_somebody_notified;
}

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(), ShutdownIsolate,
                         reinterpret_cast<uword>(this));
}

MessageHandler* Isolate::message_handler() const {
  return message_handler_;
}

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);
  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 = Set::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();
          Set::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);
  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_;
  message_handler_ = nullptr;

  // Clean up any synchronous FFI callbacks registered with this isolate. Skip
  // if this isolate never registered any.
  if (ffi_callback_list_head_ != nullptr) {
    group()->callback_metadata()->DeleteAllCallbacks(&ffi_callback_list_head_);
  }

#if !defined(PRODUCT)
  if (FLAG_dump_megamorphic_stats) {
    MegamorphicCacheTable::PrintSizes(thread);
  }
  if (FLAG_dump_symbol_stats) {
    Symbols::DumpStats(group());
  }
  if (FLAG_trace_isolates) {
    group()->heap()->PrintSizes();
    OS::PrintErr(
        "[-] Stopping isolate:\n"
        "\tisolate:    %s\n",
        name());
  }
#endif  // !defined(PRODUCT)
  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
#if !defined(PRODUCT)
#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
#endif  // !defined(PRODUCT)
    OS::PrintErr("\n");
  }
}

#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::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);
    ServiceIsolate::SendIsolateShutdownMessage();
#if !defined(PRODUCT)
    debugger()->Shutdown();
#endif

#if defined(DART_INCLUDE_PROFILER)
    Profiler::IsolateShutdown(this);
#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 (isolate->is_kernel_isolate()) {
    KernelIsolate::SetKernelIsolate(nullptr);
  }
#endif
  if (isolate->is_service_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.
  ASSERT(!Thread::Current()->HasActiveState());
  Thread::ExitIsolate(/*isolate_shutdown=*/true);

  // Now it's safe to delete the isolate.
  delete isolate;

  // Run isolate specific cleanup function.
  if (cleanup != nullptr) {
    cleanup(isolate_group->embedder_data(), callback_data);
  }

  const bool shutdown_group = isolate_group->UnregisterIsolateDecrementCount();
  if (shutdown_group) {
    KernelIsolate::NotifyAboutIsolateGroupShutdown(isolate_group);
#if defined(DART_INCLUDE_PROFILER)
    Profiler::IsolateGroupShutdown(isolate_group);
#endif

    {
      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);

      // Clean up any synchronous FFI callbacks registered with this
      // isolate group. Skip if this isolate group never registered any.
      if (isolate_group->ffi_callback_list_head_ != nullptr) {
        isolate_group->callback_metadata()->DeleteAllCallbacks(
            &isolate_group->ffi_callback_list_head_);
      }

      Thread::ExitIsolateGroupAsHelper(/*bypass_safepoint=*/false);
    }

    if (!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.
  }
}

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;

Random* IsolateGroup::isolate_group_random_ = nullptr;
Monitor* Isolate::isolate_creation_monitor_ = nullptr;
bool Isolate::creation_enabled_ = false;
intptr_t Isolate::pending_shutdowns_ = 0;

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*>(&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 (is_service_isolate()) {
    ServiceIsolate::VisitObjectPointers(visitor);
  }
#endif  // !defined(PRODUCT)

  visitor->VisitPointer(
      reinterpret_cast<ObjectPtr*>(&loaded_prefixes_set_storage_));
}

void Isolate::VisitStackPointers(ObjectPointerVisitor* visitor,
                                 ValidationPolicy validate_frames) {
  if (mutator_thread_ != nullptr) {
    mutator_thread_->VisitObjectPointers(visitor, validate_frames);
  }
}

void IsolateGroup::ReleaseStoreBuffers() {
  thread_registry()->ReleaseStoreBuffers();
}

void IsolateGroup::FlushMarkingStacks() {
  thread_registry()->FlushMarkingStacks();
}

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();
}

bool Isolate::SetOwnerThread(ThreadId expected_old_owner, ThreadId new_owner) {
  return owner_thread_.compare_exchange_strong(expected_old_owner, new_owner);
}

ThreadId Isolate::GetOwnerThread(PortMap::Locker* locker) {
  ASSERT(Isolate::Current() == this || locker != nullptr);
  return owner_thread_.load();
}

void IsolateGroup::EnableIncrementalBarrier(
    MarkingStack* old_marking_stack,
    MarkingStack* new_marking_stack,
    MarkingStack* deferred_marking_stack) {
  ASSERT(old_marking_stack_ == nullptr);
  old_marking_stack_ = old_marking_stack;
  ASSERT(new_marking_stack_ == nullptr);
  new_marking_stack_ = new_marking_stack;
  ASSERT(deferred_marking_stack_ == nullptr);
  deferred_marking_stack_ = deferred_marking_stack;
  thread_registry()->AcquireMarkingStacks();
  ASSERT(Thread::Current()->is_marking());
}

void IsolateGroup::DisableIncrementalBarrier() {
  thread_registry()->ReleaseMarkingStacks();
  ASSERT(old_marking_stack_ != nullptr);
  old_marking_stack_ = nullptr;
  ASSERT(new_marking_stack_ != nullptr);
  new_marking_stack_ = nullptr;
  ASSERT(deferred_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->OwnsSafepoint() ||
           (thread->task_kind() == Thread::kMutatorTask) ||
           (thread->task_kind() == Thread::kMarkerTask) ||
           (thread->task_kind() == Thread::kCompactorTask) ||
           (thread->task_kind() == Thread::kScavengerTask) ||
           (thread->task_kind() == Thread::kIncrementalCompactorTask));
    for (Isolate* isolate : isolates_) {
      function(isolate);
    }
    return;
  }
  if (thread != nullptr && thread->OwnsSafepoint()) {
    for (Isolate* isolate : isolates_) {
      function(isolate);
    }
    return;
  }
  SafepointReadRwLocker ml(thread, isolates_lock_.get());
  for (Isolate* isolate : isolates_) {
    function(isolate);
  }
}

void IsolateGroup::ForEachMutatorAtASafepoint(
    std::function<void(Thread* thread)> function) {
  auto thread = Thread::Current();
  ASSERT(thread->OwnsSafepoint() ||
         (thread->task_kind() == Thread::kMutatorTask) ||
         (thread->task_kind() == Thread::kMarkerTask) ||
         (thread->task_kind() == Thread::kCompactorTask) ||
         (thread->task_kind() == Thread::kScavengerTask) ||
         (thread->task_kind() == Thread::kIncrementalCompactorTask));
  for (Isolate* isolate : isolates_) {
    auto thread = isolate->mutator_thread();
    if (thread != nullptr) {
      function(thread);
    }
  }
  for (Thread* mutator : mutators_) {
    ASSERT(mutator != nullptr);
    function(mutator);
  }
}

void IsolateGroup::RunWithStoppedMutatorsCallable(Callable* callable) {
  auto thread = Thread::Current();
  StoppedMutatorsScope stopped_mutators_scope(thread);

  if (thread->OwnsSafepoint()) {
    RELEASE_ASSERT(thread->OwnsSafepoint());
    callable->Call();
    return;
  }

  {
    SafepointReadRwLocker ml(thread, isolates_lock_.get());
    if (thread->IsDartMutatorThread() && ContainsOnlyOneIsolate()) {
      callable->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.
  DeoptSafepointOperationScope safepoint_scope(thread);
  callable->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,
                                       intptr_t slice) {
  switch (slice) {
    case kClassTable:
      class_table()->VisitObjectPointers(visitor);
      if (heap_walk_class_table() != class_table()) {
        heap_walk_class_table()->VisitObjectPointers(visitor);
      }
      break;
    case kApiState:
      api_state()->VisitObjectPointersUnlocked(visitor);
      break;
    case kObjectStore:
      object_store()->VisitObjectPointers(visitor);
      roots()->VisitObjectPointers(visitor);
      break;
    case kInitialFieldTable:
      initial_field_table()->VisitObjectPointers(visitor);
      break;
    case kSentinelFieldTable:
      sentinel_field_table()->VisitObjectPointers(visitor);
      break;
    case kSharedInitialFieldTable:
      shared_initial_field_table()->VisitObjectPointers(visitor);
      break;
    case kSharedFieldTable:
      shared_field_table()->VisitObjectPointers(visitor);
      break;
    case kBackgroundCompiler:
      NOT_IN_PRECOMPILED(background_compiler()->VisitPointers(visitor));
      break;
    case kDebugger:
#if !defined(PRODUCT)
      if (debugger() != nullptr) {
        debugger()->VisitObjectPointers(visitor);
      }
#endif
      break;
    case kReloadContext:
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
      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)
      break;
    case kLoadedBlobs:
      if (source()->loaded_blobs_ != nullptr) {
        visitor->VisitPointer(
            reinterpret_cast<ObjectPtr*>(&(source()->loaded_blobs_)));
      }
      break;
    case kBecome:
      if (become() != nullptr) {
        become()->VisitObjectPointers(visitor);
      }
      break;
    case kObjectIdZones:
#if !defined(PRODUCT)
      if (visitor->trace_object_id_rings()) {
        for (Isolate* isolate : isolates_) {
          isolate->ForEachServiceIdZone([visitor](RingServiceIdZone* ring) {
            ring->VisitPointers(visitor);
          });
        }
      }
#endif  // !defined(PRODUCT)
      break;
    default:
      UNREACHABLE();
  }
}

void IsolateGroup::VisitSharedPointers(ObjectPointerVisitor* visitor) {
  for (intptr_t i = 0; i < kNumRootSlices; i++) {
    VisitSharedPointers(visitor, static_cast<RootSlice>(i));
  }
}

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).
    isolate->VisitStackPointers(visitor, validate_frames);
  }

  visitor->clear_gc_root_type();
}

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);
}

#if !defined(PRODUCT)
RingServiceIdZone& Isolate::AddServiceIdZone(
    ObjectIdRing::BackingBufferKind backing_buffer_kind,
    ObjectIdRing::IdPolicy id_assignment_policy,
    int32_t capacity) {
  EnsureDefaultServiceIdZone();
  switch (backing_buffer_kind) {
    case ObjectIdRing::BackingBufferKind::kRing: {
      const intptr_t zone_id = next_service_id_zone_id_++;

      auto* zone =
          new RingServiceIdZone(zone_id, id_assignment_policy, capacity);
      service_id_zones_->Insert({zone_id, zone});

      return *zone;
    }
    default:
      UNREACHABLE();
  }
}

void Isolate::DeleteServiceIdZone(int32_t id) {
  ASSERT(service_id_zones_ != nullptr);
  auto* pair = service_id_zones_->Lookup(static_cast<intptr_t>(id));
  ASSERT(pair != nullptr);

  delete pair->value;
  service_id_zones_->Remove(static_cast<intptr_t>(id));
}

RingServiceIdZone& Isolate::EnsureDefaultServiceIdZone() {
  if (service_id_zones_ == nullptr) {
    service_id_zones_ = new MallocDirectChainedHashMap<
        IntKeyRawPointerValueTrait<RingServiceIdZone*>>();
  }
  if (!service_id_zones_->HasKey(0)) {
    service_id_zones_->Insert(
        {0,
         new RingServiceIdZone(0, ObjectIdRing::IdPolicy::kAllocateId,
                               RingServiceIdZone::kCapacityOfDefaultIdZone)});
  }
  return *service_id_zones_->Lookup(0)->value;
}

RingServiceIdZone* Isolate::GetServiceIdZone(intptr_t zone_id) const {
  if (service_id_zones_ == nullptr) {
    return nullptr;
  }
  auto* pair = service_id_zones_->Lookup(zone_id);
  return pair != nullptr ? pair->value : nullptr;
}

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;
  }
}

static ServiceEvent IsolatePauseEvent(Isolate* isolate) {
  if (!isolate->is_runnable()) {
    // Isolate is not yet runnable.
    ASSERT((isolate->debugger() == nullptr) ||
           (isolate->debugger()->PauseEvent() == nullptr));
    return ServiceEvent(isolate, ServiceEvent::kNone);
  } else if (isolate->message_handler()->should_pause_on_start()) {
    if (isolate->message_handler()->is_paused_on_start()) {
      ASSERT((isolate->debugger() == nullptr) ||
             (isolate->debugger()->PauseEvent() == nullptr));
      return ServiceEvent(isolate, ServiceEvent::kPauseStart);
    } 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((isolate->debugger() == nullptr) ||
             (isolate->debugger()->PauseEvent() == nullptr));
      return ServiceEvent(isolate, ServiceEvent::kNone);
    }
  } else if (isolate->message_handler()->is_paused_on_exit() &&
             ((isolate->debugger() == nullptr) ||
              (isolate->debugger()->PauseEvent() == nullptr))) {
    return ServiceEvent(isolate, ServiceEvent::kPauseExit);
  } else if ((isolate->debugger() != nullptr) &&
             (isolate->debugger()->PauseEvent() != nullptr) &&
             !isolate->ResumeRequest()) {
    return *(isolate->debugger()->PauseEvent());
  } else {
    ServiceEvent pause_event(isolate, ServiceEvent::kResume);

#if !defined(DART_PRECOMPILED_RUNTIME)
    if (isolate->debugger() != nullptr) {
      // TODO(turnidge): Don't compute a full stack trace.
      DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
      if (stack->Length() > 0) {
        pause_event.set_top_frame(stack->FrameAt(0));
      }
    }
#endif

    return pause_event;
  }
}

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());
  jsobj.AddPropertyF("isolateGroupId", ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
                     group()->id());
  if (ref) {
    return;
  }
  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", open_ports_keepalive_);
  jsobj.AddProperty("pauseOnExit", message_handler()->should_pause_on_exit());
#if !defined(DART_PRECOMPILED_RUNTIME)
  jsobj.AddProperty("_isReloading", group()->IsReloading());
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

  ServiceEvent pause_event = IsolatePauseEvent(this);
  jsobj.AddProperty("pauseEvent", &pause_event);

  const Library& lib = Library::Handle(group()->object_store()->root_library());
  if (!lib.IsNull()) {
    jsobj.AddProperty("rootLib", lib);
  }

  if (Profiler::IsRunning()) {
    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);
}

void Isolate::PrintPauseEventJSON(JSONStream* stream) {
  IsolatePauseEvent(this).PrintJSON(stream);
}

#endif  // !defined(PRODUCT)

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();
}

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.LookupFunctionAllowPrivate(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(
        /* 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();

  Dart_MessageNotifyCallback current_notify_callback =
      iso->message_notify_callback();
  // It is possible that WakePauseEventHandler was replaced by original callback
  // while waiting for pause_loop_monitor_. In that case PauseEventHandler
  // is no longer running and the original callback needs to be invoked instead
  // of incrementing wake_pause_event_handler_count_.
  if (current_notify_callback != Isolate::WakePauseEventHandler) {
    if (current_notify_callback != nullptr) {
      current_notify_callback(isolate);
    }
  } else {
    ++iso->wake_pause_event_handler_count_;
  }
}

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();
  ASSERT(wake_pause_event_handler_count_ == 0);
  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;
  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;
    }

#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) {
    while (wake_pause_event_handler_count_ > 0) {
      saved_notify_callback(Api::CastIsolate(this));
      --wake_pause_event_handler_count_;
    }
  } else {
    wake_pause_event_handler_count_ = 0;
  }
  set_message_notify_callback(saved_notify_callback);
  Dart_ExitScope();
}
#endif  // !defined(PRODUCT)

void Isolate::VisitIsolates(IsolateVisitor* visitor) {
  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;
}

std::unique_ptr<char[]> Isolate::LookupIsolateNameByPort(Dart_Port port) {
  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();
}

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:
  IsolateKillerVisitor(Isolate::LibMsgId msg_id,
                       bool kill_system_isolates = false)
      : target_(nullptr),
        msg_id_(msg_id),
        kill_system_isolates_(kill_system_isolates) {}

  IsolateKillerVisitor(Isolate* isolate, Isolate::LibMsgId msg_id)
      : target_(isolate), msg_id_(msg_id), kill_system_isolates_(false) {}

  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 (kill_system_isolates_) {
      ASSERT(target_ == nullptr);
      // Don't kill the service isolate or kernel isolate.
      return IsSystemIsolate(isolate) && !Isolate::IsVMInternalIsolate(isolate);
    }
    // If a target_ is specified, then only kill the target_.
    // Otherwise, don't kill the service isolate or kernel isolate.
    return (((target_ != nullptr) && (isolate == target_)) ||
            ((target_ == nullptr) && !IsSystemIsolate(isolate)));
  }

  Isolate* target_;
  Isolate::LibMsgId msg_id_;
  bool kill_system_isolates_;
};

void Isolate::KillAllIsolates(LibMsgId msg_id) {
  IsolateKillerVisitor visitor(msg_id);
  VisitIsolates(&visitor);
}

void Isolate::KillAllSystemIsolates(LibMsgId msg_id) {
  IsolateKillerVisitor visitor(msg_id, /*kill_system_isolates=*/true);
  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 != nullptr);
  MonitorLocker ml(&spawn_count_monitor_);
  while (spawn_count_ > 0) {
    ml.WaitWithSafepointCheck(thread);
  }
}

bool Isolate::TryAcquireOwnership() {
  ThreadId current_thread_id = OSThread::GetCurrentThreadId();
  if (SetOwnerThread(OSThread::kInvalidThreadId, current_thread_id)) {
    return true;
  }
  return owner_thread_ == current_thread_id;
}

void Isolate::ReleaseOwnership() {
  bool result = SetOwnerThread(OSThread::GetCurrentThreadId(),
                               OSThread::kInvalidThreadId);
  ASSERT(result);
}

FfiCallbackMetadata::Trampoline Isolate::CreateAsyncFfiCallback(
    Zone* zone,
    const Function& send_function,
    Dart_Port send_port) {
  return group()->callback_metadata()->CreateAsyncFfiCallback(
      this, zone, send_function, send_port, &ffi_callback_list_head_);
}

FfiCallbackMetadata::Trampoline Isolate::CreateIsolateLocalFfiCallback(
    Zone* zone,
    const Function& trampoline,
    const Closure& target,
    bool keep_isolate_alive) {
  if (keep_isolate_alive) {
    UpdateNativeCallableKeepIsolateAliveCounter(1);
  }
  return group()->callback_metadata()->CreateLocalFfiCallback(
      this, /*isolate_group=*/nullptr, zone, trampoline, target,
      &ffi_callback_list_head_);
}

FfiCallbackMetadata::Trampoline
IsolateGroup::CreateIsolateGroupBoundFfiCallback(Zone* zone,
                                                 const Function& trampoline,
                                                 const Closure& target) {
  return callback_metadata()->CreateLocalFfiCallback(
      /*isolate=*/nullptr, this, zone, trampoline, target,
      &ffi_callback_list_head_);
}

void IsolateGroup::DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback) {
  callback_metadata()->DeleteCallback(callback, &ffi_callback_list_head_);
}

bool Isolate::HasLivePorts() {
  ASSERT(0 <= open_ports_ && 0 <= open_ports_keepalive_ &&
         open_ports_keepalive_ <= open_ports_);
  return open_ports_keepalive_ > 0;
}

ReceivePortPtr Isolate::CreateReceivePort(const String& debug_name) {
  Dart_Port port_id = PortMap::CreatePort(message_handler());
  ++open_ports_;
  ++open_ports_keepalive_;
  return ReceivePort::New(port_id, debug_name);
}

void Isolate::SetReceivePortKeepAliveState(const ReceivePort& receive_port,
                                           bool keep_isolate_alive) {
  // Changing keep-isolate-alive state of a closed port is a NOP.
  if (!receive_port.is_open()) return;

  ASSERT(0 < open_ports_);

  // If the state doesn't change it's a NOP.
  if (receive_port.keep_isolate_alive() == keep_isolate_alive) return;

  if (keep_isolate_alive) {
    ASSERT(open_ports_keepalive_ < open_ports_);
    ++open_ports_keepalive_;
  } else {
    ASSERT(0 < open_ports_keepalive_);
    --open_ports_keepalive_;
  }
  receive_port.set_keep_isolate_alive(keep_isolate_alive);
}

void Isolate::CloseReceivePort(const ReceivePort& receive_port) {
  // Closing an already closed port is a NOP.
  if (!receive_port.is_open()) return;

  ASSERT(open_ports_ > 0);
  const bool ok = PortMap::ClosePort(receive_port.Id());
  RELEASE_ASSERT(ok);

  if (receive_port.keep_isolate_alive()) {
    --open_ports_keepalive_;
    receive_port.set_keep_isolate_alive(false);
  }
  --open_ports_;
  receive_port.set_is_open(false);
}

void Isolate::DeleteFfiCallback(FfiCallbackMetadata::Trampoline callback) {
  group()->callback_metadata()->DeleteCallback(callback,
                                               &ffi_callback_list_head_);
}

void Isolate::UpdateNativeCallableKeepIsolateAliveCounter(intptr_t delta) {
  ffi_callback_keep_alive_counter_ += delta;
  ASSERT(ffi_callback_keep_alive_counter_ >= 0);
}

bool Isolate::HasOpenNativeCallables() {
  ASSERT(ffi_callback_keep_alive_counter_ >= 0);
  return ffi_callback_keep_alive_counter_ > 0;
}

#if !defined(PRODUCT)
void IsolateGroup::CloneClassTableForReload() {
  RELEASE_ASSERT(class_table_ == heap_walk_class_table_);
  class_table_ = class_table_->Clone();
  set_cached_class_table_table(nullptr);
}

void IsolateGroup::RestoreOriginalClassTable() {
  RELEASE_ASSERT(class_table_ != heap_walk_class_table_);
  class_table_allocator_.Free(class_table_);
  class_table_ = heap_walk_class_table_;
  set_cached_class_table_table(class_table_->table());
}

void IsolateGroup::DropOriginalClassTable() {
  RELEASE_ASSERT(class_table_ != heap_walk_class_table_);
  class_table_allocator_.Free(heap_walk_class_table_);
  heap_walk_class_table_ = class_table_;
  set_cached_class_table_table(class_table_->table());
}
#endif

}  // namespace dart
