rusty_v8 0.32.1

Rust bindings to V8
Documentation
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.

//! This module's public API exports a number of 'scope' types.
//!
//! These types carry information about the state of the V8 Isolate, as well as
//! lifetimes for certain (return) values. More specialized scopes typically
//! deref to more generic scopes, and ultimately they all deref to `Isolate`.
//!
//! The scope types in the public API are all pointer wrappers, and they all
//! point at a heap-allocated struct `data::ScopeData`. `ScopeData` allocations
//! are never shared between scopes; each Handle/Context/CallbackScope gets
//! its own instance.
//!
//! Notes about the available scope types:
//! See also the tests at the end of this file.
//!
//! - `HandleScope<'s, ()>`
//!   - 's = lifetime of local handles created in this scope, and of the scope
//!     itself.
//!   - This type is returned when a HandleScope is constructed from a direct
//!     reference to an isolate (`&mut Isolate` or `&mut OwnedIsolate`).
//!   - A `Context` is _not_ available. Only certain types JavaScript values can
//!     be created: primitive values, templates, and instances of `Context`.
//!   - Derefs to `Isolate`.
//!
//! - `HandleScope<'s>`
//!   - 's = lifetime of local handles created in this scope, and of the scope
//!     itself.
//!   - A `Context` is available; any type of value can be created.
//!   - Derefs to `HandleScope<'s, ()>`
//!
//! - `ContextScope<'s, P>`
//!   - 's = lifetime of the scope itself.
//!   - A `Context` is available; any type of value can be created.
//!   - Derefs to `P`.
//!   - When constructed as the child of a `HandleScope<'a, ()>`, the returned
//!     type is `ContextScope<'s, HandleScope<'p>>`. In other words, the parent
//!     HandleScope gets an upgrade to indicate the availability of a `Context`.
//!   - When a new scope is constructed inside this type of scope, the
//!     `ContextScope` wrapper around `P` is erased first, which means that the
//!     child scope is set up as if it had been created with `P` as its parent.
//!
//! - `EscapableHandleScope<'s, 'e>`
//!   - 's = lifetime of local handles created in this scope, and of the scope
//!     itself.
//!   - 'e = lifetime of the HandleScope that will receive the local handle that
//!     is created by `EscapableHandleScope::escape()`.
//!   - A `Context` is available; any type of value can be created.
//!   - Derefs to `HandleScope<'s>`.
//!
//! - `TryCatch<'s, P>`
//!   - 's = lifetime of the TryCatch scope.
//!   - `P` is either a `HandleScope` or an `EscapableHandleScope`. This type
//!     also determines for how long the values returned by `TryCatch` methods
//!     `exception()`, `message()`, and `stack_trace()` are valid.
//!   - Derefs to `P`.
//!   - Creating a new scope inside the `TryCatch` block makes its methods
//!     inaccessible until the inner scope is dropped. However, the `TryCatch`
//!     object will nonetheless catch all exception thrown during its lifetime.
//!
//! - `CallbackScope<'s, ()>`
//!   - 's = lifetime of local handles created in this scope, and the value
//!     returned from the callback, and of the scope itself.
//!   - A `Context` is _not_ available. Only certain types JavaScript values can
//!     be created: primitive values, templates, and instances of `Context`.
//!   - Derefs to `HandleScope<'s, ()>`.
//!   - This scope type is only to be constructed inside embedder defined
//!     callbacks when these are called by V8.
//!   - When a scope is created inside, type is erased to `HandleScope<'s, ()>`.
//!
//! - `CallbackScope<'s>`
//!   - 's = lifetime of local handles created in this scope, and the value
//!     returned from the callback, and of the scope itself.
//!   - A `Context` is available; any type of value can be created.
//!   - Derefs to `HandleScope<'s>`.
//!   - This scope type is only to be constructed inside embedder defined
//!     callbacks when these are called by V8.
//!   - When a scope is created inside, type is erased to `HandleScope<'s>`.

use std::alloc::alloc;
use std::alloc::Layout;
use std::any::type_name;
use std::cell::Cell;
use std::convert::TryInto;

use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::num::NonZeroUsize;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ptr;
use std::ptr::NonNull;

use crate::function::FunctionCallbackInfo;
use crate::function::PropertyCallbackInfo;
use crate::Context;
use crate::Data;
use crate::DataError;
use crate::Handle;
use crate::Isolate;
use crate::Local;
use crate::Message;
use crate::Object;
use crate::OwnedIsolate;
use crate::Primitive;
use crate::PromiseRejectMessage;
use crate::Value;

/// Stack-allocated class which sets the execution context for all operations
/// executed within a local scope. After entering a context, all code compiled
/// and run is compiled and run in this context.
#[derive(Debug)]
pub struct ContextScope<'s, P> {
  data: NonNull<data::ScopeData>,
  _phantom: PhantomData<&'s mut P>,
}

impl<'s, P: param::NewContextScope<'s>> ContextScope<'s, P> {
  #[allow(clippy::new_ret_no_self)]
  pub fn new(param: &'s mut P, context: Local<Context>) -> P::NewScope {
    let scope_data = param.get_scope_data_mut();
    if scope_data.get_isolate_ptr()
      != unsafe { raw::v8__Context__GetIsolate(&*context) }
    {
      panic!(
        "{} and Context do not belong to the same Isolate",
        type_name::<P>()
      )
    }
    let new_scope_data = scope_data.new_context_scope_data(context);
    new_scope_data.as_scope()
  }
}

/// A stack-allocated class that governs a number of local handles.
/// After a handle scope has been created, all local handles will be
/// allocated within that handle scope until either the handle scope is
/// deleted or another handle scope is created.  If there is already a
/// handle scope and a new one is created, all allocations will take
/// place in the new handle scope until it is deleted.  After that,
/// new handles will again be allocated in the original handle scope.
///
/// After the handle scope of a local handle has been deleted the
/// garbage collector will no longer track the object stored in the
/// handle and may deallocate it.  The behavior of accessing a handle
/// for which the handle scope has been deleted is undefined.
#[derive(Debug)]
pub struct HandleScope<'s, C = Context> {
  data: NonNull<data::ScopeData>,
  _phantom: PhantomData<&'s mut C>,
}

impl<'s> HandleScope<'s> {
  #[allow(clippy::new_ret_no_self)]
  pub fn new<P: param::NewHandleScope<'s>>(param: &'s mut P) -> P::NewScope {
    param
      .get_scope_data_mut()
      .new_handle_scope_data()
      .as_scope()
  }

  /// Opens a new `HandleScope` and enters a `Context` in one step.
  /// The first argument should be an `Isolate` or `OwnedIsolate`.
  /// The second argument can be any handle that refers to a `Context` object;
  /// usually this will be a `Global<Context>`.
  pub fn with_context<
    P: param::NewHandleScopeWithContext<'s>,
    H: Handle<Data = Context>,
  >(
    param: &'s mut P,
    context: H,
  ) -> Self {
    let context_ref = context.open(param.get_isolate_mut());
    param
      .get_scope_data_mut()
      .new_handle_scope_data_with_context(context_ref)
      .as_scope()
  }

  /// Returns the context of the currently running JavaScript, or the context
  /// on the top of the stack if no JavaScript is running.
  pub fn get_current_context(&self) -> Local<'s, Context> {
    let context_ptr = data::ScopeData::get(self).get_current_context();
    unsafe { Local::from_raw(context_ptr) }.unwrap()
  }

  /// Returns either the last context entered through V8's C++ API, or the
  /// context of the currently running microtask while processing microtasks.
  /// If a context is entered while executing a microtask, that context is
  /// returned.
  pub fn get_entered_or_microtask_context(&self) -> Local<'s, Context> {
    let data = data::ScopeData::get(self);
    let isolate_ptr = data.get_isolate_ptr();
    let context_ptr =
      unsafe { raw::v8__Isolate__GetEnteredOrMicrotaskContext(isolate_ptr) };
    unsafe { Local::from_raw(context_ptr) }.unwrap()
  }
}

impl<'s> HandleScope<'s, ()> {
  /// Schedules an exception to be thrown when returning to JavaScript. When
  /// an exception has been scheduled it is illegal to invoke any
  /// JavaScript operation; the caller must return immediately and only
  /// after the exception has been handled does it become legal to invoke
  /// JavaScript operations.
  ///
  /// This function always returns the `undefined` value.
  pub fn throw_exception(
    &mut self,
    exception: Local<Value>,
  ) -> Local<'s, Value> {
    unsafe {
      self.cast_local(|sd| {
        raw::v8__Isolate__ThrowException(sd.get_isolate_ptr(), &*exception)
      })
    }
    .unwrap()
  }

  pub(crate) unsafe fn cast_local<T>(
    &mut self,
    f: impl FnOnce(&mut data::ScopeData) -> *const T,
  ) -> Option<Local<'s, T>> {
    Local::from_raw(f(data::ScopeData::get_mut(self)))
  }

  pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate {
    data::ScopeData::get(self).get_isolate_ptr()
  }
}

impl<'s> HandleScope<'s> {
  /// Return data that was previously attached to the isolate snapshot via
  /// SnapshotCreator, and removes the reference to it. If called again with
  /// same `index` argument, this function returns `DataError::NoData`.
  ///
  /// The value that was stored in the snapshot must either match or be
  /// convertible to type parameter `T`, otherwise `DataError::BadType` is
  /// returned.
  pub fn get_isolate_data_from_snapshot_once<T>(
    &mut self,
    index: usize,
  ) -> Result<Local<'s, T>, DataError>
  where
    T: 'static,
    for<'l> Local<'l, Data>: TryInto<Local<'l, T>, Error = DataError>,
  {
    unsafe {
      self
        .cast_local(|sd| {
          raw::v8__Isolate__GetDataFromSnapshotOnce(sd.get_isolate_ptr(), index)
        })
        .ok_or_else(DataError::no_data::<T>)
        .and_then(|data| data.try_into())
    }
  }

