[go: up one dir, main page]

v8/
context.rs

1// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
2use crate::Context;
3use crate::HandleScope;
4use crate::Local;
5use crate::MicrotaskQueue;
6use crate::Object;
7use crate::ObjectTemplate;
8use crate::Value;
9use crate::Weak;
10use crate::handle::UnsafeRefHandle;
11use crate::isolate::BuildTypeIdHasher;
12use crate::isolate::Isolate;
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 Isolate,
24    templ: *const ObjectTemplate,
25    global_object: *const Value,
26    microtask_queue: *mut MicrotaskQueue,
27  ) -> *const Context;
28  fn v8__Context__GetIsolate(this: *const Context) -> *mut Isolate;
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 Isolate,
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 = 1;
99
100  /// Creates a new context.
101  #[inline(always)]
102  pub fn new<'s>(
103    scope: &mut HandleScope<'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: &mut HandleScope<'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>(
142    &self,
143    scope: &mut HandleScope<'s, ()>,
144  ) -> Local<'s, Object> {
145    unsafe { scope.cast_local(|_| v8__Context__Global(self)) }.unwrap()
146  }
147
148  #[inline(always)]
149  pub fn get_microtask_queue(&self) -> &MicrotaskQueue {
150    unsafe { &*v8__Context__GetMicrotaskQueue(self) }
151  }
152
153  #[inline(always)]
154  pub fn set_microtask_queue(&self, microtask_queue: &MicrotaskQueue) {
155    unsafe {
156      v8__Context__SetMicrotaskQueue(self, microtask_queue);
157    }
158  }
159
160  #[inline]
161  fn get_annex_mut(
162    &self,
163    create_if_not_present: bool,
164  ) -> Option<&mut ContextAnnex> {
165    let isolate = unsafe { &mut *v8__Context__GetIsolate(self) };
166
167    let num_data_fields =
168      unsafe { v8__Context__GetNumberOfEmbedderDataFields(self) } as int;
169    if num_data_fields > Self::ANNEX_SLOT {
170      let annex_ptr = unsafe {
171        v8__Context__GetAlignedPointerFromEmbedderData(self, Self::ANNEX_SLOT)
172      } as *mut ContextAnnex;
173      if !annex_ptr.is_null() {
174        // SAFETY: This reference doesn't outlive the Context, so it can't outlive
175        // the annex itself. Also, any mutations or accesses to the annex after
176        // its creation require a mutable reference to the context's isolate, but
177        // such a mutable reference is consumed by this reference during its
178        // lifetime.
179        return Some(unsafe { &mut *annex_ptr });
180      }
181    }
182
183    if !create_if_not_present {
184      return None;
185    }
186
187    let annex = Box::new(ContextAnnex {
188      slots: Default::default(),
189      // Gets replaced later in the method.
190      self_weak: Weak::empty(isolate),
191    });
192    let annex_ptr = Box::into_raw(annex);
193    unsafe {
194      v8__Context__SetAlignedPointerInEmbedderData(
195        self,
196        Self::ANNEX_SLOT,
197        annex_ptr as *mut _,
198      );
199    };
200    assert!(
201      unsafe { v8__Context__GetNumberOfEmbedderDataFields(self) } as int
202        > Self::ANNEX_SLOT
203    );
204
205    // Make sure to drop the annex after the context is dropped, by creating a
206    // weak handle with a finalizer that drops the annex, and storing the weak
207    // in the annex itself.
208    let weak = {
209      // SAFETY: `self` can only have been derived from a `Local` or `Global`,
210      // and assuming the caller is only using safe code, the `Local` or
211      // `Global` must still be alive, so `self_ref_handle` won't outlive it.
212      // We also check above that `isolate` is the context's isolate.
213      let self_ref_handle = unsafe { UnsafeRefHandle::new(self, isolate) };
214
215      Weak::with_guaranteed_finalizer(
216        isolate,
217        self_ref_handle,
218        Box::new(move || {
219          // SAFETY: The lifetimes of references to the annex returned by this
220          // method are always tied to the context, and because this is the
221          // context's finalizer, we know there are no living references to
222          // the annex. And since the finalizer is only called once, the annex
223          // can't have been dropped before.
224          let _ = unsafe { Box::from_raw(annex_ptr) };
225        }),
226      )
227    };
228
229    // SAFETY: This reference doesn't outlive the Context, so it can't outlive
230    // the annex itself. Also, any mutations or accesses to the annex after
231    // its creation require a mutable reference to the context's isolate, but
232    // such a mutable reference is consumed by this reference during its
233    // lifetime.
234    let annex_mut = unsafe { &mut *annex_ptr };
235    annex_mut.self_weak = weak;
236    Some(annex_mut)
237  }
238
239  /// Get a reference to embedder data added with [`Self::set_slot()`].
240  #[inline(always)]
241  pub fn get_slot<T: 'static>(&self) -> Option<Rc<T>> {
242    if let Some(annex) = self.get_annex_mut(false) {
243      annex.slots.get(&TypeId::of::<T>()).map(|slot| {
244        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
245        // stored with T's TypeId as their key.
246        unsafe { slot.borrow::<Rc<T>>().clone() }
247      })
248    } else {
249      None
250    }
251  }
252
253  /// Use with [`Context::get_slot`] and [`Context::get_slot_mut`] to associate
254  /// state with a Context.
255  ///
256  /// This method gives ownership of value to the Context. Exactly one object of
257  /// each type can be associated with a Context. If called more than once with
258  /// an object of the same type, the earlier version will be dropped and
259  /// replaced.
260  ///
261  /// Returns true if value was set without replacing an existing value.
262  ///
263  /// The value will be dropped when the context is garbage collected.
264  #[inline(always)]
265  pub fn set_slot<T: 'static>(&self, value: Rc<T>) -> Option<Rc<T>> {
266    self
267      .get_annex_mut(true)
268      .unwrap()
269      .slots
270      .insert(TypeId::of::<T>(), RawSlot::new(value))
271      .map(|slot| {
272        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
273        // stored with T's TypeId as their key.
274        unsafe { slot.into_inner::<Rc<T>>() }
275      })
276  }
277
278  /// Removes the embedder data added with [`Self::set_slot()`] and returns it
279  /// if it exists.
280  #[inline(always)]
281  pub fn remove_slot<T: 'static>(&self) -> Option<Rc<T>> {
282    if let Some(annex) = self.get_annex_mut(false) {
283      annex.slots.remove(&TypeId::of::<T>()).map(|slot| {
284        // SAFETY: `Self::set_slot` guarantees that only values of type T will be
285        // stored with T's TypeId as their key.
286        unsafe { slot.into_inner::<Rc<T>>() }
287      })
288    } else {
289      None
290    }
291  }
292
293  /// Removes all embedder data added with [`Self::set_slot()`], and
294  /// deletes any internal state needed to keep track of such slots.
295  ///
296  /// This is needed to make a snapshot with
297  /// [`SnapshotCreator`](crate::SnapshotCreator), since the internal embedder
298  /// state uses [`Weak`] handles, which cannot be alive at the time of
299  /// snapshotting.
300  #[inline(always)]
301  pub fn clear_all_slots(&self) {
302    if let Some(annex_mut) = self.get_annex_mut(false) {
303      let annex_ptr = annex_mut as *mut ContextAnnex;
304      let _ = unsafe { Box::from_raw(annex_ptr) };
305      unsafe {
306        v8__Context__SetAlignedPointerInEmbedderData(
307          self,
308          Self::ANNEX_SLOT,
309          null_mut(),
310        );
311      };
312    }
313  }
314
315  /// Sets the embedder data with the given index, growing the data as needed.
316  ///
317  /// Note that index 0 currently has a special meaning for Chrome's debugger.
318  #[inline(always)]
319  pub fn set_embedder_data(&self, slot: i32, data: Local<'_, Value>) {
320    unsafe {
321      v8__Context__SetEmbedderData(self, slot, &*data);
322    }
323  }
324
325  /// Gets the embedder data with the given index, which must have been set by
326  /// a previous call to SetEmbedderData with the same index.
327  #[inline(always)]
328  pub fn get_embedder_data<'s>(
329    &self,
330    scope: &mut HandleScope<'s, ()>,
331    slot: i32,
332  ) -> Option<Local<'s, Value>> {
333    unsafe { scope.cast_local(|_| v8__Context__GetEmbedderData(self, slot)) }
334  }
335
336  #[inline(always)]
337  pub unsafe fn set_aligned_pointer_in_embedder_data(
338    &self,
339    slot: i32,
340    data: *mut c_void,
341  ) {
342    // Initialize the annex when slot count > INTERNAL_SLOT_COUNT.
343    self.get_annex_mut(true);
344
345    unsafe {
346      v8__Context__SetAlignedPointerInEmbedderData(
347        self,
348        slot + Self::INTERNAL_SLOT_COUNT,
349        data,
350      );
351    }
352  }
353
354  #[inline(always)]
355  pub fn get_aligned_pointer_from_embedder_data(
356    &self,
357    slot: i32,
358  ) -> *mut c_void {
359    unsafe {
360      v8__Context__GetAlignedPointerFromEmbedderData(
361        self,
362        slot + Self::INTERNAL_SLOT_COUNT,
363      )
364    }
365  }
366
367  /// Create a new context from a (non-default) context snapshot. There
368  /// is no way to provide a global object template since we do not create
369  /// a new global object from template, but we can reuse a global object.
370  pub fn from_snapshot<'s>(
371    scope: &mut HandleScope<'s, ()>,
372    context_snapshot_index: usize,
373    options: ContextOptions,
374  ) -> Option<Local<'s, Context>> {
375    unsafe {
376      scope.cast_local(|sd| {
377        v8__Context__FromSnapshot(
378          sd.get_isolate_mut(),
379          context_snapshot_index,
380          options.global_object.map_or_else(null, |o| &*o as *const _),
381          options.microtask_queue.unwrap_or_else(null_mut),
382        )
383      })
384    }
385  }
386
387  #[inline(always)]
388  pub fn get_security_token<'s>(
389    &self,
390    scope: &mut HandleScope<'s, ()>,
391  ) -> Local<'s, Value> {
392    unsafe { scope.cast_local(|_| v8__Context__GetSecurityToken(self)) }
393      .unwrap()
394  }
395
396  #[inline(always)]
397  pub fn set_security_token(&self, token: Local<Value>) {
398    unsafe {
399      v8__Context__SetSecurityToken(self, &*token);
400    }
401  }
402
403  #[inline(always)]
404  pub fn use_default_security_token(&self) {
405    unsafe {
406      v8__Context__UseDefaultSecurityToken(self);
407    }
408  }
409
410  pub fn set_allow_generation_from_strings(&self, allow: bool) {
411    unsafe {
412      v8__Context__AllowCodeGenerationFromStrings(self, allow);
413    }
414  }
415
416  pub fn is_code_generation_from_strings_allowed(&self) -> bool {
417    unsafe { v8__Context_IsCodeGenerationFromStringsAllowed(self) }
418  }
419}
420
421struct ContextAnnex {
422  slots: HashMap<TypeId, RawSlot, BuildTypeIdHasher>,
423  // In order to run the finalizer that drops the ContextAnnex when the Context
424  // is GC'd, the corresponding Weak must be kept alive until that time.
425  self_weak: Weak<Context>,
426}