rusty_v8/
isolate_create_params.rs

1use crate::array_buffer;
2use crate::array_buffer::Allocator as ArrayBufferAllocator;
3use crate::support::char;
4use crate::support::int;
5use crate::support::intptr_t;
6use crate::support::Allocated;
7use crate::support::Allocation;
8use crate::support::Opaque;
9use crate::support::SharedPtr;
10
11use std::any::Any;
12use std::convert::TryFrom;
13use std::iter::once;
14use std::mem::size_of;
15use std::mem::MaybeUninit;
16use std::os::raw::c_char;
17use std::ptr::null;
18
19/// Should return a pointer to memory that persists for the lifetime of the
20/// isolate.
21pub type CounterLookupCallback = extern "C" fn(name: *const c_char) -> *mut i32;
22
23/// Initial configuration parameters for a new Isolate.
24#[must_use]
25#[derive(Debug, Default)]
26pub struct CreateParams {
27  raw: raw::CreateParams,
28  allocations: CreateParamAllocations,
29}
30
31impl CreateParams {
32  /// Enables the host application to provide a mechanism for recording
33  /// statistics counters.
34  pub fn counter_lookup_callback(
35    mut self,
36    callback: CounterLookupCallback,
37  ) -> Self {
38    self.raw.counter_lookup_callback = Some(callback);
39    self
40  }
41
42  /// Explicitly specify a startup snapshot blob.
43  pub fn snapshot_blob(mut self, data: impl Allocated<[u8]>) -> Self {
44    let data = Allocation::of(data);
45    let header = Allocation::of(raw::StartupData::boxed_header(&data));
46    self.raw.snapshot_blob = &*header;
47    self.allocations.snapshot_blob_data = Some(data);
48    self.allocations.snapshot_blob_header = Some(header);
49    self
50  }
51
52  /// The ArrayBuffer::ArrayBufferAllocator to use for allocating and freeing
53  /// the backing store of ArrayBuffers.
54  pub fn array_buffer_allocator(
55    mut self,
56    array_buffer_allocator: impl Into<SharedPtr<ArrayBufferAllocator>>,
57  ) -> Self {
58    self.raw.array_buffer_allocator_shared = array_buffer_allocator.into();
59    self
60  }
61
62  /// Specifies an optional nullptr-terminated array of raw addresses in the
63  /// embedder that V8 can match against during serialization and use for
64  /// deserialization. This array and its content must stay valid for the
65  /// entire lifetime of the isolate.
66  pub fn external_references(
67    mut self,
68    ext_refs: impl Allocated<[intptr_t]>,
69  ) -> Self {
70    let last_non_null = ext_refs
71      .iter()
72      .cloned()
73      .enumerate()
74      .rev()
75      .find_map(|(idx, value)| if value != 0 { Some(idx) } else { None });
76    let first_null = ext_refs
77      .iter()
78      .cloned()
79      .enumerate()
80      .find_map(|(idx, value)| if value == 0 { Some(idx) } else { None });
81    match (last_non_null, first_null) {
82      (None, _) => {
83        // Empty list.
84        self.raw.external_references = null();
85        self.allocations.external_references = None;
86      }
87      (_, None) => {
88        // List does not have null terminator. Make a copy and add it.
89        let ext_refs =
90          ext_refs.iter().cloned().chain(once(0)).collect::<Vec<_>>();
91        let ext_refs = Allocation::of(ext_refs);
92        self.raw.external_references = &ext_refs[0];
93        self.allocations.external_references = Some(ext_refs);
94      }
95      (Some(idx1), Some(idx2)) if idx1 + 1 == idx2 => {
96        // List is properly null terminated, we'll use it as-is.
97        let ext_refs = Allocation::of(ext_refs);
98        self.raw.external_references = &ext_refs[0];
99        self.allocations.external_references = Some(ext_refs);
100      }
101      _ => panic!("unexpected null pointer in external references list"),
102    }
103    self
104  }
105
106  /// Whether calling Atomics.wait (a function that may block) is allowed in
107  /// this isolate. This can also be configured via SetAllowAtomicsWait.
108  pub fn allow_atomics_wait(mut self, value: bool) -> Self {
109    self.raw.allow_atomics_wait = value;
110    self
111  }
112
113  /// Termination is postponed when there is no active SafeForTerminationScope.
114  pub fn only_terminate_in_safe_scope(mut self, value: bool) -> Self {
115    self.raw.only_terminate_in_safe_scope = value;
116    self
117  }
118
119  /// The following parameters describe the offsets for addressing type info
120  /// for wrapped API objects and are used by the fast C API
121  /// (for details see v8-fast-api-calls.h).
122  pub fn embedder_wrapper_type_info_offsets(
123    mut self,
124    embedder_wrapper_type_index: int,
125    embedder_wrapper_object_index: int,
126  ) -> Self {
127    self.raw.embedder_wrapper_type_index = embedder_wrapper_type_index;
128    self.raw.embedder_wrapper_object_index = embedder_wrapper_object_index;
129    self
130  }
131
132  /// Configures the constraints with reasonable default values based on the
133  /// provided lower and upper bounds.
134  ///
135  /// By default V8 starts with a small heap and dynamically grows it to match
136  /// the set of live objects. This may lead to ineffective garbage collections
137  /// at startup if the live set is large. Setting the initial heap size avoids
138  /// such garbage collections. Note that this does not affect young generation
139  /// garbage collections.
140  ///
141  /// When the heap size approaches `max`, V8 will perform series of
142  /// garbage collections and invoke the
143  /// [NearHeapLimitCallback](struct.Isolate.html#method.add_near_heap_limit_callback).
144  /// If the garbage collections do not help and the callback does not
145  /// increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory.
146  ///
147  /// The heap size includes both the young and the old generation.
148  ///
149  /// # Arguments
150  ///
151  /// * `initial` - The initial heap size or zero in bytes
152  /// * `max` - The hard limit for the heap size in bytes
153  pub fn heap_limits(mut self, initial: usize, max: usize) -> Self {
154    self
155      .raw
156      .constraints
157      .configure_defaults_from_heap_size(initial, max);
158    self
159  }
160
161  pub(crate) fn finalize(mut self) -> (raw::CreateParams, Box<dyn Any>) {
162    if self.raw.array_buffer_allocator_shared.is_null() {
163      self = self.array_buffer_allocator(array_buffer::new_default_allocator());
164    }
165    let Self { raw, allocations } = self;
166    (raw, Box::new(allocations))
167  }
168}
169
170#[derive(Debug, Default)]
171struct CreateParamAllocations {
172  // Owner of the snapshot data buffer itself.
173  snapshot_blob_data: Option<Allocation<[u8]>>,
174  // Owns `struct StartupData` which contains just the (ptr, len) tuple in V8's
175  // preferred format. We have to heap allocate this because we need to put a
176  // stable pointer to it in `CreateParams`.
177  snapshot_blob_header: Option<Allocation<raw::StartupData>>,
178  external_references: Option<Allocation<[intptr_t]>>,
179}
180
181#[test]
182fn create_param_defaults() {
183  let params = CreateParams::default();
184  assert_eq!(params.raw.embedder_wrapper_type_index, -1);
185  assert_eq!(params.raw.embedder_wrapper_object_index, -1);
186  assert!(!params.raw.only_terminate_in_safe_scope);
187  assert!(params.raw.allow_atomics_wait);
188}
189
190pub(crate) mod raw {
191  use super::*;
192
193  #[repr(C)]
194  #[derive(Debug)]
195  pub(crate) struct CreateParams {
196    pub code_event_handler: *const Opaque, // JitCodeEventHandler
197    pub constraints: ResourceConstraints,
198    pub snapshot_blob: *const StartupData,
199    pub counter_lookup_callback: Option<CounterLookupCallback>,
200    pub create_histogram_callback: *const Opaque, // CreateHistogramCallback
201    pub add_histogram_sample_callback: *const Opaque, // AddHistogramSampleCallback
202    pub array_buffer_allocator: *mut ArrayBufferAllocator,
203    pub array_buffer_allocator_shared: SharedPtr<ArrayBufferAllocator>,
204    pub external_references: *const intptr_t,
205    pub allow_atomics_wait: bool,
206    pub only_terminate_in_safe_scope: bool,
207    pub embedder_wrapper_type_index: int,
208    pub embedder_wrapper_object_index: int,
209    // NOTE(bartlomieju): this field is deprecated in V8 API.
210    // This is an std::vector<std::string>. It's usually no bigger
211    // than three or four words but let's take a generous upper bound.
212    pub supported_import_assertions: [usize; 8],
213  }
214
215  extern "C" {
216    fn v8__Isolate__CreateParams__CONSTRUCT(
217      buf: *mut MaybeUninit<CreateParams>,
218    );
219    fn v8__Isolate__CreateParams__SIZEOF() -> usize;
220  }
221
222  impl Default for CreateParams {
223    fn default() -> Self {
224      let size = unsafe { v8__Isolate__CreateParams__SIZEOF() };
225      assert!(size <= size_of::<Self>());
226      let mut buf = MaybeUninit::<Self>::uninit();
227      unsafe { v8__Isolate__CreateParams__CONSTRUCT(&mut buf) };
228      unsafe { buf.assume_init() }
229    }
230  }
231
232  #[repr(C)]
233  #[derive(Debug)]
234  pub(crate) struct StartupData {
235    pub data: *const char,
236    pub raw_size: int,
237  }
238
239  impl StartupData {
240    pub(super) fn boxed_header(data: &Allocation<[u8]>) -> Box<Self> {
241      Box::new(Self {
242        data: &data[0] as *const _ as *const char,
243        raw_size: int::try_from(data.len()).unwrap(),
244      })
245    }
246  }
247
248  #[repr(C)]
249  #[derive(Debug)]
250  pub(crate) struct ResourceConstraints {
251    code_range_size_: usize,
252    max_old_generation_size_: usize,
253    max_young_generation_size_: usize,
254    initial_old_generation_size_: usize,
255    initial_young_generation_size_: usize,
256    stack_limit_: *mut u32,
257  }
258
259  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  }
266
267  impl ResourceConstraints {
268    pub fn configure_defaults_from_heap_size(
269      &mut self,
270      initial_heap_size_in_bytes: usize,
271      maximum_heap_size_in_bytes: usize,
272    ) {
273      unsafe {
274        v8__ResourceConstraints__ConfigureDefaultsFromHeapSize(
275          self,
276          initial_heap_size_in_bytes,
277          maximum_heap_size_in_bytes,
278        )
279      };
280    }
281  }
282}