  /// Return data that was previously attached to the context snapshot via
  /// SnapshotCreator, and removes the reference to it. If called again with
  /// same `index` argument, this function returns `DataError::NoData`.
  ///
  /// The value that was stored in the snapshot must either match or be
  /// convertible to type parameter `T`, otherwise `DataError::BadType` is
  /// returned.
  pub fn get_context_data_from_snapshot_once<T>(
    &mut self,
    index: usize,
  ) -> Result<Local<'s, T>, DataError>
  where
    T: 'static,
    for<'l> Local<'l, Data>: TryInto<Local<'l, T>, Error = DataError>,
  {
    unsafe {
      self
        .cast_local(|sd| {
          raw::v8__Context__GetDataFromSnapshotOnce(
            sd.get_current_context(),
            index,
          )
        })
        .ok_or_else(DataError::no_data::<T>)
        .and_then(|data| data.try_into())
    }
  }
}

/// A HandleScope which first allocates a handle in the current scope
/// which will be later filled with the escape value.
// TODO(piscisaureus): type parameter `C` is not very useful in practice; being
// a source of complexity and potential confusion, it is desirable to
// eventually remove it. Blocker at the time of writing is that there are some
// tests that enter an `EscapableHandleScope` without creating a `ContextScope`
// at all. These tests need to updated first.
#[derive(Debug)]
pub struct EscapableHandleScope<'s, 'e: 's, C = Context> {
  data: NonNull<data::ScopeData>,
  _phantom:
    PhantomData<(&'s mut raw::HandleScope, &'e mut raw::EscapeSlot, &'s C)>,
}

impl<'s, 'e: 's> EscapableHandleScope<'s, 'e> {
  #[allow(clippy::new_ret_no_self)]
  pub fn new<P: param::NewEscapableHandleScope<'s, 'e>>(
    param: &'s mut P,
  ) -> P::NewScope {
    param
      .get_scope_data_mut()
      .new_escapable_handle_scope_data()
      .as_scope()
  }
}

impl<'s, 'e: 's, C> EscapableHandleScope<'s, 'e, C> {
  /// Pushes the value into the previous scope and returns a handle to it.
  /// Cannot be called twice.
  pub fn escape<T>(&mut self, value: Local<T>) -> Local<'e, T>
  where
    for<'l> Local<'l, T>: Into<Local<'l, Data>>,
  {
    let escape_slot = data::ScopeData::get_mut(self)
      .get_escape_slot_mut()
      .expect("internal error: EscapableHandleScope has no escape slot")
      .take()
      .expect("EscapableHandleScope::escape() called twice");
    escape_slot.escape(value)
  }
}

/// An external exception handler.
#[derive(Debug)]
pub struct TryCatch<'s, P> {
  data: NonNull<data::ScopeData>,
  _phantom: PhantomData<&'s mut P>,
}

impl<'s, P: param::NewTryCatch<'s>> TryCatch<'s, P> {
  #[allow(clippy::new_ret_no_self)]
  pub fn new(param: &'s mut P) -> P::NewScope {
    param.get_scope_data_mut().new_try_catch_data().as_scope()
  }
}

impl<'s, P> TryCatch<'s, P> {
  /// Returns true if an exception has been caught by this try/catch block.
  pub fn has_caught(&self) -> bool {
    unsafe { raw::v8__TryCatch__HasCaught(self.get_raw()) }
  }

  /// For certain types of exceptions, it makes no sense to continue execution.
  ///
  /// If CanContinue returns false, the correct action is to perform any C++
  /// cleanup needed and then return. If CanContinue returns false and
  /// HasTerminated returns true, it is possible to call
  /// CancelTerminateExecution in order to continue calling into the engine.
  pub fn can_continue(&self) -> bool {
    unsafe { raw::v8__TryCatch__CanContinue(self.get_raw()) }
  }

  /// Returns true if an exception has been caught due to script execution
  /// being terminated.
  ///
  /// There is no JavaScript representation of an execution termination
  /// exception. Such exceptions are thrown when the TerminateExecution
  /// methods are called to terminate a long-running script.
  ///
  /// If such an exception has been thrown, HasTerminated will return true,
  /// indicating that it is possible to call CancelTerminateExecution in order
  /// to continue calling into the engine.
  pub fn has_terminated(&self) -> bool {
    unsafe { raw::v8__TryCatch__HasTerminated(self.get_raw()) }
  }

  /// Returns true if verbosity is enabled.
  pub fn is_verbose(&self) -> bool {
    unsafe { raw::v8__TryCatch__IsVerbose(self.get_raw()) }
  }

  /// Set verbosity of the external exception handler.
  ///
  /// By default, exceptions that are caught by an external exception
  /// handler are not reported. Call SetVerbose with true on an
  /// external exception handler to have exceptions caught by the
  /// handler reported as if they were not caught.
  pub fn set_verbose(&mut self, value: bool) {
    unsafe { raw::v8__TryCatch__SetVerbose(self.get_raw_mut(), value) };
  }

  /// Set whether or not this TryCatch should capture a Message object
  /// which holds source information about where the exception
  /// occurred. True by default.
  pub fn set_capture_message(&mut self, value: bool) {
    unsafe { raw::v8__TryCatch__SetCaptureMessage(self.get_raw_mut(), value) };
  }

  /// Clears any exceptions that may have been caught by this try/catch block.
  /// After this method has been called, HasCaught() will return false. Cancels
  /// the scheduled exception if it is caught and ReThrow() is not called
  /// before.
  ///
  /// It is not necessary to clear a try/catch block before using it again; if
  /// another exception is thrown the previously caught exception will just be
  /// overwritten. However, it is often a good idea since it makes it easier
  /// to determine which operation threw a given exception.
  pub fn reset(&mut self) {
    unsafe { raw::v8__TryCatch__Reset(self.get_raw_mut()) };
  }

  fn get_raw(&self) -> &raw::TryCatch {
    data::ScopeData::get(self).get_try_catch()
  }

  fn get_raw_mut(&mut self) -> &mut raw::TryCatch {
    data::ScopeData::get_mut(self).get_try_catch_mut()
  }
}

impl<'s, 'p: 's, P> TryCatch<'s, P>
where
  Self: AsMut<HandleScope<'p, ()>>,
{
  /// Returns the exception caught by this try/catch block. If no exception has
  /// been caught an empty handle is returned.
  ///
  /// Note: v8.h states that "the returned handle is valid until this TryCatch
  /// block has been destroyed". This is incorrect; the return value lives
  /// no longer and no shorter than the active HandleScope at the time this
  /// method is called. An issue has been opened about this in the V8 bug
  /// tracker: https://bugs.chromium.org/p/v8/issues/detail?id=10537.
  pub fn exception(&mut self) -> Option<Local<'p, Value>> {
    unsafe {
      self
        .as_mut()
        .cast_local(|sd| raw::v8__TryCatch__Exception(sd.get_try_catch()))
    }
  }

  /// Returns the message associated with this exception. If there is
  /// no message associated an empty handle is returned.
  ///
  /// Note: the remark about the lifetime for the `exception()` return value
  /// applies here too.
  pub fn message(&mut self) -> Option<Local<'p, Message>> {
    unsafe {
      self
        .as_mut()
        .cast_local(|sd| raw::v8__TryCatch__Message(sd.get_try_catch()))
    }
  }

  /// Throws the exception caught by this TryCatch in a way that avoids
  /// it being caught again by this same TryCatch. As with ThrowException
  /// it is illegal to execute any JavaScript operations after calling
  /// ReThrow; the caller must return immediately to where the exception
  /// is caught.
  ///
  /// This function returns the `undefined` value when successful, or `None` if
  /// no exception was caught and therefore there was nothing to rethrow.
  pub fn rethrow(&mut self) -> Option<Local<'_, Value>> {
    unsafe {
      self
        .as_mut()
        .cast_local(|sd| raw::v8__TryCatch__ReThrow(sd.get_try_catch_mut()))
    }
  }
}

impl<'s, 'p: 's, P> TryCatch<'s, P>
where
  Self: AsMut<HandleScope<'p>>,
{
  /// Returns the .stack property of the thrown object. If no .stack
  /// property is present an empty handle is returned.
  pub fn stack_trace(&mut self) -> Option<Local<'p, Value>> {
    unsafe {
      self.as_mut().cast_local(|sd| {
        raw::v8__TryCatch__StackTrace(
          sd.get_try_catch(),
          sd.get_current_context(),
        )
      })
    }
  }
}

/// A `CallbackScope` can be used to bootstrap a `HandleScope` and
/// `ContextScope` inside a callback function that gets called by V8.
/// Bootstrapping a scope inside a callback is the only valid use case of this
/// type; using it in other places leads to undefined behavior, which is also
/// the reason `CallbackScope::new()` is marked as being an unsafe function.
///
/// For some callback types, rusty_v8 internally creates a scope and passes it
/// as an argument to to embedder callback. Eventually we intend to wrap all
/// callbacks in this fashion, so the embedder would never needs to construct
/// a CallbackScope.
///
/// A `CallbackScope<()>`, without context, can be created from:
///   - `&mut Isolate`
///   - `&mut OwnedIsolate`
///
/// A `CallbackScope`, with context, can be created from:
///   - `Local<Context>`
///   - `Local<Message>`
///   - `Local<Object>`
///   - `Local<Promise>`
///   - `Local<SharedArrayBuffer>`
///   - `&FunctionCallbackInfo`
///   - `&PropertyCallbackInfo`
///   - `&PromiseRejectMessage`
#[derive(Debug)]
pub struct CallbackScope<'s, C = Context> {
  data: NonNull<data::ScopeData>,
  _phantom: PhantomData<&'s mut HandleScope<'s, C>>,
}

