v8/
module.rs

1use std::mem::MaybeUninit;
2use std::num::NonZeroI32;
3use std::ptr::null;
4
5use crate::Context;
6use crate::FixedArray;
7use crate::HandleScope;
8use crate::Isolate;
9use crate::Local;
10use crate::Message;
11use crate::Module;
12use crate::ModuleRequest;
13use crate::Object;
14use crate::String;
15use crate::UnboundModuleScript;
16use crate::Value;
17use crate::support::MapFnFrom;
18use crate::support::MapFnTo;
19use crate::support::MaybeBool;
20use crate::support::ToCFn;
21use crate::support::UnitType;
22use crate::support::int;
23
24/// Called during Module::instantiate_module. Provided with arguments:
25/// (context, specifier, import_attributes, referrer). Return None on error.
26///
27/// Note: this callback has an unusual signature due to ABI incompatibilities
28/// between Rust and C++. However end users can implement the callback as
29/// follows; it'll be automatically converted.
30///
31/// ```rust,ignore
32///   fn my_resolve_callback<'a>(
33///      context: v8::Local<'a, v8::Context>,
34///      specifier: v8::Local<'a, v8::String>,
35///      import_attributes: v8::Local<'a, v8::FixedArray>,
36///      referrer: v8::Local<'a, v8::Module>,
37///   ) -> Option<v8::Local<'a, v8::Module>> {
38///      // ...
39///      Some(resolved_module)
40///   }
41/// ```
42#[cfg(not(target_os = "windows"))]
43#[repr(C)]
44// System V ABI
45pub struct ResolveModuleCallbackRet(*const Module);
46
47#[cfg(not(target_os = "windows"))]
48pub type ResolveModuleCallback<'a> =
49  unsafe extern "C" fn(
50    Local<'a, Context>,
51    Local<'a, String>,
52    Local<'a, FixedArray>,
53    Local<'a, Module>,
54  ) -> ResolveModuleCallbackRet;
55
56// Windows x64 ABI: Local<Module> returned on the stack.
57#[cfg(target_os = "windows")]
58pub type ResolveModuleCallback<'a> = unsafe extern "C" fn(
59  *mut *const Module,
60  Local<'a, Context>,
61  Local<'a, String>,
62  Local<'a, FixedArray>,
63  Local<'a, Module>,
64)
65  -> *mut *const Module;
66
67impl<'a, F> MapFnFrom<F> for ResolveModuleCallback<'a>
68where
69  F: UnitType
70    + Fn(
71      Local<'a, Context>,
72      Local<'a, String>,
73      Local<'a, FixedArray>,
74      Local<'a, Module>,
75    ) -> Option<Local<'a, Module>>,
76{
77  #[cfg(not(target_os = "windows"))]
78  fn mapping() -> Self {
79    let f = |context, specifier, import_attributes, referrer| {
80      ResolveModuleCallbackRet(
81        (F::get())(context, specifier, import_attributes, referrer)
82          .map(|r| -> *const Module { &*r })
83          .unwrap_or(null()),
84      )
85    };
86    f.to_c_fn()
87  }
88
89  #[cfg(target_os = "windows")]
90  fn mapping() -> Self {
91    let f = |ret_ptr, context, specifier, import_attributes, referrer| {
92      let r = (F::get())(context, specifier, import_attributes, referrer)
93        .map(|r| -> *const Module { &*r })
94        .unwrap_or(null());
95      unsafe { std::ptr::write(ret_ptr, r) }; // Write result to stack.
96      ret_ptr // Return stack pointer to the return value.
97    };
98    f.to_c_fn()
99  }
100}
101
102// System V ABI.
103#[cfg(not(target_os = "windows"))]
104#[repr(C)]
105pub struct SyntheticModuleEvaluationStepsRet(*const Value);
106
107#[cfg(not(target_os = "windows"))]
108pub type SyntheticModuleEvaluationSteps<'a> =
109  unsafe extern "C" fn(
110    Local<'a, Context>,
111    Local<'a, Module>,
112  ) -> SyntheticModuleEvaluationStepsRet;
113
114// Windows x64 ABI: Local<Value> returned on the stack.
115#[cfg(target_os = "windows")]
116pub type SyntheticModuleEvaluationSteps<'a> =
117  unsafe extern "C" fn(
118    *mut *const Value,
119    Local<'a, Context>,
120    Local<'a, Module>,
121  ) -> *mut *const Value;
122
123impl<'a, F> MapFnFrom<F> for SyntheticModuleEvaluationSteps<'a>
124where
125  F: UnitType
126    + Fn(Local<'a, Context>, Local<'a, Module>) -> Option<Local<'a, Value>>,
127{
128  #[cfg(not(target_os = "windows"))]
129  fn mapping() -> Self {
130    let f = |context, module| {
131      SyntheticModuleEvaluationStepsRet(
132        (F::get())(context, module).map_or(null(), |r| -> *const Value { &*r }),
133      )
134    };
135    f.to_c_fn()
136  }
137
138  #[cfg(target_os = "windows")]
139  fn mapping() -> Self {
140    let f = |ret_ptr, context, module| {
141      let r = (F::get())(context, module)
142        .map(|r| -> *const Value { &*r })
143        .unwrap_or(null());
144      unsafe { std::ptr::write(ret_ptr, r) }; // Write result to stack.
145      ret_ptr // Return stack pointer to the return value.
146    };
147    f.to_c_fn()
148  }
149}
150
151// System V ABI
152#[cfg(not(target_os = "windows"))]
153#[repr(C)]
154pub struct ResolveSourceCallbackRet(*const Object);
155
156#[cfg(not(target_os = "windows"))]
157pub type ResolveSourceCallback<'a> =
158  unsafe extern "C" fn(
159    Local<'a, Context>,
160    Local<'a, String>,
161    Local<'a, FixedArray>,
162    Local<'a, Module>,
163  ) -> ResolveSourceCallbackRet;
164
165// Windows x64 ABI: Local<Module> returned on the stack.
166#[cfg(target_os = "windows")]
167pub type ResolveSourceCallback<'a> = unsafe extern "C" fn(
168  *mut *const Object,
169  Local<'a, Context>,
170  Local<'a, String>,
171  Local<'a, FixedArray>,
172  Local<'a, Module>,
173)
174  -> *mut *const Object;
175
176impl<'a, F> MapFnFrom<F> for ResolveSourceCallback<'a>
177where
178  F: UnitType
179    + Fn(
180      Local<'a, Context>,
181      Local<'a, String>,
182      Local<'a, FixedArray>,
183      Local<'a, Module>,
184    ) -> Option<Local<'a, Object>>,
185{
186  #[cfg(not(target_os = "windows"))]
187  fn mapping() -> Self {
188    let f = |context, specifier, import_attributes, referrer| {
189      ResolveSourceCallbackRet(
190        (F::get())(context, specifier, import_attributes, referrer)
191          .map(|r| -> *const Object { &*r })
192          .unwrap_or(null()),
193      )
194    };
195    f.to_c_fn()
196  }
197
198  #[cfg(target_os = "windows")]
199  fn mapping() -> Self {
200    let f = |ret_ptr, context, specifier, import_attributes, referrer| {
201      let r = (F::get())(context, specifier, import_attributes, referrer)
202        .map(|r| -> *const Object { &*r })
203        .unwrap_or(null());
204      unsafe { std::ptr::write(ret_ptr, r) }; // Write result to stack.
205      ret_ptr // Return stack pointer to the return value.
206    };
207    f.to_c_fn()
208  }
209}
210
211unsafe extern "C" {
212  fn v8__Module__GetStatus(this: *const Module) -> ModuleStatus;
213  fn v8__Module__GetException(this: *const Module) -> *const Value;
214  fn v8__Module__GetModuleRequests(this: *const Module) -> *const FixedArray;
215  fn v8__Module__SourceOffsetToLocation(
216    this: *const Module,
217    offset: int,
218    out: *mut Location,
219  );
220  fn v8__Module__GetModuleNamespace(this: *const Module) -> *const Value;
221  fn v8__Module__GetIdentityHash(this: *const Module) -> int;
222  fn v8__Module__ScriptId(this: *const Module) -> int;
223  fn v8__Module__InstantiateModule(
224    this: *const Module,
225    context: *const Context,
226    cb: ResolveModuleCallback,
227    source_callback: Option<ResolveSourceCallback>,
228  ) -> MaybeBool;
229  fn v8__Module__Evaluate(
230    this: *const Module,
231    context: *const Context,
232  ) -> *const Value;
233  fn v8__Module__IsGraphAsync(this: *const Module) -> bool;
234  fn v8__Module__IsSourceTextModule(this: *const Module) -> bool;
235  fn v8__Module__IsSyntheticModule(this: *const Module) -> bool;
236  fn v8__Module__CreateSyntheticModule(
237    isolate: *const Isolate,
238    module_name: *const String,
239    export_names_len: usize,
240    export_names_raw: *const *const String,
241    evaluation_steps: SyntheticModuleEvaluationSteps,
242  ) -> *const Module;
243  fn v8__Module__SetSyntheticModuleExport(
244    this: *const Module,
245    isolate: *const Isolate,
246    export_name: *const String,
247    export_value: *const Value,
248  ) -> MaybeBool;
249  fn v8__Module__GetUnboundModuleScript(
250    this: *const Module,
251  ) -> *const UnboundModuleScript;
252  fn v8__Location__GetLineNumber(this: *const Location) -> int;
253  fn v8__Location__GetColumnNumber(this: *const Location) -> int;
254  fn v8__ModuleRequest__GetSpecifier(
255    this: *const ModuleRequest,
256  ) -> *const String;
257  fn v8__ModuleRequest__GetSourceOffset(this: *const ModuleRequest) -> int;
258  fn v8__ModuleRequest__GetImportAttributes(
259    this: *const ModuleRequest,
260  ) -> *const FixedArray;
261  fn v8__Module__GetStalledTopLevelAwaitMessage(
262    this: *const Module,
263    isolate: *const Isolate,
264    out_vec: *mut StalledTopLevelAwaitMessage,
265    vec_len: usize,
266  ) -> usize;
267}
268
269#[repr(C)]
270pub struct StalledTopLevelAwaitMessage {
271  pub module: *const Module,
272  pub message: *const Message,
273}
274
275/// A location in JavaScript source.
276#[repr(C)]
277#[derive(Debug)]
278pub struct Location([i32; 2]);
279
280impl Location {
281  pub fn get_line_number(&self) -> int {
282    unsafe { v8__Location__GetLineNumber(self) }
283  }
284
285  pub fn get_column_number(&self) -> int {
286    unsafe { v8__Location__GetColumnNumber(self) }
287  }
288}
289
290/// The different states a module can be in.
291///
292/// This corresponds to the states used in ECMAScript except that "evaluated"
293/// is split into kEvaluated and kErrored, indicating success and failure,
294/// respectively.
295#[derive(Debug, PartialEq, Eq)]
296#[repr(C)]
297pub enum ModuleStatus {
298  Uninstantiated,
299  Instantiating,
300  Instantiated,
301  Evaluating,
302  Evaluated,
303  Errored,
304}
305
306impl Module {
307  /// Returns the module's current status.
308  #[inline(always)]
309  pub fn get_status(&self) -> ModuleStatus {
310    unsafe { v8__Module__GetStatus(self) }
311  }
312
313  /// For a module in kErrored status, this returns the corresponding exception.
314  #[inline(always)]
315  pub fn get_exception(&self) -> Local<Value> {
316    // Note: the returned value is not actually stored in a HandleScope,
317    // therefore we don't need a scope object here.
318    unsafe { Local::from_raw(v8__Module__GetException(self)) }.unwrap()
319  }
320
321  /// Returns the ModuleRequests for this module.
322  #[inline(always)]
323  pub fn get_module_requests(&self) -> Local<FixedArray> {
324    unsafe { Local::from_raw(v8__Module__GetModuleRequests(self)) }.unwrap()
325  }
326
327  /// For the given source text offset in this module, returns the corresponding
328  /// Location with line and column numbers.
329  #[inline(always)]
330  pub fn source_offset_to_location(&self, offset: int) -> Location {
331    let mut out = MaybeUninit::<Location>::uninit();
332    unsafe {
333      v8__Module__SourceOffsetToLocation(self, offset, out.as_mut_ptr());
334      out.assume_init()
335    }
336  }
337
338  /// Returns the V8 hash value for this value. The current implementation
339  /// uses a hidden property to store the identity hash.
340  ///
341  /// The return value will never be 0. Also, it is not guaranteed to be
342  /// unique.
343  #[inline(always)]
344  pub fn get_identity_hash(&self) -> NonZeroI32 {
345    unsafe { NonZeroI32::new_unchecked(v8__Module__GetIdentityHash(self)) }
346  }
347
348  /// Returns the underlying script's id.
349  ///
350  /// The module must be a SourceTextModule and must not have an Errored status.
351  #[inline(always)]
352  pub fn script_id(&self) -> Option<int> {
353    if !self.is_source_text_module() {
354      return None;
355    }
356    if self.get_status() == ModuleStatus::Errored {
357      return None;
358    }
359    Some(unsafe { v8__Module__ScriptId(self) })
360  }
361
362  /// Returns the namespace object of this module.
363  ///
364  /// The module's status must be at least kInstantiated.
365  #[inline(always)]
366  pub fn get_module_namespace(&self) -> Local<Value> {
367    // Note: the returned value is not actually stored in a HandleScope,
368    // therefore we don't need a scope object here.
369    unsafe { Local::from_raw(v8__Module__GetModuleNamespace(self)).unwrap() }
370  }
371
372  /// Instantiates the module and its dependencies.
373  ///
374  /// Returns an empty Maybe<bool> if an exception occurred during
375  /// instantiation. (In the case where the callback throws an exception, that
376  /// exception is propagated.)
377  #[must_use]
378  #[inline(always)]
379  pub fn instantiate_module<'a>(
380    &self,
381    scope: &mut HandleScope,
382    callback: impl MapFnTo<ResolveModuleCallback<'a>>,
383  ) -> Option<bool> {
384    unsafe {
385      v8__Module__InstantiateModule(
386        self,
387        &*scope.get_current_context(),
388        callback.map_fn_to(),
389        None,
390      )
391    }
392    .into()
393  }
394
395  /// Instantiates the module and its dependencies.
396  ///
397  /// Returns an empty Maybe<bool> if an exception occurred during
398  /// instantiation. (In the case where the callback throws an exception, that
399  /// exception is propagated.)
400  #[must_use]
401  #[inline(always)]
402  pub fn instantiate_module2<'a>(
403    &self,
404    scope: &mut HandleScope,
405    callback: impl MapFnTo<ResolveModuleCallback<'a>>,
406    source_callback: impl MapFnTo<ResolveSourceCallback<'a>>,
407  ) -> Option<bool> {
408    unsafe {
409      v8__Module__InstantiateModule(
410        self,
411        &*scope.get_current_context(),
412        callback.map_fn_to(),
413        Some(source_callback.map_fn_to()),
414      )
415    }
416    .into()
417  }
418
419  /// Evaluates the module and its dependencies.
420  ///
421  /// If status is kInstantiated, run the module's code. On success, set status
422  /// to kEvaluated and return the completion value; on failure, set status to
423  /// kErrored and propagate the thrown exception (which is then also available
424  /// via |GetException|).
425  #[must_use]
426  #[inline(always)]
427  pub fn evaluate<'s>(
428    &self,
429    scope: &mut HandleScope<'s>,
430  ) -> Option<Local<'s, Value>> {
431    unsafe {
432      scope
433        .cast_local(|sd| v8__Module__Evaluate(self, sd.get_current_context()))
434    }
435  }
436
437  /// Returns whether this module or any of its requested modules is async,
438  /// i.e. contains top-level await.
439  ///
440  /// The module's status must be at least kInstantiated.
441  #[inline(always)]
442  pub fn is_graph_async(&self) -> bool {
443    unsafe { v8__Module__IsGraphAsync(self) }
444  }
445
446  /// Returns whether the module is a SourceTextModule.
447  #[inline(always)]
448  pub fn is_source_text_module(&self) -> bool {
449    unsafe { v8__Module__IsSourceTextModule(self) }
450  }
451
452  /// Returns whether the module is a SyntheticModule.
453  #[inline(always)]
454  pub fn is_synthetic_module(&self) -> bool {
455    unsafe { v8__Module__IsSyntheticModule(self) }
456  }
457
458  /// Creates a new SyntheticModule with the specified export names, where
459  /// evaluation_steps will be executed upon module evaluation.
460  /// export_names must not contain duplicates.
461  /// module_name is used solely for logging/debugging and doesn't affect module
462  /// behavior.
463  #[inline(always)]
464  pub fn create_synthetic_module<'s, 'a>(
465    scope: &mut HandleScope<'s>,
466    module_name: Local<String>,
467    export_names: &[Local<String>],
468    evaluation_steps: impl MapFnTo<SyntheticModuleEvaluationSteps<'a>>,
469  ) -> Local<'s, Module> {
470    let export_names = Local::slice_into_raw(export_names);
471    let export_names_len = export_names.len();
472    let export_names = export_names.as_ptr();
473    unsafe {
474      scope
475        .cast_local(|sd| {
476          v8__Module__CreateSyntheticModule(
477            sd.get_isolate_ptr(),
478            &*module_name,
479            export_names_len,
480            export_names,
481            evaluation_steps.map_fn_to(),
482          )
483        })
484        .unwrap()
485    }
486  }
487
488  /// Set this module's exported value for the name export_name to the specified
489  /// export_value. This method must be called only on Modules created via
490  /// create_synthetic_module.  An error will be thrown if export_name is not one
491  /// of the export_names that were passed in that create_synthetic_module call.
492  /// Returns Some(true) on success, None if an error was thrown.
493  #[must_use]
494  #[inline(always)]
495  pub fn set_synthetic_module_export(
496    &self,
497    scope: &mut HandleScope,
498    export_name: Local<String>,
499    export_value: Local<Value>,
500  ) -> Option<bool> {
501    unsafe {
502      v8__Module__SetSyntheticModuleExport(
503        self,
504        scope.get_isolate_ptr(),
505        &*export_name,
506        &*export_value,
507      )
508    }
509    .into()
510  }
511
512  #[inline(always)]
513  pub fn get_unbound_module_script<'s>(
514    &self,
515    scope: &mut HandleScope<'s>,
516  ) -> Local<'s, UnboundModuleScript> {
517    unsafe {
518      scope
519        .cast_local(|_| v8__Module__GetUnboundModuleScript(self))
520        .unwrap()
521    }
522  }
523
524  /// Search the modules requested directly or indirectly by the module for
525  /// any top-level await that has not yet resolved. If there is any, the
526  /// returned vector contains a tuple of the unresolved module and a message
527  /// with the pending top-level await.
528  /// An embedder may call this before exiting to improve error messages.
529  pub fn get_stalled_top_level_await_message(
530    &self,
531    scope: &mut HandleScope,
532  ) -> Vec<(Local<Module>, Local<Message>)> {
533    let mut out_vec: Vec<StalledTopLevelAwaitMessage> = Vec::with_capacity(16);
534    for _i in 0..16 {
535      out_vec.push(StalledTopLevelAwaitMessage {
536        module: std::ptr::null(),
537        message: std::ptr::null(),
538      });
539    }
540
541    let returned_len = unsafe {
542      v8__Module__GetStalledTopLevelAwaitMessage(
543        self,
544        scope.get_isolate_ptr(),
545        out_vec.as_mut_ptr(),
546        out_vec.len(),
547      )
548    };
549
550    let mut ret_vec = Vec::with_capacity(returned_len);
551    for item in out_vec.iter().take(returned_len) {
552      unsafe {
553        ret_vec.push((
554          Local::from_raw(item.module).unwrap(),
555          Local::from_raw(item.message).unwrap(),
556        ));
557      }
558    }
559    ret_vec
560  }
561}
562
563impl ModuleRequest {
564  /// Returns the module specifier for this ModuleRequest.
565  #[inline(always)]
566  pub fn get_specifier(&self) -> Local<String> {
567    unsafe { Local::from_raw(v8__ModuleRequest__GetSpecifier(self)) }.unwrap()
568  }
569
570  /// Returns the source code offset of this module request.
571  /// Use Module::source_offset_to_location to convert this to line/column numbers.
572  #[inline(always)]
573  pub fn get_source_offset(&self) -> int {
574    unsafe { v8__ModuleRequest__GetSourceOffset(self) }
575  }
576
577  /// Contains the import attributes for this request in the form:
578  /// [key1, value1, source_offset1, key2, value2, source_offset2, ...].
579  /// The keys and values are of type v8::String, and the source offsets are of
580  /// type Int32. Use Module::source_offset_to_location to convert the source
581  /// offsets to Locations with line/column numbers.
582  ///
583  /// All assertions present in the module request will be supplied in this
584  /// list, regardless of whether they are supported by the host. Per
585  /// https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions,
586  /// hosts are expected to ignore assertions that they do not support (as
587  /// opposed to, for example, triggering an error if an unsupported assertion is
588  /// present).
589  #[inline(always)]
590  pub fn get_import_attributes(&self) -> Local<FixedArray> {
591    unsafe { Local::from_raw(v8__ModuleRequest__GetImportAttributes(self)) }
592      .unwrap()
593  }
594}