Skip to main content

v8/
context.rs

1use crate::isolate::RealIsolate;
2use crate::scope::PinScope;
3// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
4use crate::Context;
5use crate::Local;
6use crate::MicrotaskQueue;
7use crate::Object;
8use crate::ObjectTemplate;
9use crate::Value;
10use crate::Weak;
11use crate::handle::UnsafeRefHandle;
12use crate::isolate::BuildTypeIdHasher;
13use crate::isolate::RawSlot;
14use crate::support::int;
15use std::any::TypeId;
16use std::collections::HashMap;
17use std::ffi::c_void;
18use std::ptr::{null, null_mut};
19use std::rc::Rc;
20
21unsafe extern "C" {
22  fn v8__Context__New(
23    isolate: *mut RealIsolate,
24    templ: *const ObjectTemplate,
25    global_object: *const Value,
26    microtask_queue: *mut MicrotaskQueue,
27  ) -> *const Context;
28  fn v8__Isolate__GetCurrent() -> *mut RealIsolate;
29  fn v8__Context__Global(this: *const Context) -> *const Object;
30  fn v8__Context__GetExtrasBindingObject(this: *const Context)
31  -> *const Object;
32  fn v8__Context__GetNumberOfEmbedderDataFields(this: *const Context) -> u32;
33  fn v8__Context__GetAlignedPointerFromEmbedderData(
34    this: *const Context,
35    index: int,
36  ) -> *mut c_void;
37  fn v8__Context__SetAlignedPointerInEmbedderData(
38    this: *const Context,
39    index: int,
40    value: *mut c_void,
41  );
42  fn v8__Context__GetEmbedderData(
43    this: *const Context,
44    index: int,
45  ) -> *const Value;
46  fn v8__Context__SetEmbedderData(
47    this: *const Context,
48    index: int,
49    value: *const Value,
50  );
51  fn v8__Context__FromSnapshot(
52    isolate: *mut RealIsolate,
53    context_snapshot_index: usize,
54    global_object: *const Value,
55    microtask_queue: *mut MicrotaskQueue,
56  ) -> *const Context;
57  pub(super) fn v8__Context__GetSecurityToken(
58    this: *const Context,
59  ) -> *const Value;
60  pub(super) fn v8__Context__SetSecurityToken(
61    this: *const Context,
62    value: *const Value,
63  );
64  pub(super) fn v8__Context__UseDefaultSecurityToken(this: *const Context);
65  pub(super) fn v8__Context__AllowCodeGenerationFromStrings(
66    this: *const Context,
67    allow: bool,
68  );
69  pub(super) fn v8__Context_IsCodeGenerationFromStringsAllowed(
70    this: *const Context,
71  ) -> bool;
72  fn v8__Context__GetMicrotaskQueue(
73    this: *const Context,
74  ) -> *const MicrotaskQueue;
75  fn v8__Context__SetMicrotaskQueue(
76    this: *const Context,
77    microtask_queue: *const MicrotaskQueue,
78  );
79}
80
81#[derive(Default)]
82pub struct ContextOptions<'s> {
83  /// An optional object template from which the global object for the newly created context will
84  /// be created.
85  pub global_template: Option<Local<'s, ObjectTemplate>>,
86  /// An optional global object to be reused for the newly created context. This global object
87  /// must have been created by a previous call to Context::New with the same global template.
88  /// The state of the global object will be completely reset and only object identify will
89  /// remain.
90  pub global_object: Option<Local<'s, Value>>,
91  /// An optional microtask queue used to manage the microtasks created in this context. If not
92  /// set the per-isolate default microtask queue would be used.
93  pub microtask_queue: Option<*mut MicrotaskQueue>,
94}
95
96impl Context {
97  const ANNEX_SLOT: int = 1;
98  const INTERNAL_SLOT_COUNT: int = 2;
99
100  /// Creates a new context.
101  #[inline(always)]
102  pub fn new<'s>(
103    scope: &PinScope<'s, '_, ()>,
104    options: ContextOptions,
105  ) -> Local<'s, Context> {
106    unsafe {
107      scope.cast_local(|sd| {
108        v8__Context__New(
109          sd.get_isolate_ptr(),
110          options
111            .global_template
112            .map_or_else(null, |t| &*t as *const _),
113          options.global_object.map_or_else(null, |o| &*o as *const _),
114          options.microtask_queue.unwrap_or_else(null_mut),
115        )
116      })
117    }
118    .unwrap()
119  }
120
121  #[inline(always)]
122  pub fn get_extras_binding_object<'s>(
123    &self,
124    scope: &PinScope<'s, '_, ()>,
125  ) -> Local<'s, Object> {
126    unsafe { scope.cast_local(|_| v8__Context__GetExtrasBindingObject(self)) }
127      .unwrap()
128  }
129
130  /// Returns the global proxy object.
131  ///
132  /// Global proxy object is a thin wrapper whose prototype points to actual
133  /// context's global object with the properties like Object, etc. This is done
134  /// that way for security reasons (for more details see
135  /// https://wiki.mozilla.org/Gecko:SplitWindow).
136  ///
137  /// Please note that changes to global proxy object prototype most probably
138  /// would break VM---v8 expects only global object as a prototype of global
139  /// proxy object.
140  #[inline(always)]
141  pub fn global<'s>(&self, scope: &PinScope<'s, '_, ()>) -> Local<'s, Object> {
142    unsafe { scope.cast_local(|_| v8__Context__Global(self)) }.unwrap()
143  }
144
145  #[inline(always)]
146  pub fn get_microtask_queue(&self) -> &MicrotaskQueue {
147    unsafe { &*v8__Context__GetMicrotaskQueue(self) }
148  }
149
150  #[inline(always)]
151  pub fn set_microtask_queue(&self, microtask_queue: &MicrotaskQueue) {
152    unsafe {
153      v8__Context__SetMicrotaskQueue(self, microtask_queue);
154    }
155  }
156
157  #[inline]
158  #[allow(clippy::mut_from_ref)]
159  fn get_annex_mut(
160    &self,
161    create_if_not_present: bool,
162  ) -> Option<&mut ContextAnnex> {
163    let isolate = unsafe { v8__Isolate__GetCurrent() };
164    let mut isolate = unsafe { crate::isolate::Isolate::from_raw_ptr(isolate) };
165
166    let num_data_fields =
167      unsafe { v8__Context__GetNumberOfEmbedderDataFields(self) } as int;
168    if num_data_fields > Self::ANNEX_SLOT {
169      let annex_ptr = unsafe {
170        v8__Context__GetAlignedPointerFromEmbedderData(self, Self::ANNEX_SLOT)
171      } as *mut ContextAnnex;
172      if !annex_ptr.is_null() {
173        // SAFETY: This reference doesn't outlive the Context, so it can't outlive
174        // the annex itself. Also, any mutations or accesses to the annex after
175        // its creation require a mutable reference to the context's isolate, but
176        // such a mutable reference is consumed by this reference during its
177        // lifetime.
178        return Some(unsafe { &mut *annex_ptr });
179      }
180    }
181
182    if !create_if_not_present {
183      return None;
184    }
185
186    let annex = Box::new(ContextAnnex {
187      slots: Default::default(),
188      // Gets replaced later in the method.
189      self_weak: Weak::empty(&mut isolate),
190    });
191    let annex_ptr = Box::into_raw(annex);
192    unsafe {
193      v8__Context__SetAlignedPointerInEmbedderData(
194        self,
195        Self::ANNEX_SLOT,
196        annex_ptr as *mut _,
197      );
198    };
199    assert!(
200      unsafe { v8__Context__GetNumberOfEmbedderDataFields(self) } as int
201        > Self::ANNEX_SLOT
202    );
203
204    // Make sure to drop the annex after the context is dropped, by creating a
205    // weak handle with a finalizer that drops the annex, and storing the weak
206    // in the annex itself.
207    let weak = {
208      // SAFETY: `self` can only have been derived from a `Local` or `Global`,
209      // and assuming the caller is only using safe code, the `Local` or
210      // `Global` must still be alive, so `self_ref_handle` won't outlive it.
211      // We also check above that `isolate` is the context's isolate.
212      let self_ref_handle = unsafe { UnsafeRefHandle::new(self, &mut isolate) };
213
214      Weak::with_guaranteed_finalizer(
215        &mut isolate,
216        self_ref_handle,
217        Box::new(move || {
218          // SAFETY: The lifetimes of references to the annex returned by this
219          // method are always tied to the context, and because this is the
220          // context's finalizer, we know there are no living references to
221          // the annex. And since the finalizer is only called once, the annex
222          // can't have been dropped before.
223          let _ = unsafe { Box::from_raw(annex_ptr) };
224        }),
225      )
226    };
227
228    // SAFETY: This reference doesn't outlive the Context, so it can't outlive
229    // the annex itself. Also, any mutations or accesses to the annex after
230    // its creation require a mutable reference to the context's isolate, but
231    // such a mutable reference is consumed by this reference during its
232    // lifetime.
233    let annex_mut = unsafe { &mut *annex_ptr };
234    annex_mut.self_weak = weak;
235    Some(annex_mut)
236  }
237
238  /// Get a reference to embedder data added with [`Self::set_slot()`].
239  #[inline(always)]
240  pub fn get_slot<T: 'static>(&self) -> Option<Rc<T>> {
241    if let Some(annex) = self.get_annex_mut(false) {
242      annex.slots.get(&TypeId::of::<T>()).map(|slot| {
243        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
244        // stored with T's TypeId as their key.
245        unsafe { slot.borrow::<Rc<T>>().clone() }
246      })
247    } else {
248      None
249    }
250  }
251
252  /// Use with [`Context::get_slot`] and [`Context::get_slot_mut`] to associate
253  /// state with a Context.
254  ///
255  /// This method gives ownership of value to the Context. Exactly one object of
256  /// each type can be associated with a Context. If called more than once with
257  /// an object of the same type, the earlier version will be dropped and
258  /// replaced.
259  ///
260  /// Returns true if value was set without replacing an existing value.
261  ///
262  /// The value will be dropped when the context is garbage collected.
263  #[inline(always)]
264  pub fn set_slot<T: 'static>(&self, value: Rc<T>) -> Option<Rc<T>> {
265    self
266      .get_annex_mut(true)
267      .unwrap()
268      .slots
269      .insert(TypeId::of::<T>(), RawSlot::new(value))
270      .map(|slot| {
271        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
272        // stored with T's TypeId as their key.
273        unsafe { slot.into_inner::<Rc<T>>() }
274      })
275  }
276
277  /// Removes the embedder data added with [`Self::set_slot()`] and returns it
278  /// if it exists.
279  #[inline(always)]
280  pub fn remove_slot<T: 'static>(&self) -> Option<Rc<T>> {
281    if let Some(annex) = self.get_annex_mut(false) {
282      annex.slots.remove(&TypeId::of::<T>()).map(|slot| {
283        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
284        // stored with T's TypeId as their key.
285        unsafe { slot.into_inner::<Rc<T>>() }
286      })
287    } else {
288      None
289    }
290  }
291
292  /// Removes all embedder data added with [`Self::set_slot()`], and
293  /// deletes any internal state needed to keep track of such slots.
294  ///
295  /// This is needed to make a snapshot with
296  /// [`SnapshotCreator`](crate::SnapshotCreator), since the internal embedder
297  /// state uses [`Weak`] handles, which cannot be alive at the time of
298  /// snapshotting.
299  #[inline(always)]
300  pub fn clear_all_slots(&self) {
301    if let Some(annex_mut) = self.get_annex_mut(false) {
302      let annex_ptr = annex_mut as *mut ContextAnnex;
303      let _ = unsafe { Box::from_raw(annex_ptr) };
304      unsafe {
305        v8__Context__SetAlignedPointerInEmbedderData(
306          self,
307          Self::ANNEX_SLOT,
308          null_mut(),
309        );
310      };
311    }
312  }
313
314  /// Sets the embedder data with the given index, growing the data as needed.
315  ///
316  /// Note that index 0 currently has a special meaning for Chrome's debugger.
317  #[inline(always)]
318  pub fn set_embedder_data(&self, slot: i32, data: Local<'_, Value>) {
319    // Initialize the annex when slot count > INTERNAL_SLOT_COUNT.
320    self.get_annex_mut(true);
321
322    unsafe {
323      v8__Context__SetEmbedderData(
324        self,
325        slot + Self::INTERNAL_SLOT_COUNT,
326        &*data,
327      );
328    }
329  }
330
331  /// Gets the embedder data with the given index, which must have been set by
332  /// a previous call to SetEmbedderData with the same index.
333  #[inline(always)]
334  pub fn get_embedder_data<'s>(
335    &self,
336    scope: &PinScope<'s, '_, ()>,
337    slot: i32,
338  ) -> Option<Local<'s, Value>> {
339    unsafe {
340      scope.cast_local(|_| {
341        v8__Context__GetEmbedderData(self, slot + Self::INTERNAL_SLOT_COUNT)
342      })
343    }
344  }
345
346  #[inline(always)]
347  pub unsafe fn set_aligned_pointer_in_embedder_data(
348    &self,
349    slot: i32,
350    data: *mut c_void,
351  ) {
352    // Initialize the annex when slot count > INTERNAL_SLOT_COUNT.
353    self.get_annex_mut(true);
354
355    unsafe {
356      v8__Context__SetAlignedPointerInEmbedderData(
357        self,
358        slot + Self::INTERNAL_SLOT_COUNT,
359        data,
360      );
361    }
362  }
363
364  #[inline(always)]
365  pub fn get_aligned_pointer_from_embedder_data(
366    &self,
367    slot: i32,
368  ) -> *mut c_void {
369    unsafe {
370      v8__Context__GetAlignedPointerFromEmbedderData(
371        self,
372        slot + Self::INTERNAL_SLOT_COUNT,
373      )
374    }
375  }
376
377  /// Create a new context from a (non-default) context snapshot. There
378  /// is no way to provide a global object template since we do not create
379  /// a new global object from template, but we can reuse a global object.
380  pub fn from_snapshot<'s>(
381    scope: &PinScope<'s, '_, ()>,
382    context_snapshot_index: usize,
383    options: ContextOptions,
384  ) -> Option<Local<'s, Context>> {
385    unsafe {
386      scope.cast_local(|sd| {
387        v8__Context__FromSnapshot(
388          sd.get_isolate_ptr(),
389          context_snapshot_index,
390          options.global_object.map_or_else(null, |o| &*o as *const _),
391          options.microtask_queue.unwrap_or_else(null_mut),
392        )
393      })
394    }
395  }
396
397  #[inline(always)]
398  pub fn get_security_token<'s>(
399    &self,
400    scope: &PinScope<'s, '_, ()>,
401  ) -> Local<'s, Value> {
402    unsafe { scope.cast_local(|_| v8__Context__GetSecurityToken(self)) }
403      .unwrap()
404  }
405
406  #[inline(always)]
407  pub fn set_security_token(&self, token: Local<Value>) {
408    unsafe {
409      v8__Context__SetSecurityToken(self, &*token);
410    }
411  }
412
413  #[inline(always)]
414  pub fn use_default_security_token(&self) {
415    unsafe {
416      v8__Context__UseDefaultSecurityToken(self);
417    }
418  }
419
420  pub fn set_allow_generation_from_strings(&self, allow: bool) {
421    unsafe {
422      v8__Context__AllowCodeGenerationFromStrings(self, allow);
423    }
424  }
425
426  pub fn is_code_generation_from_strings_allowed(&self) -> bool {
427    unsafe { v8__Context_IsCodeGenerationFromStringsAllowed(self) }
428  }
429}
430
431struct ContextAnnex {
432  slots: HashMap<TypeId, RawSlot, BuildTypeIdHasher>,
433  // In order to run the finalizer that drops the ContextAnnex when the Context
434  // is GC'd, the corresponding Weak must be kept alive until that time.
435  self_weak: Weak<Context>,
436}