impl<'s> CallbackScope<'s> {
  #[allow(clippy::new_ret_no_self)]
  pub unsafe fn new<P: param::NewCallbackScope<'s>>(param: P) -> P::NewScope {
    let (isolate, context) = param.get_isolate_mut_and_maybe_current_context();
    data::ScopeData::get_current_mut(isolate)
      .new_callback_scope_data(context)
      .as_scope()
  }
}

macro_rules! impl_as {
  // Implements `AsRef<Isolate>` and AsMut<Isolate>` on a scope type.
  (<$($params:tt),+> $src_type:ty as Isolate) => {
    impl<$($params),*> AsRef<Isolate> for $src_type {
      fn as_ref(&self) -> &Isolate {
        data::ScopeData::get(self).get_isolate()
      }
    }

    impl<$($params),*> AsMut<Isolate> for $src_type {
      fn as_mut(&mut self) -> &mut Isolate {
        data::ScopeData::get_mut(self).get_isolate_mut()
      }
    }
  };

  // Implements `AsRef` and `AsMut` traits for the purpose of converting a
  // a scope reference to a scope reference with a different but compatible type.
  (<$($params:tt),+> $src_type:ty as $tgt_type:ty) => {
    impl<$($params),*> AsRef<$tgt_type> for $src_type {
      fn as_ref(&self) -> &$tgt_type {
        self.cast_ref()
      }
    }

    impl<$($params),*> AsMut< $tgt_type> for $src_type {
      fn as_mut(&mut self) -> &mut $tgt_type {
        self.cast_mut()
      }
    }
  };
}

impl_as!(<'s, 'p, P> ContextScope<'s, P> as Isolate);
impl_as!(<'s, C> HandleScope<'s, C> as Isolate);
impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as Isolate);
impl_as!(<'s, P> TryCatch<'s, P> as Isolate);
impl_as!(<'s, C> CallbackScope<'s, C> as Isolate);

impl_as!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p, ()>);
impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as HandleScope<'p, ()>);
impl_as!(<'s, C> HandleScope<'s, C> as HandleScope<'s, ()>);
impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as HandleScope<'s, ()>);
impl_as!(<'s, 'p, C> TryCatch<'s, HandleScope<'p, C>> as HandleScope<'p, ()>);
impl_as!(<'s, 'p, 'e, C> TryCatch<'s, EscapableHandleScope<'p, 'e, C>> as HandleScope<'p, ()>);
impl_as!(<'s, C> CallbackScope<'s, C> as HandleScope<'s, ()>);

impl_as!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p>);
impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as HandleScope<'p>);
impl_as!(<'s> HandleScope<'s> as HandleScope<'s>);
impl_as!(<'s, 'e> EscapableHandleScope<'s, 'e> as HandleScope<'s>);
impl_as!(<'s, 'p> TryCatch<'s, HandleScope<'p>> as HandleScope<'p>);
impl_as!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as HandleScope<'p>);
impl_as!(<'s> CallbackScope<'s> as HandleScope<'s>);

impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e, ()>);
impl_as!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> as EscapableHandleScope<'s, 'e, ()>);
impl_as!(<'s, 'p, 'e, C> TryCatch<'s, EscapableHandleScope<'p, 'e, C>> as EscapableHandleScope<'p, 'e, ()>);

impl_as!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>);
impl_as!(<'s, 'e> EscapableHandleScope<'s, 'e> as EscapableHandleScope<'s, 'e>);
impl_as!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>);

impl_as!(<'s, 'p, C> TryCatch<'s, HandleScope<'p, C>> as TryCatch<'s, HandleScope<'p, ()>>);
impl_as!(<'s, 'p, 'e, C> TryCatch<'s, EscapableHandleScope<'p, 'e, C>> as TryCatch<'s, HandleScope<'p, ()>>);
impl_as!(<'s, 'p, 'e, C> TryCatch<'s, EscapableHandleScope<'p, 'e, C>> as TryCatch<'s, EscapableHandleScope<'p, 'e, ()>>);

impl_as!(<'s, 'p> TryCatch<'s, HandleScope<'p>> as TryCatch<'s, HandleScope<'p>>);
impl_as!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as TryCatch<'s, HandleScope<'p>>);
impl_as!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as TryCatch<'s, EscapableHandleScope<'p, 'e>>);

macro_rules! impl_deref {
  (<$($params:tt),+> $src_type:ty as $tgt_type:ty) => {
    impl<$($params),*> Deref for $src_type {
      type Target = $tgt_type;
      fn deref(&self) -> &Self::Target {
        self.as_ref()
      }
    }

    impl<$($params),*> DerefMut for $src_type {
      fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_mut()
      }
    }
  };
}

impl_deref!(<'s, 'p> ContextScope<'s, HandleScope<'p>> as HandleScope<'p>);
impl_deref!(<'s, 'p, 'e> ContextScope<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>);

impl_deref!(<'s> HandleScope<'s, ()> as Isolate);
impl_deref!(<'s> HandleScope<'s> as HandleScope<'s, ()>);

impl_deref!(<'s, 'e> EscapableHandleScope<'s, 'e, ()> as HandleScope<'s, ()>);
impl_deref!(<'s, 'e> EscapableHandleScope<'s, 'e> as HandleScope<'s>);

impl_deref!(<'s, 'p> TryCatch<'s, HandleScope<'p, ()>> as HandleScope<'p, ()>);
impl_deref!(<'s, 'p> TryCatch<'s, HandleScope<'p>> as HandleScope<'p>);
impl_deref!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e, ()>> as EscapableHandleScope<'p, 'e, ()>);
impl_deref!(<'s, 'p, 'e> TryCatch<'s, EscapableHandleScope<'p, 'e>> as EscapableHandleScope<'p, 'e>);

impl_deref!(<'s> CallbackScope<'s, ()> as HandleScope<'s, ()>);
impl_deref!(<'s> CallbackScope<'s> as HandleScope<'s>);

macro_rules! impl_scope_drop {
  (<$($params:tt),+> $type:ty) => {
    unsafe impl<$($params),*> Scope for $type {}

    impl<$($params),*> Drop for $type {
      fn drop(&mut self) {
        data::ScopeData::get_mut(self).notify_scope_dropped();
      }
    }
  };
}

impl_scope_drop!(<'s, 'p, P> ContextScope<'s, P>);
impl_scope_drop!(<'s, C> HandleScope<'s, C> );
impl_scope_drop!(<'s, 'e, C> EscapableHandleScope<'s, 'e, C> );
impl_scope_drop!(<'s, P> TryCatch<'s, P> );
impl_scope_drop!(<'s, C> CallbackScope<'s, C> );

pub unsafe trait Scope: Sized {}

trait ScopeCast: Sized {
  fn cast_ref<S: Scope>(&self) -> &S;
  fn cast_mut<S: Scope>(&mut self) -> &mut S;
}

impl<T: Scope> ScopeCast for T {
  fn cast_ref<S: Scope>(&self) -> &S {
    assert_eq!(Layout::new::<Self>(), Layout::new::<S>());
    unsafe { &*(self as *const _ as *const S) }
  }

  fn cast_mut<S: Scope>(&mut self) -> &mut S {
    assert_eq!(Layout::new::<Self>(), Layout::new::<S>());
    unsafe { &mut *(self as *mut _ as *mut S) }
  }
}

/// Scopes are typically constructed as the child of another scope. The scope
/// that is returned from `«Child»Scope::new(parent: &mut «Parent»Scope)` does
/// not necessarily have type `«Child»Scope`, but rather its type is a merger of
/// both the the parent and child scope types.
///
/// For example: a `ContextScope` created inside `HandleScope<'a, ()>` does not
/// produce a `ContextScope`, but rather a `HandleScope<'a, Context>`, which
/// describes a scope that is both a `HandleScope` _and_ a `ContextScope`.
///
/// The Traits in the (private) `param` module define which types can be passed
/// as a parameter to the `«Some»Scope::new()` constructor, and what the
/// actual, merged scope type will be that `new()` returns for a specific
/// parameter type.
mod param {
  use super::*;

  pub trait NewContextScope<'s>: getter::GetScopeData {
    type NewScope: Scope;
  }

  impl<'s, 'p: 's, P: Scope> NewContextScope<'s> for ContextScope<'p, P> {
    type NewScope = ContextScope<'s, P>;
  }

  impl<'s, 'p: 's, C> NewContextScope<'s> for HandleScope<'p, C> {
    type NewScope = ContextScope<'s, HandleScope<'p>>;
  }

  impl<'s, 'p: 's, 'e: 'p, C> NewContextScope<'s>
    for EscapableHandleScope<'p, 'e, C>
  {
    type NewScope = ContextScope<'s, EscapableHandleScope<'p, 'e>>;
  }

  impl<'s, 'p: 's, P: NewContextScope<'s>> NewContextScope<'s>
    for TryCatch<'p, P>
  {
    type NewScope = <P as NewContextScope<'s>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewContextScope<'s> for CallbackScope<'p, C> {
    type NewScope = ContextScope<'s, HandleScope<'p>>;
  }

  pub trait NewHandleScope<'s>: getter::GetScopeData {
    type NewScope: Scope;
  }

  impl<'s> NewHandleScope<'s> for Isolate {
    type NewScope = HandleScope<'s, ()>;
  }

  impl<'s> NewHandleScope<'s> for OwnedIsolate {
    type NewScope = HandleScope<'s, ()>;
  }

  impl<'s, 'p: 's, P: NewHandleScope<'s>> NewHandleScope<'s>
    for ContextScope<'p, P>
  {
    type NewScope = <P as NewHandleScope<'s>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewHandleScope<'s> for HandleScope<'p, C> {
    type NewScope = HandleScope<'s, C>;
  }

  impl<'s, 'p: 's, 'e: 'p, C> NewHandleScope<'s>
    for EscapableHandleScope<'p, 'e, C>
  {
    type NewScope = EscapableHandleScope<'s, 'e, C>;
  }

  impl<'s, 'p: 's, P: NewHandleScope<'s>> NewHandleScope<'s> for TryCatch<'p, P> {
    type NewScope = <P as NewHandleScope<'s>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewHandleScope<'s> for CallbackScope<'p, C> {
    type NewScope = HandleScope<'s, C>;
  }

  pub trait NewHandleScopeWithContext<'s>: getter::GetScopeData {
    fn get_isolate_mut(&mut self) -> &mut Isolate;
  }

  impl<'s> NewHandleScopeWithContext<'s> for Isolate {
    fn get_isolate_mut(&mut self) -> &mut Isolate {
      self
    }
  }

  impl<'s> NewHandleScopeWithContext<'s> for OwnedIsolate {
    fn get_isolate_mut(&mut self) -> &mut Isolate {
      &mut *self
    }
  }

  pub trait NewEscapableHandleScope<'s, 'e: 's>: getter::GetScopeData {
    type NewScope: Scope;
  }

  impl<'s, 'p: 's, 'e: 'p, P: NewEscapableHandleScope<'s, 'e>>
    NewEscapableHandleScope<'s, 'e> for ContextScope<'p, P>
  {
    type NewScope = <P as NewEscapableHandleScope<'s, 'e>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewEscapableHandleScope<'s, 'p> for HandleScope<'p, C> {
    type NewScope = EscapableHandleScope<'s, 'p, C>;
  }

  impl<'s, 'p: 's, 'e: 'p, C> NewEscapableHandleScope<'s, 'p>
    for EscapableHandleScope<'p, 'e, C>
  {
    type NewScope = EscapableHandleScope<'s, 'p, C>;
  }

  impl<'s, 'p: 's, 'e: 'p, P: NewEscapableHandleScope<'s, 'e>>
    NewEscapableHandleScope<'s, 'e> for TryCatch<'p, P>
  {
    type NewScope = <P as NewEscapableHandleScope<'s, 'e>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewEscapableHandleScope<'s, 'p> for CallbackScope<'p, C> {
    type NewScope = EscapableHandleScope<'s, 'p, C>;
  }

  pub trait NewTryCatch<'s>: getter::GetScopeData {
    type NewScope: Scope;
  }

  impl<'s, 'p: 's, P: NewTryCatch<'s>> NewTryCatch<'s> for ContextScope<'p, P> {
    type NewScope = <P as NewTryCatch<'s>>::NewScope;
  }

  impl<'s, 'p: 's, C> NewTryCatch<'s> for HandleScope<'p, C> {
    type NewScope = TryCatch<'s, HandleScope<'p, C>>;
  }

  impl<'s, 'p: 's, 'e: 'p, C> NewTryCatch<'s>
    for EscapableHandleScope<'p, 'e, C>
  {
    type NewScope = TryCatch<'s, EscapableHandleScope<'p, 'e, C>>;
  }

  impl<'s, 'p: 's, P> NewTryCatch<'s> for TryCatch<'p, P> {
    type NewScope = TryCatch<'s, P>;
  }

  impl<'s, 'p: 's, C> NewTryCatch<'s> for CallbackScope<'p, C> {
    type NewScope = TryCatch<'s, HandleScope<'p, C>>;
  }

  pub trait NewCallbackScope<'s>: Sized + getter::GetIsolate<'s> {
    type NewScope: Scope;

    unsafe fn get_isolate_mut_and_maybe_current_context(
      self,
    ) -> (&'s mut Isolate, Option<Local<'s, Context>>) {
      (self.get_isolate_mut(), None)
    }
  }

  impl<'s> NewCallbackScope<'s> for &'s mut Isolate {
    type NewScope = CallbackScope<'s, ()>;
  }

  impl<'s> NewCallbackScope<'s> for &'s mut OwnedIsolate {
    type NewScope = CallbackScope<'s, ()>;
  }

  impl<'s> NewCallbackScope<'s> for &'s FunctionCallbackInfo {
    type NewScope = CallbackScope<'s>;
  }

  impl<'s> NewCallbackScope<'s> for &'s PropertyCallbackInfo {
    type NewScope = CallbackScope<'s>;
  }

  impl<'s> NewCallbackScope<'s> for Local<'s, Context> {
    type NewScope = CallbackScope<'s>;

    unsafe fn get_isolate_mut_and_maybe_current_context(
      self,
    ) -> (&'s mut Isolate, Option<Local<'s, Context>>) {
      (getter::GetIsolate::get_isolate_mut(self), Some(self))
    }
  }

  impl<'s> NewCallbackScope<'s> for Local<'s, Message> {
    type NewScope = CallbackScope<'s>;
  }

  impl<'s, T: Into<Local<'s, Object>>> NewCallbackScope<'s> for T {
    type NewScope = CallbackScope<'s>;
  }

  impl<'s> NewCallbackScope<'s> for &'s PromiseRejectMessage<'s> {
    type NewScope = CallbackScope<'s>;
  }
}

