Skip to main content

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