Skip to main content

v8/
module.rs

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