/// The private `getter` module defines traits to look up the related `Isolate`
/// and `ScopeData` for many different types. The implementation of those traits
/// on the types that implement them are also all contained in this module.
mod getter {
  pub use super::*;

  pub trait GetIsolate<'s> {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate;
  }

  impl<'s> GetIsolate<'s> for &'s mut Isolate {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      self
    }
  }

  impl<'s> GetIsolate<'s> for &'s mut OwnedIsolate {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      &mut *self
    }
  }

  impl<'s> GetIsolate<'s> for &'s FunctionCallbackInfo {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      &mut *raw::v8__FunctionCallbackInfo__GetIsolate(self)
    }
  }

  impl<'s> GetIsolate<'s> for &'s PropertyCallbackInfo {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      &mut *raw::v8__PropertyCallbackInfo__GetIsolate(self)
    }
  }

  impl<'s> GetIsolate<'s> for Local<'s, Context> {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      &mut *raw::v8__Context__GetIsolate(&*self)
    }
  }

  impl<'s> GetIsolate<'s> for Local<'s, Message> {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      &mut *raw::v8__Message__GetIsolate(&*self)
    }
  }

  impl<'s, T: Into<Local<'s, Object>>> GetIsolate<'s> for T {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      let object: Local<Object> = self.into();
      &mut *raw::v8__Object__GetIsolate(&*object)
    }
  }

  impl<'s> GetIsolate<'s> for &'s PromiseRejectMessage<'s> {
    unsafe fn get_isolate_mut(self) -> &'s mut Isolate {
      let object: Local<Object> = self.get_promise().into();
      &mut *raw::v8__Object__GetIsolate(&*object)
    }
  }

  pub trait GetScopeData {
    fn get_scope_data_mut(&mut self) -> &mut data::ScopeData;
  }

  impl<T: Scope> GetScopeData for T {
    fn get_scope_data_mut(&mut self) -> &mut data::ScopeData {
      data::ScopeData::get_mut(self)
    }
  }

  impl GetScopeData for Isolate {
    fn get_scope_data_mut(&mut self) -> &mut data::ScopeData {
      data::ScopeData::get_root_mut(self)
    }
  }

  impl GetScopeData for OwnedIsolate {
    fn get_scope_data_mut(&mut self) -> &mut data::ScopeData {
      data::ScopeData::get_root_mut(self)
    }
  }
}

/// All publicly exported `«Some»Scope` types are essentially wrapping a pointer
/// to a heap-allocated struct `ScopeData`. This module contains the definition
/// for `ScopeData` and its inner types, as well as related helper traits.
pub(crate) mod data {
  use super::*;

  #[derive(Debug)]
  pub struct ScopeData {
    // The first four fields are always valid - even when the `Box<ScopeData>`
    // struct is free (does not contain data related to an actual scope).
    // The `previous` and `isolate` fields never change; the `next` field is
    // set to `None` initially when the struct is created, but it may later be
    // assigned a `Some(Box<ScopeData>)` value, after which this field never
    // changes again.
    isolate: NonNull<Isolate>,
    previous: Option<NonNull<ScopeData>>,
    next: Option<Box<ScopeData>>,
    // The 'status' field is also always valid (but does change).
    status: Cell<ScopeStatus>,
    // The following fields are only valid when this ScopeData object is in use
    // (eiter current or shadowed -- not free).
    context: Cell<Option<NonNull<Context>>>,
    escape_slot: Option<NonNull<Option<raw::EscapeSlot>>>,
    try_catch: Option<NonNull<raw::TryCatch>>,
    scope_type_specific_data: ScopeTypeSpecificData,
  }

  impl ScopeData {
    /// Returns a mutable reference to the data associated with topmost scope
    /// on the scope stack. This function does not automatically exit zombie
    /// scopes, so it might return a zombie ScopeData reference.
    pub(crate) fn get_current_mut(isolate: &mut Isolate) -> &mut Self {
      let self_mut = isolate
        .get_current_scope_data()
        .map(NonNull::as_ptr)
        .map(|p| unsafe { &mut *p })
        .unwrap();
      match self_mut.status.get() {
        ScopeStatus::Current { .. } => self_mut,
        _ => unreachable!(),
      }
    }

