mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-12-26 09:13:46 -05:00
1965 lines
70 KiB
Rust
1965 lines
70 KiB
Rust
// 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([MaybeUninit<usize>; 3]);
|
|
|
|
impl HandleScope {
|
|
/// Creates an uninitialized `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 {
|
|
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([MaybeUninit<usize>; 6]);
|
|
|
|
impl TryCatch {
|
|
/// Creates an uninitialized `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 {
|
|
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>();
|
|
}
|
|
}
|
|
}
|