v8/
snapshot.rs

1use crate::Context;
2use crate::Data;
3use crate::Local;
4use crate::OwnedIsolate;
5use crate::external_references::ExternalReference;
6use crate::isolate::RealIsolate;
7use crate::isolate_create_params::raw;
8use crate::support::char;
9use crate::support::int;
10
11use std::borrow::Cow;
12use std::mem::MaybeUninit;
13
14unsafe extern "C" {
15  fn v8__SnapshotCreator__CONSTRUCT(
16    buf: *mut MaybeUninit<SnapshotCreator>,
17    params: *const raw::CreateParams,
18  );
19  fn v8__SnapshotCreator__DESTRUCT(this: *mut SnapshotCreator);
20  fn v8__SnapshotCreator__GetIsolate(
21    this: *const SnapshotCreator,
22  ) -> *mut RealIsolate;
23  fn v8__SnapshotCreator__CreateBlob(
24    this: *mut SnapshotCreator,
25    function_code_handling: FunctionCodeHandling,
26  ) -> RawStartupData;
27  fn v8__SnapshotCreator__SetDefaultContext(
28    this: *mut SnapshotCreator,
29    context: *const Context,
30  );
31  fn v8__SnapshotCreator__AddContext(
32    this: *mut SnapshotCreator,
33    context: *const Context,
34  ) -> usize;
35  fn v8__SnapshotCreator__AddData_to_isolate(
36    this: *mut SnapshotCreator,
37    data: *const Data,
38  ) -> usize;
39  fn v8__SnapshotCreator__AddData_to_context(
40    this: *mut SnapshotCreator,
41    context: *const Context,
42    data: *const Data,
43  ) -> usize;
44  fn v8__StartupData__CanBeRehashed(this: *const RawStartupData) -> bool;
45  fn v8__StartupData__IsValid(this: *const RawStartupData) -> bool;
46  fn v8__StartupData__data__DELETE(this: *const char);
47}
48
49#[repr(C)]
50#[derive(Debug, Clone, Copy)]
51pub(crate) struct RawStartupData {
52  pub(crate) data: *const char,
53  pub(crate) raw_size: int,
54}
55
56#[derive(Debug, Clone)]
57pub struct StartupData(StartupDataInner);
58
59#[derive(Debug)]
60enum StartupDataInner {
61  Cpp(RawStartupData),
62  Cow(Cow<'static, [u8]>),
63}
64
65impl StartupData {
66  /// Whether the data created can be rehashed and and the hash seed can be
67  /// recomputed when deserialized.
68  /// Only valid for StartupData returned by SnapshotCreator::CreateBlob().
69  pub fn can_be_rehashed(&self) -> bool {
70    let tmp = match &self.0 {
71      StartupDataInner::Cpp(t) => *t,
72      StartupDataInner::Cow(c) => RawStartupData {
73        data: c.as_ptr() as _,
74        raw_size: c.len() as _,
75      },
76    };
77    unsafe { v8__StartupData__CanBeRehashed(&tmp) }
78  }
79
80  /// Allows embedders to verify whether the data is valid for the current
81  /// V8 instance.
82  pub fn is_valid(&self) -> bool {
83    let tmp = match &self.0 {
84      StartupDataInner::Cpp(t) => *t,
85      StartupDataInner::Cow(c) => RawStartupData {
86        data: c.as_ptr() as _,
87        raw_size: c.len() as _,
88      },
89    };
90    unsafe { v8__StartupData__IsValid(&tmp) }
91  }
92}
93
94impl std::ops::Deref for StartupData {
95  type Target = [u8];
96
97  fn deref(&self) -> &Self::Target {
98    match &self.0 {
99      StartupDataInner::Cpp(t) => unsafe {
100        std::slice::from_raw_parts(t.data as _, t.raw_size as _)
101      },
102      StartupDataInner::Cow(c) => c,
103    }
104  }
105}
106
107impl<T> From<T> for StartupData
108where
109  T: Into<Cow<'static, [u8]>>,
110{
111  fn from(value: T) -> Self {
112    Self(StartupDataInner::Cow(value.into()))
113  }
114}
115
116impl Drop for StartupData {
117  fn drop(&mut self) {
118    if let StartupDataInner::Cpp(raw) = self.0 {
119      unsafe {
120        v8__StartupData__data__DELETE(raw.data);
121      }
122    }
123  }
124}
125
126impl Clone for StartupDataInner {
127  fn clone(&self) -> Self {
128    match self {
129      // Cpp -> Cow to maintain unique ownership of the underlying pointer
130      Self::Cpp(r) => Self::Cow(
131        unsafe { std::slice::from_raw_parts(r.data as _, r.raw_size as _) }
132          .to_vec()
133          .into(),
134      ),
135      Self::Cow(c) => Self::Cow(c.clone()),
136    }
137  }
138}
139
140#[repr(C)]
141#[derive(Debug)]
142pub enum FunctionCodeHandling {
143  Clear,
144  Keep,
145}
146
147/// Helper class to create a snapshot data blob.
148#[repr(C)]
149#[derive(Debug)]
150pub(crate) struct SnapshotCreator([usize; 1]);
151
152impl SnapshotCreator {
153  /// Create an isolate, and set it up for serialization.
154  /// The isolate is created from scratch.
155  #[inline(always)]
156  #[allow(clippy::new_ret_no_self)]
157  pub(crate) fn new(
158    external_references: Option<Cow<'static, [ExternalReference]>>,
159    params: Option<crate::CreateParams>,
160  ) -> OwnedIsolate {
161    Self::new_impl(external_references, None, params)
162  }
163
164  /// Create an isolate, and set it up for serialization.
165  /// The isolate is created from scratch.
166  #[inline(always)]
167  #[allow(clippy::new_ret_no_self)]
168  pub(crate) fn from_existing_snapshot(
169    existing_snapshot_blob: StartupData,
170    external_references: Option<Cow<'static, [ExternalReference]>>,
171    params: Option<crate::CreateParams>,
172  ) -> OwnedIsolate {
173    Self::new_impl(external_references, Some(existing_snapshot_blob), params)
174  }
175
176  /// Create and enter an isolate, and set it up for serialization.
177  /// The isolate is created from scratch.
178  #[inline(always)]
179  #[allow(clippy::new_ret_no_self)]
180  fn new_impl(
181    external_references: Option<Cow<'static, [ExternalReference]>>,
182    existing_snapshot_blob: Option<StartupData>,
183    params: Option<crate::CreateParams>,
184  ) -> OwnedIsolate {
185    let mut snapshot_creator: MaybeUninit<Self> = MaybeUninit::uninit();
186
187    let mut params = params.unwrap_or_default();
188    if let Some(external_refs) = external_references {
189      params = params.external_references(external_refs);
190    }
191    if let Some(snapshot_blob) = existing_snapshot_blob {
192      params = params.snapshot_blob(snapshot_blob);
193    }
194    let (raw_create_params, create_param_allocations) = params.finalize();
195
196    let snapshot_creator = unsafe {
197      v8__SnapshotCreator__CONSTRUCT(&mut snapshot_creator, &raw_create_params);
198      snapshot_creator.assume_init()
199    };
200
201    let isolate_ptr =
202      unsafe { v8__SnapshotCreator__GetIsolate(&snapshot_creator) };
203    let mut owned_isolate = OwnedIsolate::new_already_entered(isolate_ptr);
204    owned_isolate.initialize(create_param_allocations);
205    owned_isolate.set_snapshot_creator(snapshot_creator);
206    owned_isolate
207  }
208}
209
210impl Drop for SnapshotCreator {
211  fn drop(&mut self) {
212    unsafe { v8__SnapshotCreator__DESTRUCT(self) };
213  }
214}
215
216impl SnapshotCreator {
217  /// Set the default context to be included in the snapshot blob.
218  /// The snapshot will not contain the global proxy, and we expect one or a
219  /// global object template to create one, to be provided upon deserialization.
220  #[inline(always)]
221  pub(crate) fn set_default_context(&mut self, context: Local<Context>) {
222    unsafe { v8__SnapshotCreator__SetDefaultContext(self, &*context) };
223  }
224
225  /// Add additional context to be included in the snapshot blob.
226  /// The snapshot will include the global proxy.
227  ///
228  /// Returns the index of the context in the snapshot blob.
229  #[inline(always)]
230  pub(crate) fn add_context(&mut self, context: Local<Context>) -> usize {
231    unsafe { v8__SnapshotCreator__AddContext(self, &*context) }
232  }
233
234  /// Attach arbitrary `v8::Data` to the isolate snapshot, which can be
235  /// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
236  /// deserialization. This data does not survive when a new snapshot is created
237  /// from an existing snapshot.
238  #[inline(always)]
239  pub(crate) fn add_isolate_data<T>(&mut self, data: Local<T>) -> usize
240  where
241    for<'l> Local<'l, T>: Into<Local<'l, Data>>,
242  {
243    unsafe { v8__SnapshotCreator__AddData_to_isolate(self, &*data.into()) }
244  }
245
246  /// Attach arbitrary `v8::Data` to the context snapshot, which can be
247  /// retrieved via `HandleScope::get_context_data_from_snapshot_once()` after
248  /// deserialization. This data does not survive when a new snapshot is
249  /// created from an existing snapshot.
250  #[inline(always)]
251  pub(crate) fn add_context_data<T>(
252    &mut self,
253    context: Local<Context>,
254    data: Local<T>,
255  ) -> usize
256  where
257    for<'l> Local<'l, T>: Into<Local<'l, Data>>,
258  {
259    unsafe {
260      v8__SnapshotCreator__AddData_to_context(self, &*context, &*data.into())
261    }
262  }
263
264  /// Creates a snapshot data blob.
265  /// This must not be called from within a handle scope.
266  #[inline(always)]
267  pub(crate) fn create_blob(
268    &mut self,
269    function_code_handling: FunctionCodeHandling,
270  ) -> Option<StartupData> {
271    let blob =
272      unsafe { v8__SnapshotCreator__CreateBlob(self, function_code_handling) };
273    if blob.data.is_null() {
274      debug_assert!(blob.raw_size == 0);
275      None
276    } else {
277      debug_assert!(blob.raw_size > 0);
278      Some(StartupData(StartupDataInner::Cpp(blob)))
279    }
280  }
281}