    /// Initializes the scope stack by creating a 'dummy' `ScopeData` at the
    /// very bottom. This makes it possible to store the freelist of reusable
    /// ScopeData objects even when no scope is entered.
    pub(crate) fn new_root(isolate: &mut Isolate) {
      let root = Box::leak(Self::boxed(isolate.into()));
      root.status = ScopeStatus::Current { zombie: false }.into();
      debug_assert!(isolate.get_current_scope_data().is_none());
      isolate.set_current_scope_data(Some(root.into()));
    }

    /// Activates and returns the 'root' `ScopeData` object that is created when
    /// the isolate is initialized. In order to do so, any zombie scopes that
    /// remain on the scope stack are cleaned up.
    ///
    /// # Panics
    ///
    /// This function panics if the root can't be activated because there are
    /// still other scopes on the stack and they're not zombies.
    pub(crate) fn get_root_mut(isolate: &mut Isolate) -> &mut Self {
      let mut current_scope_data = Self::get_current_mut(isolate);
      loop {
        current_scope_data = match current_scope_data {
          root if root.previous.is_none() => break root,
          data => data.try_exit_scope(),
        };
      }
    }

    /// Drops the scope stack and releases all `Box<ScopeData>` allocations.
    /// This function should be called only when an Isolate is being disposed.
    pub(crate) fn drop_root(isolate: &mut Isolate) {
      let root = Self::get_root_mut(isolate);
      unsafe { Box::from_raw(root) };
      isolate.set_current_scope_data(None);
    }

