Skip to main content

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::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  /// Returns the maximum size of the old generation in bytes.
177  pub fn max_old_generation_size_in_bytes(&self) -> usize {
178    self.raw.constraints.max_old_generation_size_in_bytes()
179  }
180
181  /// Sets the maximum size of the old generation in bytes. When the old
182  /// generation approaches this limit, V8 will perform series of garbage
183  /// collections and invoke the NearHeapLimitCallback.
184  pub fn set_max_old_generation_size_in_bytes(mut self, limit: usize) -> Self {
185    self
186      .raw
187      .constraints
188      .set_max_old_generation_size_in_bytes(limit);
189    self
190  }
191
192  /// Returns the maximum size of the young generation in bytes.
193  pub fn max_young_generation_size_in_bytes(&self) -> usize {
194    self.raw.constraints.max_young_generation_size_in_bytes()
195  }
196
197  /// Sets the maximum size of the young generation in bytes. The young
198  /// generation consists of two semi-spaces and a large object space. This
199  /// affects frequency of Scavenge garbage collections.
200  pub fn set_max_young_generation_size_in_bytes(
201    mut self,
202    limit: usize,
203  ) -> Self {
204    self
205      .raw
206      .constraints
207      .set_max_young_generation_size_in_bytes(limit);
208    self
209  }
210
211  /// Returns the code range size in bytes.
212  pub fn code_range_size_in_bytes(&self) -> usize {
213    self.raw.constraints.code_range_size_in_bytes()
214  }
215
216  /// Sets the amount of virtual memory reserved for generated code in bytes.
217  /// This is relevant for 64-bit architectures that rely on code range for
218  /// calls in code.
219  pub fn set_code_range_size_in_bytes(mut self, limit: usize) -> Self {
220    self.raw.constraints.set_code_range_size_in_bytes(limit);
221    self
222  }
223
224  /// Returns the stack limit (the address beyond which the VM's stack may
225  /// not grow), or null if not set.
226  pub fn stack_limit(&self) -> *mut u32 {
227    self.raw.constraints.stack_limit()
228  }
229
230  /// Sets the address beyond which the VM's stack may not grow.
231  ///
232  /// # Safety
233  ///
234  /// The caller must ensure that the pointer remains valid for the lifetime
235  /// of the isolate, and points to a valid stack boundary.
236  pub unsafe fn set_stack_limit(mut self, value: *mut u32) -> Self {
237    self.raw.constraints.set_stack_limit(value);
238    self
239  }
240
241  /// Returns the initial size of the old generation in bytes.
242  pub fn initial_old_generation_size_in_bytes(&self) -> usize {
243    self.raw.constraints.initial_old_generation_size_in_bytes()
244  }
245
246  /// Sets the initial size of the old generation in bytes. Setting the
247  /// initial size avoids ineffective garbage collections at startup if the
248  /// live set is large.
249  pub fn set_initial_old_generation_size_in_bytes(
250    mut self,
251    initial_size: usize,
252  ) -> Self {
253    self
254      .raw
255      .constraints
256      .set_initial_old_generation_size_in_bytes(initial_size);
257    self
258  }
259
260  /// Returns the initial size of the young generation in bytes.
261  pub fn initial_young_generation_size_in_bytes(&self) -> usize {
262    self
263      .raw
264      .constraints
265      .initial_young_generation_size_in_bytes()
266  }
267
268  /// Sets the initial size of the young generation in bytes.
269  pub fn set_initial_young_generation_size_in_bytes(
270    mut self,
271    initial_size: usize,
272  ) -> Self {
273    self
274      .raw
275      .constraints
276      .set_initial_young_generation_size_in_bytes(initial_size);
277    self
278  }
279
280  /// A CppHeap used to construct the Isolate. V8 takes ownership of the
281  /// CppHeap passed this way.
282  pub fn cpp_heap(mut self, heap: UniqueRef<Heap>) -> Self {
283    self.raw.cpp_heap = heap.into_raw();
284    self
285  }
286
287  pub(crate) fn finalize(mut self) -> (raw::CreateParams, Box<dyn Any>) {
288    if self.raw.array_buffer_allocator_shared.is_null() {
289      self = self.array_buffer_allocator(array_buffer::new_default_allocator());
290    }
291    let Self { raw, allocations } = self;
292    (raw, Box::new(allocations))
293  }
294}
295
296#[derive(Debug, Default)]
297struct CreateParamAllocations {
298  // Owner of the snapshot data buffer itself.
299  snapshot_blob_data: Option<StartupData>,
300  // Owns `struct StartupData` which contains just the (ptr, len) tuple in V8's
301  // preferred format. We have to heap allocate this because we need to put a
302  // stable pointer to it in `CreateParams`.
303  snapshot_blob_header: Option<Box<RawStartupData>>,
304  external_references: Option<Cow<'static, [ExternalReference]>>,
305}
306
307#[test]
308fn create_param_defaults() {
309  let params = CreateParams::default();
310  assert!(params.raw.allow_atomics_wait);
311}
312
313pub(crate) mod raw {
314  use super::*;
315
316  #[repr(C)]
317  #[derive(Debug)]
318  pub(crate) struct CreateParams {
319    pub code_event_handler: *const Opaque, // JitCodeEventHandler
320    pub constraints: ResourceConstraints,
321    pub snapshot_blob: *const RawStartupData,
322    pub counter_lookup_callback: Option<CounterLookupCallback>,
323    pub create_histogram_callback: *const Opaque, // CreateHistogramCallback
324    pub add_histogram_sample_callback: *const Opaque, // AddHistogramSampleCallback
325    pub array_buffer_allocator: *mut ArrayBufferAllocator,
326    pub array_buffer_allocator_shared: SharedPtr<ArrayBufferAllocator>,
327    pub external_references: *const intptr_t,
328    pub allow_atomics_wait: bool,
329    _fatal_error_handler: *const Opaque, // FatalErrorCallback
330    _oom_error_handler: *const Opaque,   // OOMErrorCallback
331    pub cpp_heap: *const Heap,
332  }
333
334  unsafe extern "C" {
335    fn v8__Isolate__CreateParams__CONSTRUCT(
336      buf: *mut MaybeUninit<CreateParams>,
337    );
338    fn v8__Isolate__CreateParams__SIZEOF() -> usize;
339  }
340
341  impl Default for CreateParams {
342    fn default() -> Self {
343      let size = unsafe { v8__Isolate__CreateParams__SIZEOF() };
344      assert_eq!(size, size_of::<Self>());
345      let mut buf = MaybeUninit::<Self>::uninit();
346      unsafe { v8__Isolate__CreateParams__CONSTRUCT(&mut buf) };
347      unsafe { buf.assume_init() }
348    }
349  }
350
351  #[repr(C)]
352  #[derive(Debug)]
353  pub(crate) struct ResourceConstraints {
354    code_range_size_: usize,
355    max_old_generation_size_: usize,
356    max_young_generation_size_: usize,
357    initial_old_generation_size_: usize,
358    initial_young_generation_size_: usize,
359    physical_memory_size_: u64,
360    stack_limit_: *mut u32,
361  }
362
363  unsafe extern "C" {
364    fn v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
365      constraints: *mut ResourceConstraints,
366      initial_heap_size_in_bytes: usize,
367      maximum_heap_size_in_bytes: usize,
368    );
369    fn v8__ResourceConstraints__ConfigureDefaults(
370      constraints: *mut ResourceConstraints,
371      physical_memory: u64,
372      virtual_memory_limit: u64,
373    );
374    fn v8__ResourceConstraints__max_old_generation_size_in_bytes(
375      constraints: *const ResourceConstraints,
376    ) -> usize;
377    fn v8__ResourceConstraints__set_max_old_generation_size_in_bytes(
378      constraints: *mut ResourceConstraints,
379      limit: usize,
380    );
381    fn v8__ResourceConstraints__max_young_generation_size_in_bytes(
382      constraints: *const ResourceConstraints,
383    ) -> usize;
384    fn v8__ResourceConstraints__set_max_young_generation_size_in_bytes(
385      constraints: *mut ResourceConstraints,
386      limit: usize,
387    );
388    fn v8__ResourceConstraints__code_range_size_in_bytes(
389      constraints: *const ResourceConstraints,
390    ) -> usize;
391    fn v8__ResourceConstraints__set_code_range_size_in_bytes(
392      constraints: *mut ResourceConstraints,
393      limit: usize,
394    );
395    fn v8__ResourceConstraints__stack_limit(
396      constraints: *const ResourceConstraints,
397    ) -> *mut u32;
398    fn v8__ResourceConstraints__set_stack_limit(
399      constraints: *mut ResourceConstraints,
400      value: *mut u32,
401    );
402    fn v8__ResourceConstraints__initial_old_generation_size_in_bytes(
403      constraints: *const ResourceConstraints,
404    ) -> usize;
405    fn v8__ResourceConstraints__set_initial_old_generation_size_in_bytes(
406      constraints: *mut ResourceConstraints,
407      initial_size: usize,
408    );
409    fn v8__ResourceConstraints__initial_young_generation_size_in_bytes(
410      constraints: *const ResourceConstraints,
411    ) -> usize;
412    fn v8__ResourceConstraints__set_initial_young_generation_size_in_bytes(
413      constraints: *mut ResourceConstraints,
414      initial_size: usize,
415    );
416  }
417
418  impl ResourceConstraints {
419    pub fn configure_defaults_from_heap_size(
420      &mut self,
421      initial_heap_size_in_bytes: usize,
422      maximum_heap_size_in_bytes: usize,
423    ) {
424      unsafe {
425        v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
426          self,
427          initial_heap_size_in_bytes,
428          maximum_heap_size_in_bytes,
429        );
430      };
431    }
432
433    pub fn configure_defaults(
434      &mut self,
435      physical_memory: u64,
436      virtual_memory_limit: u64,
437    ) {
438      unsafe {
439        v8__ResourceConstraints__ConfigureDefaults(
440          self,
441          physical_memory,
442          virtual_memory_limit,
443        );
444      }
445    }
446
447    pub fn max_old_generation_size_in_bytes(&self) -> usize {
448      unsafe { v8__ResourceConstraints__max_old_generation_size_in_bytes(self) }
449    }
450
451    pub fn set_max_old_generation_size_in_bytes(&mut self, limit: usize) {
452      unsafe {
453        v8__ResourceConstraints__set_max_old_generation_size_in_bytes(
454          self, limit,
455        );
456      }
457    }
458
459    pub fn max_young_generation_size_in_bytes(&self) -> usize {
460      unsafe {
461        v8__ResourceConstraints__max_young_generation_size_in_bytes(self)
462      }
463    }
464
465    pub fn set_max_young_generation_size_in_bytes(&mut self, limit: usize) {
466      unsafe {
467        v8__ResourceConstraints__set_max_young_generation_size_in_bytes(
468          self, limit,
469        );
470      }
471    }
472
473    pub fn code_range_size_in_bytes(&self) -> usize {
474      unsafe { v8__ResourceConstraints__code_range_size_in_bytes(self) }
475    }
476
477    pub fn set_code_range_size_in_bytes(&mut self, limit: usize) {
478      unsafe {
479        v8__ResourceConstraints__set_code_range_size_in_bytes(self, limit);
480      }
481    }
482
483    pub fn stack_limit(&self) -> *mut u32 {
484      unsafe { v8__ResourceConstraints__stack_limit(self) }
485    }
486
487    pub fn set_stack_limit(&mut self, value: *mut u32) {
488      unsafe {
489        v8__ResourceConstraints__set_stack_limit(self, value);
490      }
491    }
492
493    pub fn initial_old_generation_size_in_bytes(&self) -> usize {
494      unsafe {
495        v8__ResourceConstraints__initial_old_generation_size_in_bytes(self)
496      }
497    }
498
499    pub fn set_initial_old_generation_size_in_bytes(
500      &mut self,
501      initial_size: usize,
502    ) {
503      unsafe {
504        v8__ResourceConstraints__set_initial_old_generation_size_in_bytes(
505          self,
506          initial_size,
507        );
508      }
509    }
510
511    pub fn initial_young_generation_size_in_bytes(&self) -> usize {
512      unsafe {
513        v8__ResourceConstraints__initial_young_generation_size_in_bytes(self)
514      }
515    }
516
517    pub fn set_initial_young_generation_size_in_bytes(
518      &mut self,
519      initial_size: usize,
520    ) {
521      unsafe {
522        v8__ResourceConstraints__set_initial_young_generation_size_in_bytes(
523          self,
524          initial_size,
525        );
526      }
527    }
528  }
529}