v8/
isolate_create_params.rs

1use crate::ExternalReference;
2use crate::array_buffer;
3use crate::array_buffer::Allocator as ArrayBufferAllocator;
4use crate::cppgc::Heap;
5use crate::snapshot::RawStartupData;
6use crate::snapshot::StartupData;
7use crate::support::Opaque;
8use crate::support::SharedPtr;
9use crate::support::UniqueRef;
10use crate::support::char;
11use crate::support::int;
12use crate::support::intptr_t;
13
14use std::any::Any;
15use std::borrow::Cow;
16use std::iter::once;
17use std::mem::MaybeUninit;
18use std::mem::size_of;
19use std::ptr::null;
20
21/// Should return a pointer to memory that persists for the lifetime of the
22/// isolate.
23pub type CounterLookupCallback =
24  unsafe extern "C" fn(name: *const char) -> *mut i32;
25
26/// Initial configuration parameters for a new Isolate.
27#[must_use]
28#[derive(Debug, Default)]
29pub struct CreateParams {
30  raw: raw::CreateParams,
31  allocations: CreateParamAllocations,
32}
33
34impl CreateParams {
35  /// Enables the host application to provide a mechanism for recording
36  /// statistics counters.
37  pub fn counter_lookup_callback(
38    mut self,
39    callback: CounterLookupCallback,
40  ) -> Self {
41    self.raw.counter_lookup_callback = Some(callback);
42    self
43  }
44
45  /// Explicitly specify a startup snapshot blob.
46  pub fn snapshot_blob(mut self, data: StartupData) -> Self {
47    let header = Box::new(RawStartupData {
48      data: data.as_ptr() as _,
49      raw_size: data.len() as _,
50    });
51    self.raw.snapshot_blob = &*header;
52    self.allocations.snapshot_blob_data = Some(data);
53    self.allocations.snapshot_blob_header = Some(header);
54    self
55  }
56
57  /// The ArrayBuffer::ArrayBufferAllocator to use for allocating and freeing
58  /// the backing store of ArrayBuffers.
59  pub fn array_buffer_allocator(
60    mut self,
61    array_buffer_allocator: impl Into<SharedPtr<ArrayBufferAllocator>>,
62  ) -> Self {
63    self.raw.array_buffer_allocator_shared = array_buffer_allocator.into();
64    self
65  }
66
67  /// Check if `array_buffer_allocator` has already been called. Useful to some
68  /// embedders that might want to set an allocator but not overwrite if one
69  /// was already set by a user.
70  pub fn has_set_array_buffer_allocator(&self) -> bool {
71    !self.raw.array_buffer_allocator_shared.is_null()
72  }
73
74  /// Specifies an optional nullptr-terminated array of raw addresses in the
75  /// embedder that V8 can match against during serialization and use for
76  /// deserialization. This array and its content must stay valid for the
77  /// entire lifetime of the isolate.
78  pub fn external_references(
79    mut self,
80    ext_refs: Cow<'static, [ExternalReference]>,
81  ) -> Self {
82    let ext_refs = if ext_refs.last()
83      == Some(&ExternalReference {
84        pointer: std::ptr::null_mut(),
85      }) {
86      ext_refs
87    } else {
88      Cow::from(
89        ext_refs
90          .into_owned()
91          .into_iter()
92          .chain(once(ExternalReference {
93            pointer: std::ptr::null_mut(),
94          }))
95          .collect::<Vec<_>>(),
96      )
97    };
98
99    self.allocations.external_references = Some(ext_refs);
100    self.raw.external_references = self
101      .allocations
102      .external_references
103      .as_ref()
104      .map(|c| c.as_ptr() as _)
105      .unwrap_or_else(null);
106
107    self
108  }
109
110  /// Whether calling Atomics.wait (a function that may block) is allowed in
111  /// this isolate. This can also be configured via SetAllowAtomicsWait.
112  pub fn allow_atomics_wait(mut self, value: bool) -> Self {
113    self.raw.allow_atomics_wait = value;
114    self
115  }
116
117  /// The following parameters describe the offsets for addressing type info
118  /// for wrapped API objects and are used by the fast C API
119  /// (for details see v8-fast-api-calls.h).
120  pub fn embedder_wrapper_type_info_offsets(
121    mut self,
122    embedder_wrapper_type_index: int,
123    embedder_wrapper_object_index: int,
124  ) -> Self {
125    self.raw.embedder_wrapper_type_index = embedder_wrapper_type_index;
126    self.raw.embedder_wrapper_object_index = embedder_wrapper_object_index;
127    self
128  }
129
130  /// Configures the constraints with reasonable default values based on the
131  /// provided lower and upper bounds.
132  ///
133  /// By default V8 starts with a small heap and dynamically grows it to match
134  /// the set of live objects. This may lead to ineffective garbage collections
135  /// at startup if the live set is large. Setting the initial heap size avoids
136  /// such garbage collections. Note that this does not affect young generation
137  /// garbage collections.
138  ///
139  /// When the heap size approaches `max`, V8 will perform series of
140  /// garbage collections and invoke the
141  /// [NearHeapLimitCallback](struct.Isolate.html#method.add_near_heap_limit_callback).
142  /// If the garbage collections do not help and the callback does not
143  /// increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory.
144  ///
145  /// The heap size includes both the young and the old generation.
146  ///
147  /// # Arguments
148  ///
149  /// * `initial` - The initial heap size or zero in bytes
150  /// * `max` - The hard limit for the heap size in bytes
151  pub fn heap_limits(mut self, initial: usize, max: usize) -> Self {
152    self
153      .raw
154      .constraints
155      .configure_defaults_from_heap_size(initial, max);
156    self
157  }
158
159  /// Configures the constraints with reasonable default values based on the capabilities
160  /// of the current device the VM is running on.
161  ///
162  /// By default V8 starts with a small heap and dynamically grows it to match
163  /// the set of live objects. This may lead to ineffective garbage collections
164  /// at startup if the live set is large. Setting the initial heap size avoids
165  /// such garbage collections. Note that this does not affect young generation
166  /// garbage collections.
167  ///
168  /// When the heap size approaches its maximum, V8 will perform series of
169  /// garbage collections and invoke the
170  /// [NearHeapLimitCallback](struct.Isolate.html#method.add_near_heap_limit_callback).
171  /// If the garbage collections do not help and the callback does not
172  /// increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory.
173  ///
174  /// # Arguments
175  ///
176  /// * `physical_memory` - The total amount of physical memory on the current device, in bytes.
177  /// * `virtual_memory_limit` - The amount of virtual memory on the current device, in bytes, or zero, if there is no limit.
178  pub fn heap_limits_from_system_memory(
179    mut self,
180    physical_memory: u64,
181    virtual_memory_limit: u64,
182  ) -> Self {
183    self
184      .raw
185      .constraints
186      .configure_defaults(physical_memory, virtual_memory_limit);
187    self
188  }
189
190  /// A CppHeap used to construct the Isolate. V8 takes ownership of the
191  /// CppHeap passed this way.
192  pub fn cpp_heap(mut self, heap: UniqueRef<Heap>) -> Self {
193    self.raw.cpp_heap = heap.into_raw();
194    self
195  }
196
197  pub(crate) fn finalize(mut self) -> (raw::CreateParams, Box<dyn Any>) {
198    if self.raw.array_buffer_allocator_shared.is_null() {
199      self = self.array_buffer_allocator(array_buffer::new_default_allocator());
200    }
201    let Self { raw, allocations } = self;
202    (raw, Box::new(allocations))
203  }
204}
205
206#[derive(Debug, Default)]
207struct CreateParamAllocations {
208  // Owner of the snapshot data buffer itself.
209  snapshot_blob_data: Option<StartupData>,
210  // Owns `struct StartupData` which contains just the (ptr, len) tuple in V8's
211  // preferred format. We have to heap allocate this because we need to put a
212  // stable pointer to it in `CreateParams`.
213  snapshot_blob_header: Option<Box<RawStartupData>>,
214  external_references: Option<Cow<'static, [ExternalReference]>>,
215}
216
217#[test]
218fn create_param_defaults() {
219  let params = CreateParams::default();
220  assert_eq!(params.raw.embedder_wrapper_type_index, -1);
221  assert_eq!(params.raw.embedder_wrapper_object_index, -1);
222  assert!(params.raw.allow_atomics_wait);
223}
224
225pub(crate) mod raw {
226  use super::*;
227
228  #[repr(C)]
229  #[derive(Debug)]
230  pub(crate) struct CreateParams {
231    pub code_event_handler: *const Opaque, // JitCodeEventHandler
232    pub constraints: ResourceConstraints,
233    pub snapshot_blob: *const RawStartupData,
234    pub counter_lookup_callback: Option<CounterLookupCallback>,
235    pub create_histogram_callback: *const Opaque, // CreateHistogramCallback
236    pub add_histogram_sample_callback: *const Opaque, // AddHistogramSampleCallback
237    pub array_buffer_allocator: *mut ArrayBufferAllocator,
238    pub array_buffer_allocator_shared: SharedPtr<ArrayBufferAllocator>,
239    pub external_references: *const intptr_t,
240    pub allow_atomics_wait: bool,
241    pub embedder_wrapper_type_index: int,
242    pub embedder_wrapper_object_index: int,
243    _fatal_error_handler: *const Opaque, // FatalErrorCallback
244    _oom_error_handler: *const Opaque,   // OOMErrorCallback
245    pub cpp_heap: *const Heap,
246  }
247
248  unsafe extern "C" {
249    fn v8__Isolate__CreateParams__CONSTRUCT(
250      buf: *mut MaybeUninit<CreateParams>,
251    );
252    fn v8__Isolate__CreateParams__SIZEOF() -> usize;
253  }
254
255  impl Default for CreateParams {
256    fn default() -> Self {
257      let size = unsafe { v8__Isolate__CreateParams__SIZEOF() };
258      assert_eq!(size, size_of::<Self>());
259      let mut buf = MaybeUninit::<Self>::uninit();
260      unsafe { v8__Isolate__CreateParams__CONSTRUCT(&mut buf) };
261      unsafe { buf.assume_init() }
262    }
263  }
264
265  #[repr(C)]
266  #[derive(Debug)]
267  pub(crate) struct ResourceConstraints {
268    code_range_size_: usize,
269    max_old_generation_size_: usize,
270    max_young_generation_size_: usize,
271    initial_old_generation_size_: usize,
272    initial_young_generation_size_: usize,
273    physical_memory_size_: u64,
274    stack_limit_: *mut u32,
275  }
276
277  unsafe extern "C" {
278    fn v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
279      constraints: *mut ResourceConstraints,
280      initial_heap_size_in_bytes: usize,
281      maximum_heap_size_in_bytes: usize,
282    );
283    fn v8__ResourceConstraints__ConfigureDefaults(
284      constraints: *mut ResourceConstraints,
285      physical_memory: u64,
286      virtual_memory_limit: u64,
287    );
288  }
289
290  impl ResourceConstraints {
291    pub fn configure_defaults_from_heap_size(
292      &mut self,
293      initial_heap_size_in_bytes: usize,
294      maximum_heap_size_in_bytes: usize,
295    ) {
296      unsafe {
297        v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
298          self,
299          initial_heap_size_in_bytes,
300          maximum_heap_size_in_bytes,
301        );
302      };
303    }
304
305    pub fn configure_defaults(
306      &mut self,
307      physical_memory: u64,
308      virtual_memory_limit: u64,
309    ) {
310      unsafe {
311        v8__ResourceConstraints__ConfigureDefaults(
312          self,
313          physical_memory,
314          virtual_memory_limit,
315        );
316      }
317    }
318  }
319}