    pub(super) fn new_context_scope_data<'s>(
      &'s mut self,
      context: Local<'s, Context>,
    ) -> &'s mut Self {
      self.new_scope_data_with(move |data| {
        data.scope_type_specific_data.init_with(|| {
          ScopeTypeSpecificData::ContextScope {
            raw_context_scope: raw::ContextScope::new(context),
          }
        });
        data.context.set(Some(context.as_non_null()));
      })
    }

    /// Implementation helper function, which creates the raw `HandleScope`, but
    /// defers (maybe) entering a context to the provided callback argument.
    /// This function gets called by `Self::new_handle_scope_data()` and
    /// `Self::new_handle_scope_data_with_context()`.
    #[inline(always)]
    fn new_handle_scope_data_with<F>(&mut self, init_context_fn: F) -> &mut Self
    where
      F: FnOnce(
        NonNull<Isolate>,
        &mut Cell<Option<NonNull<Context>>>,
        &mut Option<raw::ContextScope>,
      ),
    {
      self.new_scope_data_with(|data| {
        let isolate = data.isolate;
        data.scope_type_specific_data.init_with(|| {
          ScopeTypeSpecificData::HandleScope {
            raw_handle_scope: unsafe { raw::HandleScope::uninit() },
            raw_context_scope: None,
          }
        });
        match &mut data.scope_type_specific_data {
          ScopeTypeSpecificData::HandleScope {
            raw_handle_scope,
            raw_context_scope,
          } => {
            unsafe { raw_handle_scope.init(isolate) };
            init_context_fn(isolate, &mut data.context, raw_context_scope);
          }
          _ => unreachable!(),
        };
      })
    }

    pub(super) fn new_handle_scope_data(&mut self) -> &mut Self {
      self.new_handle_scope_data_with(|_, _, raw_context_scope| {
        debug_assert!(raw_context_scope.is_none())
      })
    }

    pub(super) fn new_handle_scope_data_with_context(
      &mut self,
      context_ref: &Context,
    ) -> &mut Self {
      self.new_handle_scope_data_with(
        move |isolate, context_data, raw_context_scope| unsafe {
          let context_nn = NonNull::from(context_ref);
          // Copy the `Context` reference to a new local handle to enure that it
          // cannot get garbage collected until after this scope is dropped.
          let local_context_ptr =
            raw::v8__Local__New(isolate.as_ptr(), context_nn.cast().as_ptr())
              as *const Context;
          let local_context_nn =
            NonNull::new_unchecked(local_context_ptr as *mut _);
          let local_context = Local::from_non_null(local_context_nn);
          // Initialize the `raw::ContextScope`. This enters the context too.
          debug_assert!(raw_context_scope.is_none());
          ptr::write(
            raw_context_scope,
            Some(raw::ContextScope::new(local_context)),
          );
          // Also store the newly created `Local<Context>` in the `Cell` that
          // serves as a look-up cache for the current context.
          context_data.set(Some(local_context_nn));
        },
      )
    }

    pub(super) fn new_escapable_handle_scope_data(&mut self) -> &mut Self {
      self.new_scope_data_with(|data| {
        // Note: the `raw_escape_slot` field must be initialized _before_ the
        // `raw_handle_scope` field, otherwise the escaped local handle ends up
        // inside the `EscapableHandleScope` that's being constructed here,
        // rather than escaping from it.
        let isolate = data.isolate;
        data.scope_type_specific_data.init_with(|| {
          ScopeTypeSpecificData::EscapableHandleScope {
            raw_handle_scope: unsafe { raw::HandleScope::uninit() },
            raw_escape_slot: Some(raw::EscapeSlot::new(isolate)),
          }
        });
        match &mut data.scope_type_specific_data {
          ScopeTypeSpecificData::EscapableHandleScope {
            raw_handle_scope,
            raw_escape_slot,
          } => {
            unsafe { raw_handle_scope.init(isolate) };
            data.escape_slot.replace(raw_escape_slot.into());
          }
          _ => unreachable!(),
        }
      })
    }

    pub(super) fn new_try_catch_data(&mut self) -> &mut Self {
      self.new_scope_data_with(|data| {
        let isolate = data.isolate;
        data.scope_type_specific_data.init_with(|| {
          ScopeTypeSpecificData::TryCatch {
            raw_try_catch: unsafe { raw::TryCatch::uninit() },
          }
        });
        match &mut data.scope_type_specific_data {
          ScopeTypeSpecificData::TryCatch { raw_try_catch } => {
            unsafe { raw_try_catch.init(isolate) };
            data.try_catch.replace(raw_try_catch.into());
          }
          _ => unreachable!(),
        }
      })
    }

    pub(super) fn new_callback_scope_data<'s>(
      &'s mut self,
      maybe_current_context: Option<Local<'s, Context>>,
    ) -> &'s mut Self {
      self.new_scope_data_with(|data| {
        debug_assert!(data.scope_type_specific_data.is_none());
        data
          .context
          .set(maybe_current_context.map(|cx| cx.as_non_null()));
      })
    }

    fn new_scope_data_with(
      &mut self,
      init_fn: impl FnOnce(&mut Self),
    ) -> &mut Self {
      // Mark this scope (the parent of the newly created scope) as 'shadowed';
      self.status.set(match self.status.get() {
        ScopeStatus::Current { zombie } => ScopeStatus::Shadowed { zombie },
        _ => unreachable!(),
      });
      // Copy fields that that will be inherited by the new scope.
      let context = self.context.get().into();
      let escape_slot = self.escape_slot;
      // Initialize the `struct ScopeData` for the new scope.
      let new_scope_data = self.allocate_or_reuse_scope_data();
      // In debug builds, `zombie` is initially set to `true`, and the flag is
      // later cleared in the `as_scope()` method, to verify that we're
      // always creating exactly one scope from any `ScopeData` object.
      // For performance reasons this check is not performed in release builds.
      new_scope_data.status = Cell::new(ScopeStatus::Current {
        zombie: cfg!(debug_assertions),
      });
      // Store fields inherited from the parent scope.
      new_scope_data.context = context;
      new_scope_data.escape_slot = escape_slot;
      (init_fn)(new_scope_data);
      // Make the newly created scope the 'current' scope for this isolate.
      let new_scope_nn = unsafe { NonNull::new_unchecked(new_scope_data) };
      new_scope_data
        .get_isolate_mut()
        .set_current_scope_data(Some(new_scope_nn));
      new_scope_data
    }

    /// Either returns an free `Box<ScopeData>` that is available for reuse,
    /// or allocates a new one on the heap.
    fn allocate_or_reuse_scope_data(&mut self) -> &mut Self {
      let self_nn = NonNull::new(self);
      match &mut self.next {
        Some(next_box) => {
          // Reuse a free `Box<ScopeData>` allocation.
          debug_assert_eq!(next_box.isolate, self.isolate);
          debug_assert_eq!(next_box.previous, self_nn);
          debug_assert_eq!(next_box.status.get(), ScopeStatus::Free);
          debug_assert!(next_box.scope_type_specific_data.is_none());
          next_box.as_mut()
        }
        next_field @ None => {
          // Allocate a new `Box<ScopeData>`.
          let mut next_box = Self::boxed(self.isolate);
          next_box.previous = self_nn;
          next_field.replace(next_box);
          next_field.as_mut().unwrap()
        }
      }
    }

    pub(super) fn as_scope<S: Scope>(&mut self) -> S {
      assert_eq!(Layout::new::<&mut Self>(), Layout::new::<S>());
      // In debug builds, a new initialized `ScopeStatus` will have the `zombie`
      // flag set, so we have to reset it. In release builds, new `ScopeStatus`
      // objects come with the `zombie` flag cleared, so no update is necessary.
      if cfg!(debug_assertions) {
        assert_eq!(self.status.get(), ScopeStatus::Current { zombie: true });
        self.status.set(ScopeStatus::Current { zombie: false });
      }
      let self_nn = NonNull::from(self);
      unsafe { ptr::read(&self_nn as *const _ as *const S) }
    }

    pub(super) fn get<S: Scope>(scope: &S) -> &Self {
      let self_mut = unsafe {
        (*(scope as *const S as *mut S as *mut NonNull<Self>)).as_mut()
      };
      self_mut.try_activate_scope();
      self_mut
    }

    pub(super) fn get_mut<S: Scope>(scope: &mut S) -> &mut Self {
      let self_mut =
        unsafe { (*(scope as *mut S as *mut NonNull<Self>)).as_mut() };
      self_mut.try_activate_scope();
      self_mut
    }

    #[inline(always)]
    fn try_activate_scope(mut self: &mut Self) -> &mut Self {
      self = match self.status.get() {
        ScopeStatus::Current { zombie: false } => self,
        ScopeStatus::Shadowed { zombie: false } => {
          self.next.as_mut().unwrap().try_exit_scope()
        }
        _ => unreachable!(),
      };
      debug_assert_eq!(
        self.get_isolate().get_current_scope_data(),
        NonNull::new(self as *mut _)
      );
      self
    }

    fn try_exit_scope(mut self: &mut Self) -> &mut Self {
      loop {
        self = match self.status.get() {
          ScopeStatus::Shadowed { .. } => {
            self.next.as_mut().unwrap().try_exit_scope()
          }
          ScopeStatus::Current { zombie: true } => break self.exit_scope(),
          ScopeStatus::Current { zombie: false } => {
            panic!("active scope can't be dropped")
          }
          _ => unreachable!(),
        }
      }
    }

    fn exit_scope(&mut self) -> &mut Self {
      // Clear out the scope type specific data field. None of the other fields
      // have a destructor, and there's no need to do any cleanup on them.
      self.scope_type_specific_data = Default::default();
      // Change the ScopeData's status field from 'Current' to 'Free', which
      // means that it is not associated with a scope and can be reused.
      self.status.set(ScopeStatus::Free);

      // Point the Isolate's current scope data slot at our parent scope.
      let previous_nn = self.previous.unwrap();
      self
        .get_isolate_mut()
        .set_current_scope_data(Some(previous_nn));
      // Update the parent scope's status field to reflect that it is now
      // 'Current' again an no longer 'Shadowed'.
      let previous_mut = unsafe { &mut *previous_nn.as_ptr() };
      previous_mut.status.set(match previous_mut.status.get() {
        ScopeStatus::Shadowed { zombie } => ScopeStatus::Current { zombie },
        _ => unreachable!(),
      });

      previous_mut
    }

    /// This function is called when any of the public scope objects (e.g
    /// `HandleScope`, `ContextScope`, etc.) are dropped.
    ///
    /// The Rust borrow checker allows values of type `HandleScope<'a>` and
    /// `EscapableHandleScope<'a, 'e>` to be dropped before their maximum
    /// lifetime ('a) is up. This creates a potential problem because any local
    /// handles that are created while these scopes are active are bound to
    /// that 'a lifetime. This means that we run the risk of creating local
    /// handles that outlive their creation scope.
    ///
    /// Therefore, we don't immediately exit the current scope at the very
    /// moment the user drops their Escapable/HandleScope handle.
    /// Instead, the current scope is marked as being a 'zombie': the scope
    /// itself is gone, but its data still on the stack. The zombie's data will
    /// be dropped when the user touches the parent scope; when that happens, it
    /// is certain that there are no accessible `Local<'a, T>` handles left,
    /// because the 'a lifetime ends there.
    ///
    /// Scope types that do no store local handles are exited immediately.
    pub(super) fn notify_scope_dropped(&mut self) {
      match &self.scope_type_specific_data {
        ScopeTypeSpecificData::HandleScope { .. }
        | ScopeTypeSpecificData::EscapableHandleScope { .. } => {
          // Defer scope exit until the parent scope is touched.
          self.status.set(match self.status.get() {
            ScopeStatus::Current { zombie: false } => {
              ScopeStatus::Current { zombie: true }
            }
            _ => unreachable!(),
          })
        }
        _ => {
          // Regular, immediate exit.
          self.exit_scope();
        }
      }
    }

    pub(crate) fn get_isolate(&self) -> &Isolate {
      unsafe { self.isolate.as_ref() }
    }

    pub(crate) fn get_isolate_mut(&mut self) -> &mut Isolate {
      unsafe { self.isolate.as_mut() }
    }

    pub(crate) fn get_isolate_ptr(&self) -> *mut Isolate {
      self.isolate.as_ptr()
    }

    pub(crate) fn get_current_context(&self) -> *const Context {
      // To avoid creating a new Local every time `get_current_context() is
      // called, the current context is usually cached in the `context` field.
      // If the `context` field contains `None`, this might mean that this cache
      // field never got populated, so we'll do that here when necessary.
      let get_current_context_from_isolate = || unsafe {
        raw::v8__Isolate__GetCurrentContext(self.get_isolate_ptr())
      };
      match self.context.get().map(|nn| nn.as_ptr() as *const _) {
        Some(context) => {
          debug_assert!(unsafe {
            raw::v8__Context__EQ(context, get_current_context_from_isolate())
          });
          context
        }
        None => {
          let context = get_current_context_from_isolate();
          self.context.set(NonNull::new(context as *mut _));
          context
        }
      }
    }

    pub(super) fn get_escape_slot_mut(
      &mut self,
    ) -> Option<&mut Option<raw::EscapeSlot>> {
      self
        .escape_slot
        .as_mut()
        .map(|escape_slot_nn| unsafe { escape_slot_nn.as_mut() })
    }

    pub(super) fn get_try_catch(&self) -> &raw::TryCatch {
      self
        .try_catch
        .as_ref()
        .map(|try_catch_nn| unsafe { try_catch_nn.as_ref() })
        .unwrap()
    }

    pub(super) fn get_try_catch_mut(&mut self) -> &mut raw::TryCatch {
      self
        .try_catch
        .as_mut()
        .map(|try_catch_nn| unsafe { try_catch_nn.as_mut() })
        .unwrap()
    }

    /// Returns a new `Box<ScopeData>` with the `isolate` field set as specified
    /// by the first parameter, and the other fields initialized to their
    /// default values. This function exists solely because it turns out that
    /// Rust doesn't optimize `Box::new(Self{ .. })` very well (a.k.a. not at
    /// all) in this case, which is why `std::alloc::alloc()` is used directly.
    fn boxed(isolate: NonNull<Isolate>) -> Box<Self> {
      unsafe {
        #[allow(clippy::cast_ptr_alignment)]
        let self_ptr = alloc(Layout::new::<Self>()) as *mut Self;
        ptr::write(
          self_ptr,
          Self {
            isolate,
            previous: Default::default(),
            next: Default::default(),
            status: Default::default(),
            context: Default::default(),
            escape_slot: Default::default(),
            try_catch: Default::default(),
            scope_type_specific_data: Default::default(),
          },
        );
        Box::from_raw(self_ptr)
      }
    }
  }

  #[derive(Debug, Clone, Copy, Eq, PartialEq)]
  enum ScopeStatus {
    Free,
    Current { zombie: bool },
    Shadowed { zombie: bool },
  }

  impl Default for ScopeStatus {
    fn default() -> Self {
      Self::Free
    }
  }

  #[derive(Debug)]
  enum ScopeTypeSpecificData {
    None,
    ContextScope {
      raw_context_scope: raw::ContextScope,
    },
    HandleScope {
      raw_handle_scope: raw::HandleScope,
      raw_context_scope: Option<raw::ContextScope>,
    },
    EscapableHandleScope {
      raw_handle_scope: raw::HandleScope,
      raw_escape_slot: Option<raw::EscapeSlot>,
    },
    TryCatch {
      raw_try_catch: raw::TryCatch,
    },
  }

  impl Default for ScopeTypeSpecificData {
    fn default() -> Self {
      Self::None
    }
  }

  impl Drop for ScopeTypeSpecificData {
    fn drop(&mut self) {
      // For `HandleScope`s that also enter a `Context`, drop order matters. The
      // context is stored in a `Local` handle, which is allocated in this
      // scope's own private `raw::HandleScope`. When that `raw::HandleScope`
      // is dropped first, we immediately lose the `Local<Context>` handle,
      // which we need in order to exit `ContextScope`.
      if let Self::HandleScope {
        raw_context_scope, ..
      } = self
      {
        *raw_context_scope = None
      }
    }
  }

  impl ScopeTypeSpecificData {
    pub fn is_none(&self) -> bool {
      matches!(self, Self::None)
    }

    /// Replaces a `ScopeTypeSpecificData::None` value with the value returned
    /// from the specified closure. This function exists because initializing
    /// scopes is performance critical, and `ptr::write()` produces more
    /// efficient code than using a regular assign statement, which will try to
    /// drop the old value and move the new value into place, even after
    /// asserting `self.is_none()`.
    pub fn init_with(&mut self, init_fn: impl FnOnce() -> Self) {
      assert!(self.is_none());
      unsafe { ptr::write(self, (init_fn)()) }
    }
  }
}

/// The `raw` module contains prototypes for all the `extern C` functions that
/// are used in this file, as well as definitions for the types they operate on.
mod raw {
  use super::*;

  #[derive(Clone, Copy, Debug)]
  #[repr(transparent)]
  pub(super) struct Address(NonZeroUsize);

  #[derive(Debug)]
  pub(super) struct ContextScope {
    entered_context: NonNull<Context>,
  }

