Skip to main content

v8/
script_compiler.rs

1// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
2use std::{marker::PhantomData, mem::MaybeUninit};
3
4use crate::Function;
5use crate::Local;
6use crate::Module;
7use crate::Object;
8use crate::ScriptOrigin;
9use crate::String;
10use crate::UniqueRef;
11use crate::isolate::RealIsolate;
12use crate::scope::PinScope;
13use crate::support::int;
14use crate::{Context, Script, UnboundScript};
15
16unsafe extern "C" {
17  fn v8__ScriptCompiler__Source__CONSTRUCT(
18    buf: *mut MaybeUninit<Source>,
19    source_string: *const String,
20    origin: *const ScriptOrigin,
21    cached_data: *mut CachedData,
22  );
23  fn v8__ScriptCompiler__Source__DESTRUCT(this: *mut Source);
24  fn v8__ScriptCompiler__Source__GetCachedData<'a>(
25    this: *const Source,
26  ) -> *const CachedData<'a>;
27  fn v8__ScriptCompiler__CachedData__NEW<'a>(
28    data: *const u8,
29    length: i32,
30  ) -> *mut CachedData<'a>;
31  fn v8__ScriptCompiler__CachedData__DELETE<'a>(this: *mut CachedData<'a>);
32  fn v8__ScriptCompiler__CompileModule(
33    isolate: *mut RealIsolate,
34    source: *mut Source,
35    options: CompileOptions,
36    no_cache_reason: NoCacheReason,
37  ) -> *const Module;
38  fn v8__ScriptCompiler__Compile(
39    context: *const Context,
40    source: *mut Source,
41    options: CompileOptions,
42    no_cache_reason: NoCacheReason,
43  ) -> *const Script;
44  fn v8__ScriptCompiler__CompileFunction(
45    context: *const Context,
46    source: *mut Source,
47    arguments_count: usize,
48    arguments: *const *const String,
49    context_extensions_count: usize,
50    context_extensions: *const *const Object,
51    options: CompileOptions,
52    no_cache_reason: NoCacheReason,
53  ) -> *const Function;
54  fn v8__ScriptCompiler__CompileUnboundScript(
55    isolate: *mut RealIsolate,
56    source: *mut Source,
57    options: CompileOptions,
58    no_cache_reason: NoCacheReason,
59  ) -> *const UnboundScript;
60
61  fn v8__ScriptCompiler__CachedDataVersionTag() -> u32;
62}
63
64/// Source code which can then be compiled to a UnboundScript or Script.
65#[repr(C)]
66#[derive(Debug)]
67pub struct Source {
68  _source_string: usize,
69  _resource_name: usize,
70  _resource_line_offset: int,
71  _resource_column_offset: int,
72  _resource_options: int,
73  _source_map_url: usize,
74  _host_defined_options: usize,
75  _cached_data: usize,
76  _consume_cache_task: usize,
77  _compile_hint_callback: usize,
78  _compile_hint_callback_data: usize,
79  _compilation_details: [usize; 3],
80}
81
82/// Compilation data that the embedder can cache and pass back to speed up future
83/// compilations. The data is produced if the CompilerOptions passed to the compilation
84/// functions in ScriptCompiler contains produce_data_to_cache = true. The data to cache
85/// can then can be retrieved from UnboundScript.
86#[repr(C)]
87#[derive(Debug)]
88pub struct CachedData<'a> {
89  data: *const u8,
90  length: i32,
91  rejected: bool,
92  buffer_policy: BufferPolicy,
93  _phantom: PhantomData<&'a ()>,
94}
95
96impl Drop for CachedData<'_> {
97  fn drop(&mut self) {
98    unsafe {
99      v8__ScriptCompiler__CachedData__DELETE(self);
100    }
101  }
102}
103
104impl<'a> CachedData<'a> {
105  pub fn new(data: &'a [u8]) -> UniqueRef<Self> {
106    let cached_data = unsafe {
107      UniqueRef::from_raw(v8__ScriptCompiler__CachedData__NEW(
108        data.as_ptr(),
109        data.len() as i32,
110      ))
111    };
112    debug_assert_eq!(
113      cached_data.buffer_policy(),
114      crate::script_compiler::BufferPolicy::BufferNotOwned
115    );
116    cached_data
117  }
118
119  #[inline(always)]
120  pub(crate) fn buffer_policy(&self) -> BufferPolicy {
121    self.buffer_policy
122  }
123
124  #[inline(always)]
125  pub fn rejected(&self) -> bool {
126    self.rejected
127  }
128}
129
130impl std::ops::Deref for CachedData<'_> {
131  type Target = [u8];
132  fn deref(&self) -> &Self::Target {
133    unsafe { std::slice::from_raw_parts(self.data, self.length as usize) }
134  }
135}
136
137#[allow(dead_code)]
138#[repr(C)]
139#[derive(Clone, Copy, Debug, PartialEq)]
140pub(crate) enum BufferPolicy {
141  BufferNotOwned = 0,
142  BufferOwned,
143}
144
145impl Source {
146  #[inline(always)]
147  pub fn new(
148    source_string: Local<String>,
149    origin: Option<&ScriptOrigin>,
150  ) -> Self {
151    let mut buf = MaybeUninit::<Self>::uninit();
152    unsafe {
153      v8__ScriptCompiler__Source__CONSTRUCT(
154        &mut buf,
155        &*source_string,
156        origin.map_or(std::ptr::null(), |x| x as *const _),
157        std::ptr::null_mut(),
158      );
159      buf.assume_init()
160    }
161  }
162
163  #[inline(always)]
164  pub fn new_with_cached_data(
165    source_string: Local<String>,
166    origin: Option<&ScriptOrigin>,
167    cached_data: UniqueRef<CachedData>,
168  ) -> Self {
169    let mut buf = MaybeUninit::<Self>::uninit();
170    unsafe {
171      v8__ScriptCompiler__Source__CONSTRUCT(
172        &mut buf,
173        &*source_string,
174        origin.map_or(std::ptr::null(), |x| x as *const _),
175        cached_data.into_raw(), // Source constructor takes ownership.
176      );
177      buf.assume_init()
178    }
179  }
180
181  #[inline(always)]
182  pub fn get_cached_data(&self) -> Option<&CachedData<'_>> {
183    unsafe {
184      let cached_data = v8__ScriptCompiler__Source__GetCachedData(self);
185      if cached_data.is_null() {
186        None
187      } else {
188        Some(&*cached_data)
189      }
190    }
191  }
192}
193
194impl Drop for Source {
195  fn drop(&mut self) {
196    unsafe { v8__ScriptCompiler__Source__DESTRUCT(self) }
197  }
198}
199
200bitflags! {
201  #[derive(Debug, Clone, Copy, PartialEq)]
202  #[repr(transparent)]
203  pub struct CompileOptions: int {
204    const NoCompileOptions = 0;
205    const ConsumeCodeCache = 1 << 0;
206    const EagerCompile = 1 << 1;
207    const ProduceCompileHints = 1 << 2;
208    const ConsumeCompileHints = 1 << 3;
209    const FollowCompileHintsMagicComment = 1 << 4;
210    const FollowCompileHintsPerFunctionMagicComment = 1 << 5;
211  }
212}
213
214/// The reason for which we are not requesting or providing a code cache.
215#[repr(C)]
216#[derive(Debug)]
217pub enum NoCacheReason {
218  NoReason = 0,
219  BecauseCachingDisabled,
220  BecauseNoResource,
221  BecauseInlineScript,
222  BecauseModule,
223  BecauseStreamingSource,
224  BecauseInspector,
225  BecauseScriptTooSmall,
226  BecauseCacheTooCold,
227  BecauseV8Extension,
228  BecauseExtensionModule,
229  BecausePacScript,
230  BecauseInDocumentWrite,
231  BecauseResourceWithNoCacheHandler,
232  BecauseDeferredProduceCodeCache,
233}
234
235/// Compile an ES module, returning a Module that encapsulates the compiled
236/// code.
237///
238/// Corresponds to the ParseModule abstract operation in the ECMAScript
239/// specification.
240#[inline(always)]
241pub fn compile_module<'s>(
242  scope: &PinScope<'s, '_>,
243  source: &mut Source,
244) -> Option<Local<'s, Module>> {
245  compile_module2(
246    scope,
247    source,
248    CompileOptions::NoCompileOptions,
249    NoCacheReason::NoReason,
250  )
251}
252
253/// Same as compile_module with more options.
254#[inline(always)]
255pub fn compile_module2<'s>(
256  scope: &PinScope<'s, '_>,
257  source: &mut Source,
258  options: CompileOptions,
259  no_cache_reason: NoCacheReason,
260) -> Option<Local<'s, Module>> {
261  unsafe {
262    scope.cast_local(|sd| {
263      v8__ScriptCompiler__CompileModule(
264        sd.get_isolate_ptr(),
265        source,
266        options,
267        no_cache_reason,
268      )
269    })
270  }
271}
272
273#[inline(always)]
274pub fn compile<'s>(
275  scope: &PinScope<'s, '_>,
276  source: &mut Source,
277  options: CompileOptions,
278  no_cache_reason: NoCacheReason,
279) -> Option<Local<'s, Script>> {
280  unsafe {
281    scope.cast_local(|sd| {
282      v8__ScriptCompiler__Compile(
283        &*sd.get_current_context(),
284        source,
285        options,
286        no_cache_reason,
287      )
288    })
289  }
290}
291
292#[inline(always)]
293pub fn compile_function<'s>(
294  scope: &PinScope<'s, '_>,
295  source: &mut Source,
296  arguments: &[Local<String>],
297  context_extensions: &[Local<Object>],
298  options: CompileOptions,
299  no_cache_reason: NoCacheReason,
300) -> Option<Local<'s, Function>> {
301  let arguments = Local::slice_into_raw(arguments);
302  let context_extensions = Local::slice_into_raw(context_extensions);
303  unsafe {
304    scope.cast_local(|sd| {
305      v8__ScriptCompiler__CompileFunction(
306        &*sd.get_current_context(),
307        source,
308        arguments.len(),
309        arguments.as_ptr(),
310        context_extensions.len(),
311        context_extensions.as_ptr(),
312        options,
313        no_cache_reason,
314      )
315    })
316  }
317}
318
319#[inline(always)]
320pub fn compile_unbound_script<'s>(
321  scope: &PinScope<'s, '_>,
322  source: &mut Source,
323  options: CompileOptions,
324  no_cache_reason: NoCacheReason,
325) -> Option<Local<'s, UnboundScript>> {
326  unsafe {
327    scope.cast_local(|sd| {
328      v8__ScriptCompiler__CompileUnboundScript(
329        sd.get_isolate_ptr(),
330        source,
331        options,
332        no_cache_reason,
333      )
334    })
335  }
336}
337
338/// Return a version tag for CachedData for the current V8 version & flags.
339///
340/// This value is meant only for determining whether a previously generated
341/// CachedData instance is still valid; the tag has no other meaing.
342///
343/// Background: The data carried by CachedData may depend on the exact V8
344/// version number or current compiler flags. This means that when persisting
345/// CachedData, the embedder must take care to not pass in data from another V8
346/// version, or the same version with different features enabled.
347///
348/// The easiest way to do so is to clear the embedder's cache on any such
349/// change.
350///
351/// Alternatively, this tag can be stored alongside the cached data and compared
352/// when it is being used.
353#[inline(always)]
354pub fn cached_data_version_tag() -> u32 {
355  unsafe { v8__ScriptCompiler__CachedDataVersionTag() }
356}