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__GetIdentityHash(this: *const Module) -> int;
224  fn v8__Module__ScriptId(this: *const Module) -> int;
225  fn v8__Module__InstantiateModule(
226    this: *const Module,
227    context: *const Context,
228    cb: ResolveModuleCallback,
229    source_callback: Option<ResolveSourceCallback>,
230  ) -> MaybeBool;
231  fn v8__Module__Evaluate(
232    this: *const Module,
233    context: *const Context,
234  ) -> *const Value;
235  fn v8__Module__IsGraphAsync(this: *const Module) -> bool;
236  fn v8__Module__IsSourceTextModule(this: *const Module) -> bool;
237  fn v8__Module__IsSyntheticModule(this: *const Module) -> bool;
238  fn v8__Module__CreateSyntheticModule(
239    isolate: *const RealIsolate,
240    module_name: *const String,
241    export_names_len: usize,
242    export_names_raw: *const *const String,
243    evaluation_steps: SyntheticModuleEvaluationSteps,
244  ) -> *const Module;
245  fn v8__Module__SetSyntheticModuleExport(
246    this: *const Module,
247    isolate: *const RealIsolate,
248    export_name: *const String,
249    export_value: *const Value,
250  ) -> MaybeBool;
251  fn v8__Module__GetUnboundModuleScript(
252    this: *const Module,
253  ) -> *const UnboundModuleScript;
254  fn v8__Location__GetLineNumber(this: *const Location) -> int;
255  fn v8__Location__GetColumnNumber(this: *const Location) -> int;
256  fn v8__ModuleRequest__GetSpecifier(
257    this: *const ModuleRequest,
258  ) -> *const String;
259  fn v8__ModuleRequest__GetPhase(
260    this: *const ModuleRequest,
261  ) -> ModuleImportPhase;
262  fn v8__ModuleRequest__GetSourceOffset(this: *const ModuleRequest) -> int;
263  fn v8__ModuleRequest__GetImportAttributes(
264    this: *const ModuleRequest,
265  ) -> *const FixedArray;
266  fn v8__Module__GetStalledTopLevelAwaitMessage(
267    this: *const Module,
268    isolate: *const RealIsolate,
269    out_vec: *mut StalledTopLevelAwaitMessage,
270    vec_len: usize,
271  ) -> usize;
272}
273
274#[repr(C)]
275pub struct StalledTopLevelAwaitMessage {
276  pub module: *const Module,
277  pub message: *const Message,
278}
279
280/// A location in JavaScript source.
281#[repr(C)]
282#[derive(Debug)]
283pub struct Location([i32; 2]);
284
285impl Location {
286  pub fn get_line_number(&self) -> int {
287    unsafe { v8__Location__GetLineNumber(self) }
288  }
289
290  pub fn get_column_number(&self) -> int {
291    unsafe { v8__Location__GetColumnNumber(self) }
292  }
293}
294
295/// The different states a module can be in.
296///
297/// This corresponds to the states used in ECMAScript except that "evaluated"
298/// is split into kEvaluated and kErrored, indicating success and failure,
299/// respectively.
300#[derive(Debug, PartialEq, Eq)]
301#[repr(C)]
302pub enum ModuleStatus {
303  Uninstantiated,
304  Instantiating,
305  Instantiated,
306  Evaluating,
307  Evaluated,
308  Errored,
309}
310
311impl Module {
312  /// Returns the module's current status.
313  #[inline(always)]
314  pub fn get_status(&self) -> ModuleStatus {
315    unsafe { v8__Module__GetStatus(self) }
316  }
317
318  /// For a module in kErrored status, this returns the corresponding exception.
319  #[inline(always)]
320  pub fn get_exception<'o>(&self) -> Local<'o, Value> {
321    // Note: the returned value is not actually stored in a HandleScope,
322    // therefore we don't need a scope object here.
323    unsafe { Local::from_raw(v8__Module__GetException(self)) }.unwrap()
324  }
325
326  /// Returns the ModuleRequests for this module.
327  #[inline(always)]
328  pub fn get_module_requests<'o>(&self) -> Local<'o, FixedArray> {
329    unsafe { Local::from_raw(v8__Module__GetModuleRequests(self)) }.unwrap()
330  }
331
332  /// For the given source text offset in this module, returns the corresponding
333  /// Location with line and column numbers.
334  #[inline(always)]
335  pub fn source_offset_to_location(&self, offset: int) -> Location {
336    let mut out = MaybeUninit::<Location>::uninit();
337    unsafe {
338      v8__Module__SourceOffsetToLocation(self, offset, out.as_mut_ptr());
339      out.assume_init()
340    }
341  }
342
343  /// Returns the V8 hash value for this value. The current implementation
344  /// uses a hidden property to store the identity hash.
345  ///
346  /// The return value will never be 0. Also, it is not guaranteed to be
347  /// unique.
348  #[inline(always)]
349  pub fn get_identity_hash(&self) -> NonZeroI32 {
350    unsafe { NonZeroI32::new_unchecked(v8__Module__GetIdentityHash(self)) }
351  }
352
353  /// Returns the underlying script's id.
354  ///
355  /// The module must be a SourceTextModule and must not have an Errored status.
356  #[inline(always)]
357  pub fn script_id(&self) -> Option<int> {
358    if !self.is_source_text_module() {
359      return None;
360    }
361    if self.get_status() == ModuleStatus::Errored {
362      return None;
363    }
364    Some(unsafe { v8__Module__ScriptId(self) })
365  }
366
367  /// Returns the namespace object of this module.
368  ///
369  /// The module's status must be at least kInstantiated.
370  #[inline(always)]
371  pub fn get_module_namespace<'o>(&self) -> Local<'o, Value> {
372    // Note: the returned value is not actually stored in a HandleScope,
373    // therefore we don't need a scope object here.
374    unsafe { Local::from_raw(v8__Module__GetModuleNamespace(self)).unwrap() }
375  }
376
377  /// Instantiates the module and its dependencies.
378  ///
379  /// Returns an empty Maybe<bool> if an exception occurred during
380  /// instantiation. (In the case where the callback throws an exception, that
381  /// exception is propagated.)
382  #[must_use]
383  #[inline(always)]
384  pub fn instantiate_module<'s, 'i>(
385    &self,
386    scope: &PinScope<'s, 'i>,
387    callback: impl MapFnTo<ResolveModuleCallback<'s>>,
388  ) -> Option<bool> {
389    unsafe {
390      v8__Module__InstantiateModule(
391        self,
392        &*scope.get_current_context(),
393        callback.map_fn_to(),
394        None,
395      )
396    }
397    .into()
398  }
399
400  /// Instantiates the module and its dependencies.
401  ///
402  /// Returns an empty Maybe<bool> if an exception occurred during
403  /// instantiation. (In the case where the callback throws an exception, that
404  /// exception is propagated.)
405  #[must_use]
406  #[inline(always)]
407  pub fn instantiate_module2<'s, 'i>(
408    &self,
409    scope: &PinScope<'s, 'i>,
410    callback: impl MapFnTo<ResolveModuleCallback<'s>>,
411    source_callback: impl MapFnTo<ResolveSourceCallback<'s>>,
412  ) -> Option<bool> {
413    unsafe {
414      v8__Module__InstantiateModule(
415        self,
416        &*scope.get_current_context(),
417        callback.map_fn_to(),
418        Some(source_callback.map_fn_to()),
419      )
420    }
421    .into()
422  }
423
424  /// Evaluates the module and its dependencies.
425  ///
426  /// If status is kInstantiated, run the module's code. On success, set status
427  /// to kEvaluated and return the completion value; on failure, set status to
428  /// kErrored and propagate the thrown exception (which is then also available
429  /// via |GetException|).
430  #[must_use]
431  #[inline(always)]
432  pub fn evaluate<'s>(
433    &self,
434    scope: &PinScope<'s, '_>,
435  ) -> Option<Local<'s, Value>> {
436    unsafe {
437      scope
438        .cast_local(|sd| v8__Module__Evaluate(self, sd.get_current_context()))
439    }
440  }
441
442  /// Returns whether this module or any of its requested modules is async,
443  /// i.e. contains top-level await.
444  ///
445  /// The module's status must be at least kInstantiated.
446  #[inline(always)]
447  pub fn is_graph_async(&self) -> bool {
448    unsafe { v8__Module__IsGraphAsync(self) }
449  }
450
451  /// Returns whether the module is a SourceTextModule.
452  #[inline(always)]
453  pub fn is_source_text_module(&self) -> bool {
454    unsafe { v8__Module__IsSourceTextModule(self) }
455  }
456
457  /// Returns whether the module is a SyntheticModule.
458  #[inline(always)]
459  pub fn is_synthetic_module(&self) -> bool {
460    unsafe { v8__Module__IsSyntheticModule(self) }
461  }
462
463  /// Creates a new SyntheticModule with the specified export names, where
464  /// evaluation_steps will be executed upon module evaluation.
465  /// export_names must not contain duplicates.
466  /// module_name is used solely for logging/debugging and doesn't affect module
467  /// behavior.
468  #[inline(always)]
469  pub fn create_synthetic_module<'s, 'i>(
470    scope: &PinScope<'s, 'i>,
471    module_name: Local<String>,
472    export_names: &[Local<String>],
473    evaluation_steps: impl MapFnTo<SyntheticModuleEvaluationSteps<'s>>,
474  ) -> Local<'s, Module> {
475    let export_names = Local::slice_into_raw(export_names);
476    let export_names_len = export_names.len();
477    let export_names = export_names.as_ptr();
478    unsafe {
479      scope
480        .cast_local(|sd| {
481          v8__Module__CreateSyntheticModule(
482            sd.get_isolate_ptr(),
483            &*module_name,
484            export_names_len,
485            export_names,
486            evaluation_steps.map_fn_to(),
487          )
488        })
489        .unwrap()
490    }
491  }
492
493  /// Set this module's exported value for the name export_name to the specified
494  /// export_value. This method must be called only on Modules created via
495  /// create_synthetic_module.  An error will be thrown if export_name is not one
496  /// of the export_names that were passed in that create_synthetic_module call.
497  /// Returns Some(true) on success, None if an error was thrown.
498  #[must_use]
499  #[inline(always)]
500  pub fn set_synthetic_module_export<'s>(
501    &self,
502    scope: &mut PinScope<'s, '_>,
503    export_name: Local<'s, String>,
504    export_value: Local<'s, Value>,
505  ) -> Option<bool> {
506    unsafe {
507      v8__Module__SetSyntheticModuleExport(
508        self,
509        scope.get_isolate_ptr(),
510        &*export_name,
511        &*export_value,
512      )
513    }
514    .into()
515  }
516
517  #[inline(always)]
518  pub fn get_unbound_module_script<'s>(
519    &self,
520    scope: &PinScope<'s, '_>,
521  ) -> Local<'s, UnboundModuleScript> {
522    unsafe {
523      scope
524        .cast_local(|_| v8__Module__GetUnboundModuleScript(self))
525        .unwrap()
526    }
527  }
528
529  /// Search the modules requested directly or indirectly by the module for
530  /// any top-level await that has not yet resolved. If there is any, the
531  /// returned vector contains a tuple of the unresolved module and a message
532  /// with the pending top-level await.
533  /// An embedder may call this before exiting to improve error messages.
534  pub fn get_stalled_top_level_await_message<'s>(
535    &self,
536    scope: &PinScope<'s, '_, ()>,
537  ) -> Vec<(Local<'s, Module>, Local<'s, Message>)> {
538    let mut out_vec: Vec<StalledTopLevelAwaitMessage> = Vec::with_capacity(16);
539    for _i in 0..16 {
540      out_vec.push(StalledTopLevelAwaitMessage {
541        module: std::ptr::null(),
542        message: std::ptr::null(),
543      });
544    }
545
546    let returned_len = unsafe {
547      v8__Module__GetStalledTopLevelAwaitMessage(
548        self,
549        scope.get_isolate_ptr(),
550        out_vec.as_mut_ptr(),
551        out_vec.len(),
552      )
553    };
554
555    let mut ret_vec = Vec::with_capacity(returned_len);
556    for item in out_vec.iter().take(returned_len) {
557      unsafe {
558        ret_vec.push((
559          Local::from_raw(item.module).unwrap(),
560          Local::from_raw(item.message).unwrap(),
561        ));
562      }
563    }
564    ret_vec
565  }
566}
567
568impl ModuleRequest {
569  /// Returns the module specifier for this ModuleRequest.
570  #[inline(always)]
571  pub fn get_specifier<'o>(&self) -> Local<'o, String> {
572    unsafe { Local::from_raw(v8__ModuleRequest__GetSpecifier(self)) }.unwrap()
573  }
574
575  /// Returns the module import phase for this ModuleRequest.
576  #[inline(always)]
577  pub fn get_phase(&self) -> ModuleImportPhase {
578    unsafe { v8__ModuleRequest__GetPhase(self) }
579  }
580  /// Returns the source code offset of this module request.
581  /// Use Module::source_offset_to_location to convert this to line/column numbers.
582  #[inline(always)]
583  pub fn get_source_offset(&self) -> int {
584    unsafe { v8__ModuleRequest__GetSourceOffset(self) }
585  }
586
587  /// Contains the import attributes for this request in the form:
588  /// [key1, value1, source_offset1, key2, value2, source_offset2, ...].
589  /// The keys and values are of type v8::String, and the source offsets are of
590  /// type Int32. Use Module::source_offset_to_location to convert the source
591  /// offsets to Locations with line/column numbers.
592  ///
593  /// All assertions present in the module request will be supplied in this
594  /// list, regardless of whether they are supported by the host. Per
595  /// https://tc39.es/proposal-import-assertions/#sec-hostgetsupportedimportassertions,
596  /// hosts are expected to ignore assertions that they do not support (as
597  /// opposed to, for example, triggering an error if an unsupported assertion is
598  /// present).
599  #[inline(always)]
600  pub fn get_import_attributes<'o>(&self) -> Local<'o, FixedArray> {
601    unsafe { Local::from_raw(v8__ModuleRequest__GetImportAttributes(self)) }
602      .unwrap()
603  }
604}