  impl ContextScope {
    pub fn new(context: Local<Context>) -> Self {
      unsafe { v8__Context__Enter(&*context) };
      Self {
        entered_context: context.as_non_null(),
      }
    }
  }

  impl Drop for ContextScope {
    fn drop(&mut self) {
      unsafe { v8__Context__Exit(self.entered_context.as_ptr()) };
    }
  }

  #[repr(C)]
  #[derive(Debug)]
  pub(super) struct HandleScope([usize; 3]);

  impl HandleScope {
    /// This function is marked unsafe because the caller must ensure that the
    /// returned value isn't dropped before `init()` has been called.
    pub unsafe fn uninit() -> Self {
      // This is safe because there is no combination of bits that would produce
      // an invalid `[usize; 3]`.
      #[allow(clippy::uninit_assumed_init)]
      Self(MaybeUninit::uninit().assume_init())
    }

    /// This function is marked unsafe because `init()` must be called exactly
    /// once, no more and no less, after creating a `HandleScope` value with
    /// `HandleScope::uninit()`.
    pub unsafe fn init(&mut self, isolate: NonNull<Isolate>) {
      let buf = NonNull::from(self).cast();
      v8__HandleScope__CONSTRUCT(buf.as_ptr(), isolate.as_ptr());
    }
  }

  impl Drop for HandleScope {
    fn drop(&mut self) {
      unsafe { v8__HandleScope__DESTRUCT(self) };
    }
  }

  #[repr(transparent)]
  #[derive(Debug)]
  pub(super) struct EscapeSlot(NonNull<raw::Address>);

  impl EscapeSlot {
    pub fn new(isolate: NonNull<Isolate>) -> Self {
      unsafe {
        let undefined = raw::v8__Undefined(isolate.as_ptr()) as *const _;
        let local = raw::v8__Local__New(isolate.as_ptr(), undefined);
        let slot_address_ptr = local as *const Address as *mut _;
        let slot_address_nn = NonNull::new_unchecked(slot_address_ptr);
        Self(slot_address_nn)
      }
    }

    pub fn escape<'e, T>(self, value: Local<'_, T>) -> Local<'e, T>
    where
      for<'l> Local<'l, T>: Into<Local<'l, Data>>,
    {
      assert_eq!(Layout::new::<Self>(), Layout::new::<Local<T>>());
      unsafe {
        let undefined = Local::<Value>::from_non_null(self.0.cast());
        debug_assert!(undefined.is_undefined());
        let value_address = *(&*value as *const T as *const Address);
        ptr::write(self.0.as_ptr(), value_address);
        Local::from_non_null(self.0.cast())
      }
    }
  }

  #[repr(C)]
  #[derive(Debug)]
  pub(super) struct TryCatch([usize; 6]);

  impl TryCatch {
    /// This function is marked unsafe because the caller must ensure that the
    /// returned value isn't dropped before `init()` has been called.
    pub unsafe fn uninit() -> Self {
      // This is safe because there is no combination of bits that would produce
      // an invalid `[usize; 6]`.
      #[allow(clippy::uninit_assumed_init)]
      Self(MaybeUninit::uninit().assume_init())
    }

    /// This function is marked unsafe because `init()` must be called exactly
    /// once, no more and no less, after creating a `TryCatch` value with
    /// `TryCatch::uninit()`.
    pub unsafe fn init(&mut self, isolate: NonNull<Isolate>) {
      let buf = NonNull::from(self).cast();
      v8__TryCatch__CONSTRUCT(buf.as_ptr(), isolate.as_ptr());
    }
  }

  impl Drop for TryCatch {
    fn drop(&mut self) {
      unsafe { v8__TryCatch__DESTRUCT(self) };
    }
  }

  extern "C" {
    pub(super) fn v8__Isolate__GetCurrentContext(
      isolate: *mut Isolate,
    ) -> *const Context;
    pub(super) fn v8__Isolate__GetEnteredOrMicrotaskContext(
      isolate: *mut Isolate,
    ) -> *const Context;
    pub(super) fn v8__Isolate__ThrowException(
      isolate: *mut Isolate,
      exception: *const Value,
    ) -> *const Value;
    pub(super) fn v8__Isolate__GetDataFromSnapshotOnce(
      this: *mut Isolate,
      index: usize,
    ) -> *const Data;

    pub(super) fn v8__Context__EQ(
      this: *const Context,
      other: *const Context,
    ) -> bool;
    pub(super) fn v8__Context__Enter(this: *const Context);
    pub(super) fn v8__Context__Exit(this: *const Context);
    pub(super) fn v8__Context__GetIsolate(this: *const Context)
      -> *mut Isolate;
    pub(super) fn v8__Context__GetDataFromSnapshotOnce(
      this: *const Context,
      index: usize,
    ) -> *const Data;

    pub(super) fn v8__HandleScope__CONSTRUCT(
      buf: *mut MaybeUninit<HandleScope>,
      isolate: *mut Isolate,
    );
    pub(super) fn v8__HandleScope__DESTRUCT(this: *mut HandleScope);

    pub(super) fn v8__Local__New(
      isolate: *mut Isolate,
      other: *const Data,
    ) -> *const Data;
    pub(super) fn v8__Undefined(isolate: *mut Isolate) -> *const Primitive;

    pub(super) fn v8__TryCatch__CONSTRUCT(
      buf: *mut MaybeUninit<TryCatch>,
      isolate: *mut Isolate,
    );
    pub(super) fn v8__TryCatch__DESTRUCT(this: *mut TryCatch);
    pub(super) fn v8__TryCatch__HasCaught(this: *const TryCatch) -> bool;
    pub(super) fn v8__TryCatch__CanContinue(this: *const TryCatch) -> bool;
    pub(super) fn v8__TryCatch__HasTerminated(this: *const TryCatch) -> bool;
    pub(super) fn v8__TryCatch__IsVerbose(this: *const TryCatch) -> bool;
    pub(super) fn v8__TryCatch__SetVerbose(this: *mut TryCatch, value: bool);
    pub(super) fn v8__TryCatch__SetCaptureMessage(
      this: *mut TryCatch,
      value: bool,
    );
    pub(super) fn v8__TryCatch__Reset(this: *mut TryCatch);
    pub(super) fn v8__TryCatch__Exception(
      this: *const TryCatch,
    ) -> *const Value;
    pub(super) fn v8__TryCatch__StackTrace(
      this: *const TryCatch,
      context: *const Context,
    ) -> *const Value;
    pub(super) fn v8__TryCatch__Message(
      this: *const TryCatch,
    ) -> *const Message;
    pub(super) fn v8__TryCatch__ReThrow(this: *mut TryCatch) -> *const Value;

    pub(super) fn v8__Message__GetIsolate(this: *const Message)
      -> *mut Isolate;
    pub(super) fn v8__Object__GetIsolate(this: *const Object) -> *mut Isolate;
    pub(super) fn v8__FunctionCallbackInfo__GetIsolate(
      this: *const FunctionCallbackInfo,
    ) -> *mut Isolate;
    pub(super) fn v8__PropertyCallbackInfo__GetIsolate(
      this: *const PropertyCallbackInfo,
    ) -> *mut Isolate;
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::new_default_platform;
  use crate::Global;
  use crate::V8;
  use std::any::type_name;
  use std::sync::Once;

  trait SameType {}
  impl<A> SameType for (A, A) {}

  /// `AssertTypeOf` facilitates comparing types. The important difference with
  /// assigning a value to a variable with an explicitly stated type is that the
  /// latter allows coercions and dereferencing to change the type, whereas
  /// `AssertTypeOf` requires the compared types to match exactly.
  struct AssertTypeOf<'a, T>(pub &'a T);
  impl<'a, T> AssertTypeOf<'a, T> {
    pub fn is<A>(self)
    where
      (A, T): SameType,
    {
      assert_eq!(type_name::<A>(), type_name::<T>());
    }
  }

  fn initialize_v8() {
    static INIT: Once = Once::new();
    INIT.call_once(|| {
      V8::initialize_platform(new_default_platform(0, false).make_shared());
      V8::initialize();
    });
  }

  #[test]
  fn deref_types() {
    initialize_v8();
    let isolate = &mut Isolate::new(Default::default());
    AssertTypeOf(isolate).is::<OwnedIsolate>();
    let l1_hs = &mut HandleScope::new(isolate);
    AssertTypeOf(l1_hs).is::<HandleScope<()>>();
    let context = Context::new(l1_hs);
    {
      let l2_cxs = &mut ContextScope::new(l1_hs, context);
      AssertTypeOf(l2_cxs).is::<ContextScope<HandleScope>>();
      {
        let d = l2_cxs.deref_mut();
        AssertTypeOf(d).is::<HandleScope>();
        let d = d.deref_mut();
        AssertTypeOf(d).is::<HandleScope<()>>();
        let d = d.deref_mut();
        AssertTypeOf(d).is::<Isolate>();
      }
      {
        let l3_tc = &mut TryCatch::new(l2_cxs);
        AssertTypeOf(l3_tc).is::<TryCatch<HandleScope>>();
        let d = l3_tc.deref_mut();
        AssertTypeOf(d).is::<HandleScope>();
        let d = d.deref_mut();
        AssertTypeOf(d).is::<HandleScope<()>>();
        let d = d.deref_mut();
        AssertTypeOf(d).is::<Isolate>();
      }
      {
        let l3_ehs = &mut EscapableHandleScope::new(l2_cxs);
        AssertTypeOf(l3_ehs).is::<EscapableHandleScope>();
        {
          let l4_cxs = &mut ContextScope::new(l3_ehs, context);
          AssertTypeOf(l4_cxs).is::<ContextScope<EscapableHandleScope>>();
          let d = l4_cxs.deref_mut();
          AssertTypeOf(d).is::<EscapableHandleScope>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<HandleScope>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<HandleScope<()>>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<Isolate>();
        }
        {
          let l4_tc = &mut TryCatch::new(l3_ehs);
          AssertTypeOf(l4_tc).is::<TryCatch<EscapableHandleScope>>();
          let d = l4_tc.deref_mut();
          AssertTypeOf(d).is::<EscapableHandleScope>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<HandleScope>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<HandleScope<()>>();
          let d = d.deref_mut();
          AssertTypeOf(d).is::<Isolate>();
        }
      }
    }
    {
      let l2_tc = &mut TryCatch::new(l1_hs);
      AssertTypeOf(l2_tc).is::<TryCatch<HandleScope<()>>>();
      let d = l2_tc.deref_mut();
      AssertTypeOf(d).is::<HandleScope<()>>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<Isolate>();
    }
    {
      let l2_ehs = &mut EscapableHandleScope::new(l1_hs);
      AssertTypeOf(l2_ehs).is::<EscapableHandleScope<()>>();
      let l3_tc = &mut TryCatch::new(l2_ehs);
      AssertTypeOf(l3_tc).is::<TryCatch<EscapableHandleScope<()>>>();
      let d = l3_tc.deref_mut();
      AssertTypeOf(d).is::<EscapableHandleScope<()>>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<HandleScope<()>>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<Isolate>();
    }
    {
      // `CallbackScope` is meant to be used inside V8 API callback functions
      // only. It assumes that a `HandleScope` already exists on the stack, and
      // that a context has been entered. Push a `ContextScope` onto the stack
      // to also meet the second expectation.
      let _ = ContextScope::new(l1_hs, context);
      let l2_cbs = &mut unsafe { CallbackScope::new(context) };
      AssertTypeOf(l2_cbs).is::<CallbackScope>();
      let d = l2_cbs.deref_mut();
      AssertTypeOf(d).is::<HandleScope>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<HandleScope<()>>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<Isolate>();
    }
    {
      let isolate: &mut Isolate = l1_hs.as_mut();
      let l2_cbs = &mut unsafe { CallbackScope::new(isolate) };
      AssertTypeOf(l2_cbs).is::<CallbackScope<()>>();
      let d = l2_cbs.deref_mut();
      AssertTypeOf(d).is::<HandleScope<()>>();
      let d = d.deref_mut();
      AssertTypeOf(d).is::<Isolate>();
    }
  }

  #[test]
  fn new_scope_types() {
    initialize_v8();
    let isolate = &mut Isolate::new(Default::default());
    AssertTypeOf(isolate).is::<OwnedIsolate>();
    let global_context: Global<Context>;
    {
      let l1_hs = &mut HandleScope::new(isolate);
      AssertTypeOf(l1_hs).is::<HandleScope<()>>();
      let context = Context::new(l1_hs);
      global_context = Global::new(l1_hs, context);
      AssertTypeOf(&HandleScope::new(l1_hs)).is::<HandleScope<()>>();
      {
        let l2_cxs = &mut ContextScope::new(l1_hs, context);
        AssertTypeOf(l2_cxs).is::<ContextScope<HandleScope>>();
        AssertTypeOf(&ContextScope::new(l2_cxs, context))
          .is::<ContextScope<HandleScope>>();
        AssertTypeOf(&HandleScope::new(l2_cxs)).is::<HandleScope>();
        AssertTypeOf(&EscapableHandleScope::new(l2_cxs))
          .is::<EscapableHandleScope>();
        AssertTypeOf(&TryCatch::new(l2_cxs)).is::<TryCatch<HandleScope>>();
      }
      {
        let l2_ehs = &mut EscapableHandleScope::new(l1_hs);
        AssertTypeOf(l2_ehs).is::<EscapableHandleScope<()>>();
        AssertTypeOf(&HandleScope::new(l2_ehs))
          .is::<EscapableHandleScope<()>>();
        AssertTypeOf(&EscapableHandleScope::new(l2_ehs))
          .is::<EscapableHandleScope<()>>();
        {
          let l3_cxs = &mut ContextScope::new(l2_ehs, context);
          AssertTypeOf(l3_cxs).is::<ContextScope<EscapableHandleScope>>();
          AssertTypeOf(&ContextScope::new(l3_cxs, context))
            .is::<ContextScope<EscapableHandleScope>>();
          AssertTypeOf(&HandleScope::new(l3_cxs)).is::<EscapableHandleScope>();
          AssertTypeOf(&EscapableHandleScope::new(l3_cxs))
            .is::<EscapableHandleScope>();
          {
            let l4_tc = &mut TryCatch::new(l3_cxs);
            AssertTypeOf(l4_tc).is::<TryCatch<EscapableHandleScope>>();
            AssertTypeOf(&ContextScope::new(l4_tc, context))
              .is::<ContextScope<EscapableHandleScope>>();
            AssertTypeOf(&HandleScope::new(l4_tc)).is::<EscapableHandleScope>();
            AssertTypeOf(&EscapableHandleScope::new(l4_tc))
              .is::<EscapableHandleScope>();
            AssertTypeOf(&TryCatch::new(l4_tc))
              .is::<TryCatch<EscapableHandleScope>>();
          }
        }
        {
          let l3_tc = &mut TryCatch::new(l2_ehs);
          AssertTypeOf(l3_tc).is::<TryCatch<EscapableHandleScope<()>>>();
          AssertTypeOf(&ContextScope::new(l3_tc, context))
            .is::<ContextScope<EscapableHandleScope>>();
          AssertTypeOf(&HandleScope::new(l3_tc))
            .is::<EscapableHandleScope<()>>();
          AssertTypeOf(&EscapableHandleScope::new(l3_tc))
            .is::<EscapableHandleScope<()>>();
          AssertTypeOf(&TryCatch::new(l3_tc))
            .is::<TryCatch<EscapableHandleScope<()>>>();
        }
      }
      {
        let l2_tc = &mut TryCatch::new(l1_hs);
        AssertTypeOf(l2_tc).is::<TryCatch<HandleScope<()>>>();
        AssertTypeOf(&ContextScope::new(l2_tc, context))
          .is::<ContextScope<HandleScope>>();
        AssertTypeOf(&HandleScope::new(l2_tc)).is::<HandleScope<()>>();
        AssertTypeOf(&EscapableHandleScope::new(l2_tc))
          .is::<EscapableHandleScope<()>>();
        AssertTypeOf(&TryCatch::new(l2_tc)).is::<TryCatch<HandleScope<()>>>();
      }
      {
        let l2_cbs = &mut unsafe { CallbackScope::new(context) };
        AssertTypeOf(l2_cbs).is::<CallbackScope>();
        AssertTypeOf(&ContextScope::new(l2_cbs, context))
          .is::<ContextScope<HandleScope>>();
        {
          let l3_hs = &mut HandleScope::new(l2_cbs);
          AssertTypeOf(l3_hs).is::<HandleScope>();
          AssertTypeOf(&ContextScope::new(l3_hs, context))
            .is::<ContextScope<HandleScope>>();
          AssertTypeOf(&HandleScope::new(l3_hs)).is::<HandleScope>();
          AssertTypeOf(&EscapableHandleScope::new(l3_hs))
            .is::<EscapableHandleScope>();
          AssertTypeOf(&TryCatch::new(l3_hs)).is::<TryCatch<HandleScope>>();
        }
        {
          let l3_ehs = &mut EscapableHandleScope::new(l2_cbs);
          AssertTypeOf(l3_ehs).is::<EscapableHandleScope>();
          AssertTypeOf(&ContextScope::new(l3_ehs, context))
            .is::<ContextScope<EscapableHandleScope>>();
          AssertTypeOf(&HandleScope::new(l3_ehs)).is::<EscapableHandleScope>();
          AssertTypeOf(&EscapableHandleScope::new(l3_ehs))
            .is::<EscapableHandleScope>();
          AssertTypeOf(&TryCatch::new(l3_ehs))
            .is::<TryCatch<EscapableHandleScope>>();
        }
        {
          let l3_tc = &mut TryCatch::new(l2_cbs);
          AssertTypeOf(l3_tc).is::<TryCatch<HandleScope>>();
          AssertTypeOf(&ContextScope::new(l3_tc, context))
            .is::<ContextScope<HandleScope>>();
          AssertTypeOf(&HandleScope::new(l3_tc)).is::<HandleScope>();
          AssertTypeOf(&EscapableHandleScope::new(l3_tc))
            .is::<EscapableHandleScope>();
          AssertTypeOf(&TryCatch::new(l3_tc)).is::<TryCatch<HandleScope>>();
        }
      }
    }
    {
      let l1_cbs = &mut unsafe { CallbackScope::new(&mut *isolate) };
      AssertTypeOf(l1_cbs).is::<CallbackScope<()>>();
      let context = Context::new(l1_cbs);
      AssertTypeOf(&ContextScope::new(l1_cbs, context))
        .is::<ContextScope<HandleScope>>();
      AssertTypeOf(&HandleScope::new(l1_cbs)).is::<HandleScope<()>>();
      AssertTypeOf(&EscapableHandleScope::new(l1_cbs))
        .is::<EscapableHandleScope<()>>();
      AssertTypeOf(&TryCatch::new(l1_cbs)).is::<TryCatch<HandleScope<()>>>();
    }
    {
      AssertTypeOf(&HandleScope::with_context(isolate, &global_context))
        .is::<HandleScope>();
      AssertTypeOf(&HandleScope::with_context(isolate, global_context))
        .is::<HandleScope>();
    }
  }
}