2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2019-03-26 11:56:34 -04:00
2021-05-19 14:53:43 -04:00
use crate ::bindings ;
2020-09-14 12:48:57 -04:00
use crate ::error ::generic_error ;
2023-04-04 08:46:31 -04:00
use crate ::error ::AnyError ;
2023-02-08 16:40:18 -05:00
use crate ::extensions ::ExtensionFileSource ;
2023-04-04 08:46:31 -04:00
use crate ::fast_string ::FastString ;
2019-06-12 19:55:59 -04:00
use crate ::module_specifier ::ModuleSpecifier ;
2021-12-15 13:22:36 -05:00
use crate ::resolve_import ;
use crate ::resolve_url ;
2023-03-16 09:27:16 -04:00
use crate ::snapshot_util ::SnapshottedData ;
2023-05-03 20:44:59 -04:00
use crate ::Extension ;
2022-10-15 10:01:01 -04:00
use crate ::JsRuntime ;
2023-05-28 14:44:41 -04:00
use anyhow ::anyhow ;
2021-11-16 09:02:28 -05:00
use anyhow ::Error ;
2019-11-16 19:17:47 -05:00
use futures ::future ::FutureExt ;
2019-08-07 12:55:39 -04:00
use futures ::stream ::FuturesUnordered ;
use futures ::stream ::Stream ;
2021-05-19 14:53:43 -04:00
use futures ::stream ::StreamFuture ;
2019-11-16 19:17:47 -05:00
use futures ::stream ::TryStreamExt ;
2021-05-19 14:53:43 -04:00
use log ::debug ;
2023-01-19 04:00:35 -05:00
use serde ::Deserialize ;
use serde ::Serialize ;
2020-09-19 19:17:35 -04:00
use std ::cell ::RefCell ;
2019-03-26 11:56:34 -04:00
use std ::collections ::HashMap ;
2019-04-16 15:13:42 -04:00
use std ::collections ::HashSet ;
2021-06-28 21:03:02 -04:00
use std ::collections ::VecDeque ;
2019-11-16 19:17:47 -05:00
use std ::future ::Future ;
use std ::pin ::Pin ;
2020-02-14 19:18:36 -05:00
use std ::rc ::Rc ;
2019-11-16 19:17:47 -05:00
use std ::task ::Context ;
use std ::task ::Poll ;
2019-03-26 11:56:34 -04:00
2023-01-19 07:47:41 -05:00
pub type ModuleId = usize ;
2021-12-21 09:53:46 -05:00
pub ( crate ) type ModuleLoadId = i32 ;
2023-04-04 08:46:31 -04:00
pub type ModuleCode = FastString ;
pub type ModuleName = FastString ;
2020-05-28 13:36:43 -04:00
2022-05-05 07:16:25 -04:00
pub const BOM_CHAR : & [ u8 ] = & [ 0xef , 0xbb , 0xbf ] ;
2021-12-15 13:22:36 -05:00
/// Strips the byte order mark from the provided text if it exists.
2022-05-05 07:16:25 -04:00
fn strip_bom ( source_code : & [ u8 ] ) -> & [ u8 ] {
if source_code . starts_with ( BOM_CHAR ) {
& source_code [ BOM_CHAR . len ( ) .. ]
2021-12-15 13:22:36 -05:00
} else {
2022-05-05 07:16:25 -04:00
source_code
2021-12-15 13:22:36 -05:00
}
}
const SUPPORTED_TYPE_ASSERTIONS : & [ & str ] = & [ " json " ] ;
/// Throws V8 exception if assertions are invalid
pub ( crate ) fn validate_import_assertions (
scope : & mut v8 ::HandleScope ,
assertions : & HashMap < String , String > ,
) {
for ( key , value ) in assertions {
if key = = " type " & & ! SUPPORTED_TYPE_ASSERTIONS . contains ( & value . as_str ( ) ) {
let message = v8 ::String ::new (
scope ,
2023-01-27 10:43:16 -05:00
& format! ( " \" {value} \" is not a valid module type. " ) ,
2021-12-15 13:22:36 -05:00
)
. unwrap ( ) ;
let exception = v8 ::Exception ::type_error ( scope , message ) ;
scope . throw_exception ( exception ) ;
return ;
}
}
}
#[ derive(Debug) ]
pub ( crate ) enum ImportAssertionsKind {
StaticImport ,
DynamicImport ,
}
pub ( crate ) fn parse_import_assertions (
scope : & mut v8 ::HandleScope ,
import_assertions : v8 ::Local < v8 ::FixedArray > ,
kind : ImportAssertionsKind ,
) -> HashMap < String , String > {
let mut assertions : HashMap < String , String > = HashMap ::default ( ) ;
let assertions_per_line = match kind {
// For static imports, assertions are triples of (keyword, value and source offset)
// Also used in `module_resolve_callback`.
ImportAssertionsKind ::StaticImport = > 3 ,
// For dynamic imports, assertions are tuples of (keyword, value)
ImportAssertionsKind ::DynamicImport = > 2 ,
} ;
assert_eq! ( import_assertions . length ( ) % assertions_per_line , 0 ) ;
let no_of_assertions = import_assertions . length ( ) / assertions_per_line ;
for i in 0 .. no_of_assertions {
let assert_key = import_assertions
. get ( scope , assertions_per_line * i )
. unwrap ( ) ;
let assert_key_val = v8 ::Local ::< v8 ::Value > ::try_from ( assert_key ) . unwrap ( ) ;
let assert_value = import_assertions
. get ( scope , ( assertions_per_line * i ) + 1 )
. unwrap ( ) ;
let assert_value_val =
v8 ::Local ::< v8 ::Value > ::try_from ( assert_value ) . unwrap ( ) ;
assertions . insert (
assert_key_val . to_rust_string_lossy ( scope ) ,
assert_value_val . to_rust_string_lossy ( scope ) ,
) ;
}
assertions
}
2022-05-07 12:46:35 -04:00
pub ( crate ) fn get_asserted_module_type_from_assertions (
2021-12-15 13:22:36 -05:00
assertions : & HashMap < String , String > ,
2022-05-07 12:46:35 -04:00
) -> AssertedModuleType {
2021-12-15 13:22:36 -05:00
assertions
. get ( " type " )
. map ( | ty | {
if ty = = " json " {
2022-05-07 12:46:35 -04:00
AssertedModuleType ::Json
2021-12-15 13:22:36 -05:00
} else {
2022-05-07 12:46:35 -04:00
AssertedModuleType ::JavaScriptOrWasm
2021-12-15 13:22:36 -05:00
}
} )
2022-05-07 12:46:35 -04:00
. unwrap_or ( AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
}
// Clippy thinks the return value doesn't need to be an Option, it's unaware
// of the mapping that MapFnFrom<F> does for ResolveModuleCallback.
#[ allow(clippy::unnecessary_wraps) ]
fn json_module_evaluation_steps < ' a > (
context : v8 ::Local < ' a , v8 ::Context > ,
module : v8 ::Local < v8 ::Module > ,
) -> Option < v8 ::Local < ' a , v8 ::Value > > {
2022-06-25 18:13:24 -04:00
// SAFETY: `CallbackScope` can be safely constructed from `Local<Context>`
2021-12-15 13:22:36 -05:00
let scope = & mut unsafe { v8 ::CallbackScope ::new ( context ) } ;
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
2023-05-28 15:13:53 -04:00
let module_map = JsRuntime ::module_map_from ( tc_scope ) ;
2021-12-15 13:22:36 -05:00
let handle = v8 ::Global ::< v8 ::Module > ::new ( tc_scope , module ) ;
let value_handle = module_map
. borrow_mut ( )
. json_value_store
. remove ( & handle )
. unwrap ( ) ;
let value_local = v8 ::Local ::new ( tc_scope , value_handle ) ;
let name = v8 ::String ::new ( tc_scope , " default " ) . unwrap ( ) ;
// This should never fail
assert! (
module . set_synthetic_module_export ( tc_scope , name , value_local )
= = Some ( true )
) ;
assert! ( ! tc_scope . has_caught ( ) ) ;
// Since TLA is active we need to return a promise.
let resolver = v8 ::PromiseResolver ::new ( tc_scope ) . unwrap ( ) ;
let undefined = v8 ::undefined ( tc_scope ) ;
resolver . resolve ( tc_scope , undefined . into ( ) ) ;
Some ( resolver . get_promise ( tc_scope ) . into ( ) )
}
/// A type of module to be executed.
///
/// For non-`JavaScript` modules, this value doesn't tell
/// how to interpret the module; it is only used to validate
/// the module against an import assertion (if one is present
/// in the import statement).
2023-01-19 04:00:35 -05:00
#[ derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize) ]
2023-02-21 15:12:22 -05:00
#[ repr(u32) ]
2021-12-15 13:22:36 -05:00
pub enum ModuleType {
JavaScript ,
Json ,
}
impl std ::fmt ::Display for ModuleType {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
match self {
Self ::JavaScript = > write! ( f , " JavaScript " ) ,
Self ::Json = > write! ( f , " JSON " ) ,
}
}
}
2020-03-02 13:12:49 -05:00
/// EsModule source code that will be loaded into V8.
///
/// Users can implement `Into<ModuleInfo>` for different file types that
/// can be transpiled to valid EsModule.
///
/// Found module URL might be different from specified URL
/// used for loading due to redirections (like HTTP 303).
2021-09-05 10:22:45 -04:00
/// Eg. Both "`https://example.com/a.ts`" and
/// "`https://example.com/b.ts`" may point to "`https://example.com/c.ts`"
2020-03-02 13:12:49 -05:00
/// By keeping track of specified and found URL we can alias modules and avoid
/// recompiling the same code 3 times.
// TODO(bartlomieju): I have a strong opinion we should store all redirects
// that happened; not only first and final target. It would simplify a lot
// of things throughout the codebase otherwise we may end up requesting
// intermediate redirects from file loader.
2023-03-21 18:33:12 -04:00
// NOTE: This should _not_ be made #[derive(Clone)] unless we take some precautions to avoid excessive string copying.
#[ derive(Debug) ]
2020-03-02 13:12:49 -05:00
pub struct ModuleSource {
2023-03-21 18:33:12 -04:00
pub code : ModuleCode ,
2021-12-15 13:22:36 -05:00
pub module_type : ModuleType ,
2023-04-04 08:46:31 -04:00
module_url_specified : ModuleName ,
/// If the module was found somewhere other than the specified address, this will be [`Some`].
module_url_found : Option < ModuleName > ,
2020-03-02 13:12:49 -05:00
}
2023-04-04 08:46:31 -04:00
impl ModuleSource {
/// Create a [`ModuleSource`] without a redirect.
pub fn new (
module_type : impl Into < ModuleType > ,
code : ModuleCode ,
specifier : & ModuleSpecifier ,
) -> Self {
let module_url_specified = specifier . as_ref ( ) . to_owned ( ) . into ( ) ;
Self {
code ,
module_type : module_type . into ( ) ,
module_url_specified ,
module_url_found : None ,
2023-03-21 18:33:12 -04:00
}
}
2023-04-04 08:46:31 -04:00
/// Create a [`ModuleSource`] with a potential redirect. If the `specifier_found` parameter is the same as the
/// specifier, the code behaves the same was as `ModuleSource::new`.
pub fn new_with_redirect (
module_type : impl Into < ModuleType > ,
code : ModuleCode ,
specifier : & ModuleSpecifier ,
specifier_found : & ModuleSpecifier ,
) -> Self {
let module_url_found = if specifier = = specifier_found {
None
} else {
Some ( specifier_found . as_ref ( ) . to_owned ( ) . into ( ) )
} ;
let module_url_specified = specifier . as_ref ( ) . to_owned ( ) . into ( ) ;
Self {
code ,
module_type : module_type . into ( ) ,
module_url_specified ,
module_url_found ,
2023-03-21 18:33:12 -04:00
}
}
2023-04-04 08:46:31 -04:00
#[ cfg(test) ]
pub fn for_test ( code : & 'static str , file : impl AsRef < str > ) -> Self {
Self {
code : ModuleCode ::from_static ( code ) ,
module_type : ModuleType ::JavaScript ,
module_url_specified : file . as_ref ( ) . to_owned ( ) . into ( ) ,
module_url_found : None ,
2023-03-21 18:33:12 -04:00
}
}
2023-04-04 08:46:31 -04:00
/// If the `found` parameter is the same as the `specified` parameter, the code behaves the same was as `ModuleSource::for_test`.
#[ cfg(test) ]
pub fn for_test_with_redirect (
code : & 'static str ,
specified : impl AsRef < str > ,
found : impl AsRef < str > ,
) -> Self {
let specified = specified . as_ref ( ) . to_string ( ) ;
let found = found . as_ref ( ) . to_string ( ) ;
let found = if found = = specified {
None
} else {
Some ( found . into ( ) )
} ;
Self {
code : ModuleCode ::from_static ( code ) ,
module_type : ModuleType ::JavaScript ,
module_url_specified : specified . into ( ) ,
module_url_found : found ,
2023-03-21 18:33:12 -04:00
}
}
}
2021-12-21 09:53:46 -05:00
pub ( crate ) type PrepareLoadFuture =
2021-11-16 09:02:28 -05:00
dyn Future < Output = ( ModuleLoadId , Result < RecursiveModuleLoad , Error > ) > ;
pub type ModuleSourceFuture = dyn Future < Output = Result < ModuleSource , Error > > ;
2019-03-26 11:56:34 -04:00
2021-12-15 13:22:36 -05:00
type ModuleLoadFuture =
dyn Future < Output = Result < ( ModuleRequest , ModuleSource ) , Error > > ;
2023-01-29 07:09:43 -05:00
#[ derive(Debug, PartialEq, Eq) ]
2023-01-10 08:35:44 -05:00
pub enum ResolutionKind {
/// This kind is used in only one situation: when a module is loaded via
/// `JsRuntime::load_main_module` and is the top-level module, ie. the one
/// passed as an argument to `JsRuntime::load_main_module`.
MainModule ,
/// This kind is returned for all other modules during module load, that are
/// static imports.
Import ,
/// This kind is returned for all modules that are loaded as a result of a
/// call to `import()` API (ie. top-level module as well as all its
/// dependencies, and any other `import()` calls from that load).
DynamicImport ,
}
2020-03-02 13:12:49 -05:00
pub trait ModuleLoader {
2019-03-26 11:56:34 -04:00
/// Returns an absolute URL.
/// When implementing an spec-complaint VM, this should be exactly the
/// algorithm described here:
2021-09-05 10:22:45 -04:00
/// <https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-module-specifier>
2020-01-25 12:53:16 -05:00
///
/// `is_main` can be used to resolve from current working directory or
/// apply import map for child imports.
2023-01-10 08:35:44 -05:00
///
/// `is_dyn_import` can be used to check permissions or deny
/// dynamic imports altogether.
2019-06-09 09:08:20 -04:00
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > ;
2019-03-26 11:56:34 -04:00
2019-06-12 19:55:59 -04:00
/// Given ModuleSpecifier, load its source code.
2020-01-25 12:53:16 -05:00
///
/// `is_dyn_import` can be used to check permissions or deny
/// dynamic imports altogether.
2019-06-12 19:55:59 -04:00
fn load (
& self ,
module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
maybe_referrer : Option < & ModuleSpecifier > ,
2020-01-25 12:53:16 -05:00
is_dyn_import : bool ,
2020-03-02 13:12:49 -05:00
) -> Pin < Box < ModuleSourceFuture > > ;
2020-04-30 08:37:06 -04:00
/// This hook can be used by implementors to do some preparation
/// work before starting loading of modules.
///
/// For example implementor might download multiple modules in
/// parallel and transpile them to final JS sources before
2020-09-06 15:44:29 -04:00
/// yielding control back to the runtime.
2020-04-30 08:37:06 -04:00
///
/// It's not required to implement this method.
fn prepare_load (
& self ,
_module_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < String > ,
_is_dyn_import : bool ,
2021-11-16 09:02:28 -05:00
) -> Pin < Box < dyn Future < Output = Result < ( ) , Error > > > > {
2020-04-30 08:37:06 -04:00
async { Ok ( ( ) ) } . boxed_local ( )
}
2019-03-26 11:56:34 -04:00
}
2020-09-06 10:50:49 -04:00
/// Placeholder structure used when creating
2020-09-06 15:44:29 -04:00
/// a runtime that doesn't support module loading.
2020-12-11 12:49:26 -05:00
pub struct NoopModuleLoader ;
2020-09-06 10:50:49 -04:00
impl ModuleLoader for NoopModuleLoader {
fn resolve (
& self ,
2023-02-13 18:43:53 -05:00
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2023-02-13 18:43:53 -05:00
Err ( generic_error (
format! ( " Module loading is not supported; attempted to resolve: \" {specifier} \" from \" {referrer} \" " )
) )
2020-09-06 10:50:49 -04:00
}
fn load (
& self ,
2023-02-13 18:43:53 -05:00
module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
maybe_referrer : Option < & ModuleSpecifier > ,
2020-09-06 10:50:49 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
2023-02-13 18:43:53 -05:00
let err = generic_error (
format! (
" Module loading is not supported; attempted to load: \" {module_specifier} \" from \" {maybe_referrer:?} \" " ,
)
) ;
async move { Err ( err ) } . boxed_local ( )
2020-09-06 10:50:49 -04:00
}
}
2023-03-08 06:44:54 -05:00
/// Function that can be passed to the `ExtModuleLoader` that allows to
2023-02-08 16:40:18 -05:00
/// transpile sources before passing to V8.
2023-03-08 06:44:54 -05:00
pub type ExtModuleLoaderCb =
2023-03-21 18:33:12 -04:00
Box < dyn Fn ( & ExtensionFileSource ) -> Result < ModuleCode , Error > > ;
2023-02-08 16:40:18 -05:00
2023-05-28 14:44:41 -04:00
pub ( crate ) struct ExtModuleLoader {
maybe_load_callback : Option < Rc < ExtModuleLoaderCb > > ,
sources : RefCell < HashMap < String , ExtensionFileSource > > ,
used_specifiers : RefCell < HashSet < String > > ,
2023-02-08 16:40:18 -05:00
}
2023-02-07 14:22:46 -05:00
2023-03-08 06:44:54 -05:00
impl ExtModuleLoader {
2023-02-08 16:40:18 -05:00
pub fn new (
2023-05-28 14:44:41 -04:00
extensions : & [ Extension ] ,
maybe_load_callback : Option < Rc < ExtModuleLoaderCb > > ,
2023-02-08 16:40:18 -05:00
) -> Self {
2023-05-28 14:44:41 -04:00
let mut sources = HashMap ::new ( ) ;
sources . extend (
extensions
. iter ( )
. flat_map ( | e | e . get_esm_sources ( ) )
. flatten ( )
. map ( | s | ( s . specifier . to_string ( ) , s . clone ( ) ) ) ,
) ;
2023-03-08 06:44:54 -05:00
ExtModuleLoader {
2023-02-08 16:40:18 -05:00
maybe_load_callback ,
2023-05-28 14:44:41 -04:00
sources : RefCell ::new ( sources ) ,
used_specifiers : Default ::default ( ) ,
2023-02-08 16:40:18 -05:00
}
2023-02-07 14:22:46 -05:00
}
2023-05-28 14:44:41 -04:00
}
2023-03-05 17:42:52 -05:00
2023-05-28 14:44:41 -04:00
impl ModuleLoader for ExtModuleLoader {
fn resolve (
2023-02-07 14:22:46 -05:00
& self ,
specifier : & str ,
referrer : & str ,
2023-05-28 14:44:41 -04:00
_kind : ResolutionKind ,
2023-02-07 14:22:46 -05:00
) -> Result < ModuleSpecifier , Error > {
2023-05-28 14:44:41 -04:00
Ok ( resolve_import ( specifier , referrer ) ? )
2023-02-07 14:22:46 -05:00
}
2023-05-28 14:44:41 -04:00
fn load (
2023-02-07 14:22:46 -05:00
& self ,
2023-05-28 14:44:41 -04:00
specifier : & ModuleSpecifier ,
_maybe_referrer : Option < & ModuleSpecifier > ,
_is_dyn_import : bool ,
2023-02-07 14:22:46 -05:00
) -> Pin < Box < ModuleSourceFuture > > {
2023-05-28 14:44:41 -04:00
let sources = self . sources . borrow ( ) ;
let source = match sources . get ( specifier . as_str ( ) ) {
Some ( source ) = > source ,
None = > return futures ::future ::err ( anyhow! ( " Specifier \" {} \" was not passed as an extension module and was not included in the snapshot. " , specifier ) ) . boxed_local ( ) ,
} ;
self
. used_specifiers
. borrow_mut ( )
. insert ( specifier . to_string ( ) ) ;
let result = if let Some ( load_callback ) = & self . maybe_load_callback {
load_callback ( source )
} else {
2023-06-13 11:45:06 -04:00
Ok ( source . load ( ) )
2023-05-28 14:44:41 -04:00
} ;
match result {
Ok ( code ) = > {
let res = ModuleSource ::new ( ModuleType ::JavaScript , code , specifier ) ;
return futures ::future ::ok ( res ) . boxed_local ( ) ;
2023-02-08 16:40:18 -05:00
}
2023-05-28 14:44:41 -04:00
Err ( err ) = > return futures ::future ::err ( err ) . boxed_local ( ) ,
2023-02-08 16:40:18 -05:00
}
2023-02-07 14:22:46 -05:00
}
2023-05-28 14:44:41 -04:00
fn prepare_load (
2023-02-07 14:22:46 -05:00
& self ,
2023-05-28 14:44:41 -04:00
_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < String > ,
_is_dyn_import : bool ,
2023-02-07 14:22:46 -05:00
) -> Pin < Box < dyn Future < Output = Result < ( ) , Error > > > > {
2023-05-28 14:44:41 -04:00
async { Ok ( ( ) ) } . boxed_local ( )
2023-05-03 20:44:59 -04:00
}
}
2020-11-27 16:45:38 -05:00
/// Basic file system module loader.
///
/// Note that this loader will **block** event loop
/// when loading file as it uses synchronous FS API
/// from standard library.
pub struct FsModuleLoader ;
impl ModuleLoader for FsModuleLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-12-15 13:22:36 -05:00
Ok ( resolve_import ( specifier , referrer ) ? )
2020-11-27 16:45:38 -05:00
}
fn load (
& self ,
module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2020-11-27 16:45:38 -05:00
_is_dynamic : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
2023-04-04 08:46:31 -04:00
fn load (
module_specifier : & ModuleSpecifier ,
) -> Result < ModuleSource , AnyError > {
2021-02-17 13:47:18 -05:00
let path = module_specifier . to_file_path ( ) . map_err ( | _ | {
2020-11-27 16:45:38 -05:00
generic_error ( format! (
2023-01-27 10:43:16 -05:00
" Provided module specifier \" {module_specifier} \" is not a file URL. "
2020-11-27 16:45:38 -05:00
) )
} ) ? ;
2021-12-15 13:22:36 -05:00
let module_type = if let Some ( extension ) = path . extension ( ) {
let ext = extension . to_string_lossy ( ) . to_lowercase ( ) ;
if ext = = " json " {
ModuleType ::Json
} else {
ModuleType ::JavaScript
}
} else {
ModuleType ::JavaScript
} ;
2023-04-04 08:46:31 -04:00
let code = std ::fs ::read_to_string ( path ) ? . into ( ) ;
let module = ModuleSource ::new ( module_type , code , module_specifier ) ;
2020-11-27 16:45:38 -05:00
Ok ( module )
}
2023-04-04 08:46:31 -04:00
futures ::future ::ready ( load ( module_specifier ) ) . boxed_local ( )
2020-11-27 16:45:38 -05:00
}
}
2021-06-19 10:14:43 -04:00
/// Describes the entrypoint of a recursive module load.
#[ derive(Debug) ]
enum LoadInit {
/// Main module specifier.
Main ( String ) ,
2021-09-17 21:44:53 -04:00
/// Module specifier for side module.
Side ( String ) ,
2021-12-15 13:22:36 -05:00
/// Dynamic import specifier with referrer and expected
/// module type (which is determined by import assertion).
2022-05-07 12:46:35 -04:00
DynamicImport ( String , String , AssertedModuleType ) ,
2019-08-07 12:55:39 -04:00
}
#[ derive(Debug, Eq, PartialEq) ]
2020-01-08 09:06:04 -05:00
pub enum LoadState {
2021-06-19 10:14:43 -04:00
Init ,
2019-08-07 12:55:39 -04:00
LoadingRoot ,
2020-01-08 09:06:04 -05:00
LoadingImports ,
Done ,
2019-03-26 11:56:34 -04:00
}
2020-09-06 15:44:29 -04:00
/// This future is used to implement parallel async module loading.
2021-12-21 09:53:46 -05:00
pub ( crate ) struct RecursiveModuleLoad {
2020-04-26 13:00:10 -04:00
pub id : ModuleLoadId ,
2020-01-08 09:06:04 -05:00
pub root_module_id : Option < ModuleId > ,
2021-12-21 09:53:46 -05:00
init : LoadInit ,
2022-05-07 12:46:35 -04:00
root_asserted_module_type : Option < AssertedModuleType > ,
2021-12-21 09:53:46 -05:00
root_module_type : Option < ModuleType > ,
state : LoadState ,
module_map_rc : Rc < RefCell < ModuleMap > > ,
pending : FuturesUnordered < Pin < Box < ModuleLoadFuture > > > ,
visited : HashSet < ModuleRequest > ,
2023-06-01 01:35:14 -04:00
// The loader is copied from `module_map_rc`, but its reference is cloned
2023-02-08 12:13:33 -05:00
// ahead of time to avoid already-borrowed errors.
2023-05-28 14:44:41 -04:00
loader : Rc < dyn ModuleLoader > ,
2019-03-26 11:56:34 -04:00
}
2020-01-08 09:06:04 -05:00
impl RecursiveModuleLoad {
2021-12-21 09:53:46 -05:00
/// Starts a new asynchronous load of the module graph for given specifier.
///
/// The module corresponding for the given `specifier` will be marked as
// "the main module" (`import.meta.main` will return `true` for this module).
fn main ( specifier : & str , module_map_rc : Rc < RefCell < ModuleMap > > ) -> Self {
2021-06-28 21:03:02 -04:00
Self ::new ( LoadInit ::Main ( specifier . to_string ( ) ) , module_map_rc )
2019-08-07 12:55:39 -04:00
}
2021-12-21 09:53:46 -05:00
/// Starts a new asynchronous load of the module graph for given specifier.
fn side ( specifier : & str , module_map_rc : Rc < RefCell < ModuleMap > > ) -> Self {
2021-09-17 21:44:53 -04:00
Self ::new ( LoadInit ::Side ( specifier . to_string ( ) ) , module_map_rc )
}
2021-12-21 09:53:46 -05:00
/// Starts a new asynchronous load of the module graph for given specifier
/// that was imported using `import()`.
fn dynamic_import (
2019-08-07 12:55:39 -04:00
specifier : & str ,
referrer : & str ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ,
2021-06-28 21:03:02 -04:00
module_map_rc : Rc < RefCell < ModuleMap > > ,
2019-08-07 12:55:39 -04:00
) -> Self {
2021-12-21 09:53:46 -05:00
Self ::new (
LoadInit ::DynamicImport (
specifier . to_string ( ) ,
referrer . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type ,
2021-12-21 09:53:46 -05:00
) ,
module_map_rc ,
)
2019-08-07 12:55:39 -04:00
}
2021-06-28 21:03:02 -04:00
fn new ( init : LoadInit , module_map_rc : Rc < RefCell < ModuleMap > > ) -> Self {
2021-12-15 13:22:36 -05:00
let id = {
let mut module_map = module_map_rc . borrow_mut ( ) ;
let id = module_map . next_load_id ;
module_map . next_load_id + = 1 ;
id
} ;
2021-06-28 21:03:02 -04:00
let loader = module_map_rc . borrow ( ) . loader . clone ( ) ;
2022-05-07 12:46:35 -04:00
let asserted_module_type = match init {
2021-12-15 13:22:36 -05:00
LoadInit ::DynamicImport ( _ , _ , module_type ) = > module_type ,
2022-05-07 12:46:35 -04:00
_ = > AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ;
2021-06-28 21:03:02 -04:00
let mut load = Self {
2021-12-15 13:22:36 -05:00
id ,
2020-01-08 09:06:04 -05:00
root_module_id : None ,
2022-05-07 12:46:35 -04:00
root_asserted_module_type : None ,
2021-12-15 13:22:36 -05:00
root_module_type : None ,
2021-06-19 10:14:43 -04:00
init ,
state : LoadState ::Init ,
2021-06-28 21:03:02 -04:00
module_map_rc : module_map_rc . clone ( ) ,
2019-06-05 16:35:38 -04:00
loader ,
2019-08-07 12:55:39 -04:00
pending : FuturesUnordered ::new ( ) ,
2021-06-28 21:03:02 -04:00
visited : HashSet ::new ( ) ,
} ;
2021-12-21 09:53:46 -05:00
// FIXME(bartlomieju): this seems fishy
2021-06-28 21:03:02 -04:00
// Ignore the error here, let it be hit in `Stream::poll_next()`.
if let Ok ( root_specifier ) = load . resolve_root ( ) {
2021-12-15 13:22:36 -05:00
if let Some ( module_id ) = module_map_rc
. borrow ( )
2023-04-04 08:46:31 -04:00
. get_id ( root_specifier , asserted_module_type )
2021-06-28 21:03:02 -04:00
{
load . root_module_id = Some ( module_id ) ;
2022-05-07 12:46:35 -04:00
load . root_asserted_module_type = Some ( asserted_module_type ) ;
load . root_module_type = Some (
module_map_rc
. borrow ( )
2023-01-19 07:47:41 -05:00
. get_info_by_id ( module_id )
2022-05-07 12:46:35 -04:00
. unwrap ( )
. module_type ,
) ;
2021-06-28 21:03:02 -04:00
}
}
load
}
2021-12-21 09:53:46 -05:00
fn resolve_root ( & self ) -> Result < ModuleSpecifier , Error > {
2021-06-28 21:03:02 -04:00
match self . init {
2023-05-03 20:44:59 -04:00
LoadInit ::Main ( ref specifier ) = > {
self
. loader
. resolve ( specifier , " . " , ResolutionKind ::MainModule )
}
LoadInit ::Side ( ref specifier ) = > {
self . loader . resolve ( specifier , " . " , ResolutionKind ::Import )
2021-09-17 21:44:53 -04:00
}
2023-05-03 20:44:59 -04:00
LoadInit ::DynamicImport ( ref specifier , ref referrer , _ ) = > self
. loader
. resolve ( specifier , referrer , ResolutionKind ::DynamicImport ) ,
2019-04-16 15:13:42 -04:00
}
}
2021-12-21 09:53:46 -05:00
async fn prepare ( & self ) -> Result < ( ) , Error > {
2021-06-19 10:14:43 -04:00
let ( module_specifier , maybe_referrer ) = match self . init {
2021-06-28 21:03:02 -04:00
LoadInit ::Main ( ref specifier ) = > {
2023-05-03 20:44:59 -04:00
let spec =
self
. loader
. resolve ( specifier , " . " , ResolutionKind ::MainModule ) ? ;
2020-04-30 08:37:06 -04:00
( spec , None )
}
2021-09-17 21:44:53 -04:00
LoadInit ::Side ( ref specifier ) = > {
2023-05-03 20:44:59 -04:00
let spec =
self
. loader
. resolve ( specifier , " . " , ResolutionKind ::Import ) ? ;
2021-09-17 21:44:53 -04:00
( spec , None )
}
2021-12-15 13:22:36 -05:00
LoadInit ::DynamicImport ( ref specifier , ref referrer , _ ) = > {
2023-05-03 20:44:59 -04:00
let spec = self . loader . resolve (
2023-01-10 08:35:44 -05:00
specifier ,
referrer ,
ResolutionKind ::DynamicImport ,
) ? ;
2020-04-30 08:37:06 -04:00
( spec , Some ( referrer . to_string ( ) ) )
}
} ;
2021-06-19 10:14:43 -04:00
self
2020-04-30 08:37:06 -04:00
. loader
2023-06-01 01:35:14 -04:00
. prepare_load ( & module_specifier , maybe_referrer , self . is_dynamic_import ( ) )
2021-06-19 10:14:43 -04:00
. await
2020-01-08 09:06:04 -05:00
}
2019-03-26 11:56:34 -04:00
2021-12-21 09:53:46 -05:00
fn is_currently_loading_main_module ( & self ) -> bool {
2021-09-17 21:44:53 -04:00
! self . is_dynamic_import ( )
& & matches! ( self . init , LoadInit ::Main ( .. ) )
& & self . state = = LoadState ::LoadingRoot
2021-05-19 14:53:43 -04:00
}
2021-12-21 09:53:46 -05:00
fn is_dynamic_import ( & self ) -> bool {
matches! ( self . init , LoadInit ::DynamicImport ( .. ) )
}
pub ( crate ) fn register_and_recurse (
2021-06-28 21:03:02 -04:00
& mut self ,
scope : & mut v8 ::HandleScope ,
2021-12-15 13:22:36 -05:00
module_request : & ModuleRequest ,
2023-04-04 08:46:31 -04:00
module_source : ModuleSource ,
2022-04-26 09:28:42 -04:00
) -> Result < ( ) , ModuleError > {
2022-05-07 12:46:35 -04:00
let expected_asserted_module_type = module_source . module_type . into ( ) ;
2023-04-04 08:46:31 -04:00
let module_url_found = module_source . module_url_found ;
let module_url_specified = module_source . module_url_specified ;
2022-05-07 12:46:35 -04:00
if module_request . asserted_module_type ! = expected_asserted_module_type {
2022-04-26 09:28:42 -04:00
return Err ( ModuleError ::Other ( generic_error ( format! (
2021-12-15 13:22:36 -05:00
" Expected a \" {} \" module but loaded a \" {} \" module. " ,
2022-05-07 12:46:35 -04:00
module_request . asserted_module_type , module_source . module_type ,
2022-04-26 09:28:42 -04:00
) ) ) ) ;
2021-12-15 13:22:36 -05:00
}
2021-06-28 21:03:02 -04:00
// Register the module in the module map unless it's already there. If the
// specified URL and the "true" URL are different, register the alias.
2023-04-04 08:46:31 -04:00
let module_url_found = if let Some ( module_url_found ) = module_url_found {
let ( module_url_found1 , module_url_found2 ) =
module_url_found . into_cheap_copy ( ) ;
2021-06-28 21:03:02 -04:00
self . module_map_rc . borrow_mut ( ) . alias (
2023-04-04 08:46:31 -04:00
module_url_specified ,
2022-05-07 12:46:35 -04:00
expected_asserted_module_type ,
2023-04-04 08:46:31 -04:00
module_url_found1 ,
2021-06-28 21:03:02 -04:00
) ;
2023-04-04 08:46:31 -04:00
module_url_found2
} else {
module_url_specified
} ;
let maybe_module_id = self
. module_map_rc
. borrow ( )
. get_id ( & module_url_found , expected_asserted_module_type ) ;
2021-06-28 21:03:02 -04:00
let module_id = match maybe_module_id {
Some ( id ) = > {
debug! (
2023-04-04 08:46:31 -04:00
" Already-registered module fetched again: {:?} " ,
module_url_found
2021-06-28 21:03:02 -04:00
) ;
id
}
2021-12-15 13:22:36 -05:00
None = > match module_source . module_type {
ModuleType ::JavaScript = > {
self . module_map_rc . borrow_mut ( ) . new_es_module (
scope ,
self . is_currently_loading_main_module ( ) ,
2023-04-04 08:46:31 -04:00
module_url_found ,
module_source . code ,
2023-01-10 08:35:44 -05:00
self . is_dynamic_import ( ) ,
2021-12-15 13:22:36 -05:00
) ?
}
ModuleType ::Json = > self . module_map_rc . borrow_mut ( ) . new_json_module (
scope ,
2023-04-04 08:46:31 -04:00
module_url_found ,
module_source . code ,
2021-12-15 13:22:36 -05:00
) ? ,
} ,
2021-06-28 21:03:02 -04:00
} ;
// Recurse the module's imports. There are two cases for each import:
// 1. If the module is not in the module map, start a new load for it in
// `self.pending`. The result of that load should eventually be passed to
// this function for recursion.
// 2. If the module is already in the module map, queue it up to be
// recursed synchronously here.
// This robustly ensures that the whole graph is in the module map before
// `LoadState::Done` is set.
let mut already_registered = VecDeque ::new ( ) ;
2021-12-15 13:22:36 -05:00
already_registered . push_back ( ( module_id , module_request . clone ( ) ) ) ;
self . visited . insert ( module_request . clone ( ) ) ;
while let Some ( ( module_id , module_request ) ) = already_registered . pop_front ( )
{
2023-02-21 15:12:22 -05:00
let referrer = ModuleSpecifier ::parse ( & module_request . specifier ) . unwrap ( ) ;
2021-06-28 21:03:02 -04:00
let imports = self
. module_map_rc
. borrow ( )
2021-12-15 13:22:36 -05:00
. get_requested_modules ( module_id )
2021-06-28 21:03:02 -04:00
. unwrap ( )
. clone ( ) ;
2021-12-15 13:22:36 -05:00
for module_request in imports {
if ! self . visited . contains ( & module_request ) {
if let Some ( module_id ) = self . module_map_rc . borrow ( ) . get_id (
module_request . specifier . as_str ( ) ,
2022-05-07 12:46:35 -04:00
module_request . asserted_module_type ,
2021-12-15 13:22:36 -05:00
) {
already_registered . push_back ( ( module_id , module_request . clone ( ) ) ) ;
2021-06-28 21:03:02 -04:00
} else {
2021-12-15 13:22:36 -05:00
let request = module_request . clone ( ) ;
2023-02-21 15:12:22 -05:00
let specifier =
ModuleSpecifier ::parse ( & module_request . specifier ) . unwrap ( ) ;
let referrer = referrer . clone ( ) ;
2021-12-15 13:22:36 -05:00
let loader = self . loader . clone ( ) ;
let is_dynamic_import = self . is_dynamic_import ( ) ;
let fut = async move {
let load_result = loader
2023-04-04 08:46:31 -04:00
. load ( & specifier , Some ( & referrer ) , is_dynamic_import )
2021-12-15 13:22:36 -05:00
. await ;
load_result . map ( | s | ( request , s ) )
} ;
2021-06-28 21:03:02 -04:00
self . pending . push ( fut . boxed_local ( ) ) ;
}
2021-12-15 13:22:36 -05:00
self . visited . insert ( module_request ) ;
2021-06-28 21:03:02 -04:00
}
}
}
// Update `self.state` however applicable.
2021-05-19 14:53:43 -04:00
if self . state = = LoadState ::LoadingRoot {
self . root_module_id = Some ( module_id ) ;
2022-05-07 12:46:35 -04:00
self . root_asserted_module_type = Some ( module_source . module_type . into ( ) ) ;
2021-05-19 14:53:43 -04:00
self . state = LoadState ::LoadingImports ;
}
if self . pending . is_empty ( ) {
self . state = LoadState ::Done ;
}
2021-06-28 21:03:02 -04:00
Ok ( ( ) )
2019-08-07 12:55:39 -04:00
}
}
2019-04-16 15:13:42 -04:00
2020-01-08 09:06:04 -05:00
impl Stream for RecursiveModuleLoad {
2021-12-15 13:22:36 -05:00
type Item = Result < ( ModuleRequest , ModuleSource ) , Error > ;
2019-03-26 11:56:34 -04:00
2019-11-16 19:17:47 -05:00
fn poll_next (
self : Pin < & mut Self > ,
cx : & mut Context ,
) -> Poll < Option < Self ::Item > > {
let inner = self . get_mut ( ) ;
2021-06-28 21:03:02 -04:00
// IMPORTANT: Do not borrow `inner.module_map_rc` here. It may not be
// available.
2019-11-16 19:17:47 -05:00
match inner . state {
2021-06-19 10:14:43 -04:00
LoadState ::Init = > {
2021-06-28 21:03:02 -04:00
let module_specifier = match inner . resolve_root ( ) {
2021-06-19 10:14:43 -04:00
Ok ( url ) = > url ,
Err ( error ) = > return Poll ::Ready ( Some ( Err ( error ) ) ) ,
} ;
2021-06-28 21:03:02 -04:00
let load_fut = if let Some ( _module_id ) = inner . root_module_id {
2021-12-15 13:22:36 -05:00
// FIXME(bartlomieju): this is very bad
2021-06-28 21:03:02 -04:00
// The root module is already in the module map.
// TODO(nayeemrmn): In this case we would ideally skip to
// `LoadState::LoadingImports` and synchronously recurse the imports
// like the bottom of `RecursiveModuleLoad::register_and_recurse()`.
// But the module map cannot be borrowed here. Instead fake a load
// event so it gets passed to that function and recursed eventually.
2022-05-07 12:46:35 -04:00
let asserted_module_type = inner . root_asserted_module_type . unwrap ( ) ;
2021-12-15 13:22:36 -05:00
let module_type = inner . root_module_type . unwrap ( ) ;
let module_request = ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : module_specifier . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type ,
2021-12-15 13:22:36 -05:00
} ;
2023-04-04 08:46:31 -04:00
// The code will be discarded, since this module is already in the
// module map.
let module_source = ModuleSource ::new (
2021-12-15 13:22:36 -05:00
module_type ,
2023-04-04 08:46:31 -04:00
Default ::default ( ) ,
& module_specifier ,
) ;
2021-12-15 13:22:36 -05:00
futures ::future ::ok ( ( module_request , module_source ) ) . boxed ( )
2021-06-28 21:03:02 -04:00
} else {
let maybe_referrer = match inner . init {
2021-12-15 13:22:36 -05:00
LoadInit ::DynamicImport ( _ , ref referrer , _ ) = > {
resolve_url ( referrer ) . ok ( )
2021-06-28 21:03:02 -04:00
}
_ = > None ,
} ;
2022-05-07 12:46:35 -04:00
let asserted_module_type = match inner . init {
2021-12-15 13:22:36 -05:00
LoadInit ::DynamicImport ( _ , _ , module_type ) = > module_type ,
2022-05-07 12:46:35 -04:00
_ = > AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ;
let module_request = ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : module_specifier . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type ,
2021-12-15 13:22:36 -05:00
} ;
let loader = inner . loader . clone ( ) ;
let is_dynamic_import = inner . is_dynamic_import ( ) ;
async move {
let result = loader
2023-04-04 08:46:31 -04:00
. load (
& module_specifier ,
maybe_referrer . as_ref ( ) ,
is_dynamic_import ,
)
2021-12-15 13:22:36 -05:00
. await ;
result . map ( | s | ( module_request , s ) )
}
. boxed_local ( )
2021-06-19 10:14:43 -04:00
} ;
inner . pending . push ( load_fut ) ;
inner . state = LoadState ::LoadingRoot ;
2019-11-16 19:17:47 -05:00
inner . try_poll_next_unpin ( cx )
2019-08-07 12:55:39 -04:00
}
2020-01-08 09:06:04 -05:00
LoadState ::LoadingRoot | LoadState ::LoadingImports = > {
2019-11-16 19:17:47 -05:00
match inner . pending . try_poll_next_unpin ( cx ) ? {
Poll ::Ready ( None ) = > unreachable! ( ) ,
2020-01-08 09:06:04 -05:00
Poll ::Ready ( Some ( info ) ) = > Poll ::Ready ( Some ( Ok ( info ) ) ) ,
2019-11-16 19:17:47 -05:00
Poll ::Pending = > Poll ::Pending ,
2019-08-07 12:55:39 -04:00
}
}
2020-01-08 09:06:04 -05:00
LoadState ::Done = > Poll ::Ready ( None ) ,
2019-11-16 19:17:47 -05:00
}
2019-03-26 11:56:34 -04:00
}
}
2023-01-19 04:00:35 -05:00
#[ derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize) ]
2023-02-21 15:12:22 -05:00
#[ repr(u32) ]
2022-05-07 12:46:35 -04:00
pub ( crate ) enum AssertedModuleType {
JavaScriptOrWasm ,
Json ,
}
impl From < ModuleType > for AssertedModuleType {
fn from ( module_type : ModuleType ) -> AssertedModuleType {
match module_type {
ModuleType ::JavaScript = > AssertedModuleType ::JavaScriptOrWasm ,
ModuleType ::Json = > AssertedModuleType ::Json ,
}
}
}
impl std ::fmt ::Display for AssertedModuleType {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
match self {
Self ::JavaScriptOrWasm = > write! ( f , " JavaScriptOrWasm " ) ,
Self ::Json = > write! ( f , " JSON " ) ,
}
}
}
/// Describes a request for a module as parsed from the source code.
/// Usually executable (`JavaScriptOrWasm`) is used, except when an
/// import assertions explicitly constrains an import to JSON, in
/// which case this will have a `AssertedModuleType::Json`.
2023-01-19 04:00:35 -05:00
#[ derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize) ]
2021-12-21 09:53:46 -05:00
pub ( crate ) struct ModuleRequest {
2023-02-21 15:12:22 -05:00
pub specifier : String ,
2022-05-07 12:46:35 -04:00
pub asserted_module_type : AssertedModuleType ,
2021-12-15 13:22:36 -05:00
}
2023-04-04 08:46:31 -04:00
#[ derive(Debug, PartialEq) ]
2021-12-21 09:53:46 -05:00
pub ( crate ) struct ModuleInfo {
#[ allow(unused) ]
2020-11-21 10:23:35 -05:00
pub id : ModuleId ,
2021-05-19 14:53:43 -04:00
// Used in "bindings.rs" for "import.meta.main" property value.
2020-01-08 09:06:04 -05:00
pub main : bool ,
2023-04-04 08:46:31 -04:00
pub name : ModuleName ,
2021-12-15 13:22:36 -05:00
pub requests : Vec < ModuleRequest > ,
pub module_type : ModuleType ,
2019-03-26 11:56:34 -04:00
}
2019-04-18 21:33:50 -04:00
/// A symbolic module entity.
2023-04-04 08:46:31 -04:00
#[ derive(Debug, PartialEq) ]
2023-01-19 04:00:35 -05:00
pub ( crate ) enum SymbolicModule {
2019-04-18 21:33:50 -04:00
/// This module is an alias to another module.
/// This is useful such that multiple names could point to
/// the same underlying module (particularly due to redirects).
2023-04-04 08:46:31 -04:00
Alias ( ModuleName ) ,
2019-04-18 21:33:50 -04:00
/// This module associates with a V8 module by id.
2020-01-06 14:07:35 -05:00
Mod ( ModuleId ) ,
2019-04-18 21:33:50 -04:00
}
2022-04-26 09:28:42 -04:00
#[ derive(Debug) ]
pub ( crate ) enum ModuleError {
Exception ( v8 ::Global < v8 ::Value > ) ,
Other ( Error ) ,
}
2019-03-26 11:56:34 -04:00
/// A collection of JS modules.
2021-12-21 09:53:46 -05:00
pub ( crate ) struct ModuleMap {
2021-05-19 14:53:43 -04:00
// Handling of specifiers and v8 objects
2023-01-19 07:47:41 -05:00
pub handles : Vec < v8 ::Global < v8 ::Module > > ,
pub info : Vec < ModuleInfo > ,
2023-04-04 08:46:31 -04:00
pub ( crate ) by_name_js : HashMap < ModuleName , SymbolicModule > ,
pub ( crate ) by_name_json : HashMap < ModuleName , SymbolicModule > ,
2023-01-19 04:00:35 -05:00
pub ( crate ) next_load_id : ModuleLoadId ,
2021-05-19 14:53:43 -04:00
// Handling of futures for loading module sources
2023-05-28 14:44:41 -04:00
pub loader : Rc < dyn ModuleLoader > ,
2021-05-19 14:53:43 -04:00
pub ( crate ) dynamic_import_map :
HashMap < ModuleLoadId , v8 ::Global < v8 ::PromiseResolver > > ,
pub ( crate ) preparing_dynamic_imports :
FuturesUnordered < Pin < Box < PrepareLoadFuture > > > ,
pub ( crate ) pending_dynamic_imports :
FuturesUnordered < StreamFuture < RecursiveModuleLoad > > ,
2021-12-15 13:22:36 -05:00
// This store is used temporarly, to forward parsed JSON
// value from `new_json_module` to `json_module_evaluation_steps`
json_value_store : HashMap < v8 ::Global < v8 ::Module > , v8 ::Global < v8 ::Value > > ,
2019-03-26 11:56:34 -04:00
}
2021-02-23 09:22:55 -05:00
impl ModuleMap {
2023-04-04 08:46:31 -04:00
pub fn collect_modules (
& self ,
) -> Vec < ( AssertedModuleType , & ModuleName , & SymbolicModule ) > {
let mut output = vec! [ ] ;
for module_type in [
AssertedModuleType ::JavaScriptOrWasm ,
AssertedModuleType ::Json ,
] {
output . extend (
self
. by_name ( module_type )
. iter ( )
. map ( | x | ( module_type , x . 0 , x . 1 ) ) ,
)
}
output
}
2023-05-10 11:48:17 -04:00
#[ cfg(debug_assertions) ]
2023-05-09 06:37:13 -04:00
pub ( crate ) fn assert_all_modules_evaluated (
& self ,
scope : & mut v8 ::HandleScope ,
) {
let mut not_evaluated = vec! [ ] ;
for ( i , handle ) in self . handles . iter ( ) . enumerate ( ) {
let module = v8 ::Local ::new ( scope , handle ) ;
if ! matches! ( module . get_status ( ) , v8 ::ModuleStatus ::Evaluated ) {
not_evaluated . push ( self . info [ i ] . name . as_str ( ) . to_string ( ) ) ;
}
}
if ! not_evaluated . is_empty ( ) {
let mut msg = " Following modules were not evaluated; make sure they are imported from other code: \n " . to_string ( ) ;
for m in not_evaluated {
msg . push_str ( & format! ( " - {} \n " , m ) ) ;
}
panic! ( " {} " , msg ) ;
}
}
2023-01-19 04:00:35 -05:00
pub fn serialize_for_snapshotting (
& self ,
scope : & mut v8 ::HandleScope ,
2023-03-16 09:27:16 -04:00
) -> SnapshottedData {
2023-02-21 15:12:22 -05:00
let array = v8 ::Array ::new ( scope , 3 ) ;
2023-01-19 04:00:35 -05:00
let next_load_id = v8 ::Integer ::new ( scope , self . next_load_id ) ;
2023-02-21 15:12:22 -05:00
array . set_index ( scope , 0 , next_load_id . into ( ) ) ;
2023-01-19 04:00:35 -05:00
2023-02-21 15:12:22 -05:00
let info_arr = v8 ::Array ::new ( scope , self . info . len ( ) as i32 ) ;
for ( i , info ) in self . info . iter ( ) . enumerate ( ) {
let module_info_arr = v8 ::Array ::new ( scope , 5 ) ;
2023-01-19 04:00:35 -05:00
2023-02-21 15:12:22 -05:00
let id = v8 ::Integer ::new ( scope , info . id as i32 ) ;
module_info_arr . set_index ( scope , 0 , id . into ( ) ) ;
let main = v8 ::Boolean ::new ( scope , info . main ) ;
module_info_arr . set_index ( scope , 1 , main . into ( ) ) ;
2023-04-04 08:46:31 -04:00
let name = info . name . v8 ( scope ) ;
2023-02-21 15:12:22 -05:00
module_info_arr . set_index ( scope , 2 , name . into ( ) ) ;
let array_len = 2 * info . requests . len ( ) as i32 ;
let requests_arr = v8 ::Array ::new ( scope , array_len ) ;
for ( i , request ) in info . requests . iter ( ) . enumerate ( ) {
2023-03-18 16:09:13 -04:00
let specifier = v8 ::String ::new_from_one_byte (
scope ,
request . specifier . as_bytes ( ) ,
v8 ::NewStringType ::Normal ,
)
. unwrap ( ) ;
2023-02-21 15:12:22 -05:00
requests_arr . set_index ( scope , 2 * i as u32 , specifier . into ( ) ) ;
let asserted_module_type =
v8 ::Integer ::new ( scope , request . asserted_module_type as i32 ) ;
requests_arr . set_index (
scope ,
( 2 * i ) as u32 + 1 ,
asserted_module_type . into ( ) ,
) ;
}
module_info_arr . set_index ( scope , 3 , requests_arr . into ( ) ) ;
let module_type = v8 ::Integer ::new ( scope , info . module_type as i32 ) ;
module_info_arr . set_index ( scope , 4 , module_type . into ( ) ) ;
info_arr . set_index ( scope , i as u32 , module_info_arr . into ( ) ) ;
}
array . set_index ( scope , 1 , info_arr . into ( ) ) ;
2023-04-04 08:46:31 -04:00
let by_name = self . collect_modules ( ) ;
let by_name_array = v8 ::Array ::new ( scope , by_name . len ( ) as i32 ) ;
2023-02-21 15:12:22 -05:00
{
2023-04-04 08:46:31 -04:00
for ( i , ( module_type , name , module ) ) in by_name . into_iter ( ) . enumerate ( ) {
2023-02-21 15:12:22 -05:00
let arr = v8 ::Array ::new ( scope , 3 ) ;
2023-04-04 08:46:31 -04:00
let specifier = name . v8 ( scope ) ;
2023-02-21 15:12:22 -05:00
arr . set_index ( scope , 0 , specifier . into ( ) ) ;
2023-01-19 04:00:35 -05:00
2023-04-04 08:46:31 -04:00
let asserted_module_type = v8 ::Integer ::new ( scope , module_type as i32 ) ;
2023-02-21 15:12:22 -05:00
arr . set_index ( scope , 1 , asserted_module_type . into ( ) ) ;
2023-04-04 08:46:31 -04:00
let symbolic_module : v8 ::Local < v8 ::Value > = match module {
2023-02-21 15:12:22 -05:00
SymbolicModule ::Alias ( alias ) = > {
2023-03-18 16:09:13 -04:00
let alias = v8 ::String ::new_from_one_byte (
scope ,
alias . as_bytes ( ) ,
v8 ::NewStringType ::Normal ,
)
. unwrap ( ) ;
2023-02-21 15:12:22 -05:00
alias . into ( )
}
SymbolicModule ::Mod ( id ) = > {
let id = v8 ::Integer ::new ( scope , * id as i32 ) ;
id . into ( )
}
} ;
arr . set_index ( scope , 2 , symbolic_module ) ;
by_name_array . set_index ( scope , i as u32 , arr . into ( ) ) ;
}
}
array . set_index ( scope , 2 , by_name_array . into ( ) ) ;
let array_global = v8 ::Global ::new ( scope , array ) ;
2023-01-19 04:00:35 -05:00
2023-01-19 07:47:41 -05:00
let handles = self . handles . clone ( ) ;
2023-03-16 09:27:16 -04:00
SnapshottedData {
module_map_data : array_global ,
module_handles : handles ,
}
2023-01-19 04:00:35 -05:00
}
2023-03-16 09:27:16 -04:00
pub fn update_with_snapshotted_data (
2023-01-19 04:00:35 -05:00
& mut self ,
scope : & mut v8 ::HandleScope ,
2023-03-16 09:27:16 -04:00
snapshotted_data : SnapshottedData ,
2023-01-19 04:00:35 -05:00
) {
2023-03-16 09:27:16 -04:00
let local_data : v8 ::Local < v8 ::Array > =
v8 ::Local ::new ( scope , snapshotted_data . module_map_data ) ;
2023-01-19 04:00:35 -05:00
{
2023-02-21 15:12:22 -05:00
let next_load_id = local_data . get_index ( scope , 0 ) . unwrap ( ) ;
2023-01-19 04:00:35 -05:00
assert! ( next_load_id . is_int32 ( ) ) ;
let integer = next_load_id . to_integer ( scope ) . unwrap ( ) ;
let val = integer . int32_value ( scope ) . unwrap ( ) ;
self . next_load_id = val ;
}
{
2023-02-21 15:12:22 -05:00
let info_val = local_data . get_index ( scope , 1 ) . unwrap ( ) ;
let info_arr : v8 ::Local < v8 ::Array > = info_val . try_into ( ) . unwrap ( ) ;
let len = info_arr . length ( ) as usize ;
2023-03-08 22:42:46 -05:00
// Over allocate so executing a few scripts doesn't have to resize this vec.
let mut info = Vec ::with_capacity ( len + 16 ) ;
2023-02-21 15:12:22 -05:00
for i in 0 .. len {
let module_info_arr : v8 ::Local < v8 ::Array > = info_arr
. get_index ( scope , i as u32 )
. unwrap ( )
. try_into ( )
. unwrap ( ) ;
let id = module_info_arr
. get_index ( scope , 0 )
. unwrap ( )
. to_integer ( scope )
. unwrap ( )
. value ( ) as ModuleId ;
let main = module_info_arr
. get_index ( scope , 1 )
. unwrap ( )
. to_boolean ( scope )
. is_true ( ) ;
let name = module_info_arr
. get_index ( scope , 2 )
. unwrap ( )
2023-04-04 08:46:31 -04:00
. to_rust_string_lossy ( scope )
. into ( ) ;
2023-02-21 15:12:22 -05:00
let requests_arr : v8 ::Local < v8 ::Array > = module_info_arr
. get_index ( scope , 3 )
. unwrap ( )
. try_into ( )
. unwrap ( ) ;
let len = ( requests_arr . length ( ) as usize ) / 2 ;
let mut requests = Vec ::with_capacity ( len ) ;
for i in 0 .. len {
let specifier = requests_arr
. get_index ( scope , ( 2 * i ) as u32 )
. unwrap ( )
. to_rust_string_lossy ( scope ) ;
let asserted_module_type_no = requests_arr
. get_index ( scope , ( 2 * i + 1 ) as u32 )
. unwrap ( )
. to_integer ( scope )
. unwrap ( )
. value ( ) ;
let asserted_module_type = match asserted_module_type_no {
0 = > AssertedModuleType ::JavaScriptOrWasm ,
1 = > AssertedModuleType ::Json ,
_ = > unreachable! ( ) ,
} ;
requests . push ( ModuleRequest {
specifier ,
asserted_module_type ,
} ) ;
}
let module_type_no = module_info_arr
. get_index ( scope , 4 )
. unwrap ( )
. to_integer ( scope )
. unwrap ( )
. value ( ) ;
let module_type = match module_type_no {
0 = > ModuleType ::JavaScript ,
1 = > ModuleType ::Json ,
_ = > unreachable! ( ) ,
} ;
let module_info = ModuleInfo {
id ,
main ,
name ,
requests ,
module_type ,
} ;
info . push ( module_info ) ;
}
self . info = info ;
2023-01-19 04:00:35 -05:00
}
2023-04-04 08:46:31 -04:00
self
. by_name_mut ( AssertedModuleType ::JavaScriptOrWasm )
. clear ( ) ;
self . by_name_mut ( AssertedModuleType ::Json ) . clear ( ) ;
2023-01-19 04:00:35 -05:00
{
2023-02-21 15:12:22 -05:00
let by_name_arr : v8 ::Local < v8 ::Array > =
local_data . get_index ( scope , 2 ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
let len = by_name_arr . length ( ) as usize ;
for i in 0 .. len {
let arr : v8 ::Local < v8 ::Array > = by_name_arr
. get_index ( scope , i as u32 )
. unwrap ( )
. try_into ( )
. unwrap ( ) ;
let specifier =
arr . get_index ( scope , 0 ) . unwrap ( ) . to_rust_string_lossy ( scope ) ;
let asserted_module_type = match arr
. get_index ( scope , 1 )
. unwrap ( )
. to_integer ( scope )
. unwrap ( )
. value ( )
{
0 = > AssertedModuleType ::JavaScriptOrWasm ,
1 = > AssertedModuleType ::Json ,
_ = > unreachable! ( ) ,
} ;
let symbolic_module_val = arr . get_index ( scope , 2 ) . unwrap ( ) ;
let val = if symbolic_module_val . is_number ( ) {
SymbolicModule ::Mod (
symbolic_module_val
. to_integer ( scope )
. unwrap ( )
. value ( )
. try_into ( )
. unwrap ( ) ,
)
} else {
2023-04-04 08:46:31 -04:00
SymbolicModule ::Alias (
symbolic_module_val . to_rust_string_lossy ( scope ) . into ( ) ,
)
2023-02-21 15:12:22 -05:00
} ;
2023-04-04 08:46:31 -04:00
self
. by_name_mut ( asserted_module_type )
. insert ( specifier . into ( ) , val ) ;
2023-02-21 15:12:22 -05:00
}
2023-01-19 04:00:35 -05:00
}
2023-03-16 09:27:16 -04:00
self . handles = snapshotted_data . module_handles ;
2023-01-19 04:00:35 -05:00
}
2023-06-01 01:35:14 -04:00
pub ( crate ) fn new ( loader : Rc < dyn ModuleLoader > ) -> ModuleMap {
2019-03-26 11:56:34 -04:00
Self {
2023-01-19 07:47:41 -05:00
handles : vec ! [ ] ,
info : vec ! [ ] ,
2023-04-04 08:46:31 -04:00
by_name_js : HashMap ::new ( ) ,
by_name_json : HashMap ::new ( ) ,
2021-12-15 13:22:36 -05:00
next_load_id : 1 ,
2021-05-19 14:53:43 -04:00
loader ,
dynamic_import_map : HashMap ::new ( ) ,
preparing_dynamic_imports : FuturesUnordered ::new ( ) ,
pending_dynamic_imports : FuturesUnordered ::new ( ) ,
2021-12-15 13:22:36 -05:00
json_value_store : HashMap ::new ( ) ,
2019-03-26 11:56:34 -04:00
}
}
2021-02-23 09:22:55 -05:00
/// Get module id, following all aliases in case of module specifier
/// that had been redirected.
2023-05-09 06:37:13 -04:00
pub ( crate ) fn get_id (
2022-05-07 12:46:35 -04:00
& self ,
2023-04-04 08:46:31 -04:00
name : impl AsRef < str > ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ,
) -> Option < ModuleId > {
2023-04-04 08:46:31 -04:00
let map = self . by_name ( asserted_module_type ) ;
let first_symbolic_module = map . get ( name . as_ref ( ) ) ? ;
let mut mod_name = match first_symbolic_module {
SymbolicModule ::Mod ( mod_id ) = > return Some ( * mod_id ) ,
SymbolicModule ::Alias ( target ) = > target ,
} ;
2021-02-23 09:22:55 -05:00
loop {
2023-04-04 08:46:31 -04:00
let symbolic_module = map . get ( mod_name . as_ref ( ) ) ? ;
2021-02-23 09:22:55 -05:00
match symbolic_module {
SymbolicModule ::Alias ( target ) = > {
2023-04-04 08:46:31 -04:00
debug_assert! ( mod_name ! = target ) ;
2021-02-23 09:22:55 -05:00
mod_name = target ;
}
SymbolicModule ::Mod ( mod_id ) = > return Some ( * mod_id ) ,
}
}
2019-03-26 11:56:34 -04:00
}
2023-04-04 08:46:31 -04:00
fn new_json_module (
2023-03-21 18:33:12 -04:00
& mut self ,
scope : & mut v8 ::HandleScope ,
name : ModuleName ,
2023-04-04 08:46:31 -04:00
source : ModuleCode ,
2023-03-21 18:33:12 -04:00
) -> Result < ModuleId , ModuleError > {
2023-04-04 08:46:31 -04:00
let name_str = name . v8 ( scope ) ;
2022-05-05 07:16:25 -04:00
let source_str = v8 ::String ::new_from_utf8 (
scope ,
2023-03-21 18:33:12 -04:00
strip_bom ( source . as_bytes ( ) ) ,
2022-05-05 07:16:25 -04:00
v8 ::NewStringType ::Normal ,
)
. unwrap ( ) ;
2021-12-15 13:22:36 -05:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let parsed_json = match v8 ::json ::parse ( tc_scope , source_str ) {
Some ( parsed_json ) = > parsed_json ,
None = > {
assert! ( tc_scope . has_caught ( ) ) ;
let exception = tc_scope . exception ( ) . unwrap ( ) ;
2022-04-26 09:28:42 -04:00
let exception = v8 ::Global ::new ( tc_scope , exception ) ;
return Err ( ModuleError ::Exception ( exception ) ) ;
2021-12-15 13:22:36 -05:00
}
} ;
let export_names = [ v8 ::String ::new ( tc_scope , " default " ) . unwrap ( ) ] ;
let module = v8 ::Module ::create_synthetic_module (
tc_scope ,
name_str ,
& export_names ,
json_module_evaluation_steps ,
) ;
let handle = v8 ::Global ::< v8 ::Module > ::new ( tc_scope , module ) ;
let value_handle = v8 ::Global ::< v8 ::Value > ::new ( tc_scope , parsed_json ) ;
self . json_value_store . insert ( handle . clone ( ) , value_handle ) ;
2023-04-04 08:46:31 -04:00
let id =
self . create_module_info ( name , ModuleType ::Json , handle , false , vec! [ ] ) ;
2021-12-15 13:22:36 -05:00
Ok ( id )
}
2023-04-04 08:46:31 -04:00
/// Create and compile an ES module.
pub ( crate ) fn new_es_module (
2023-03-21 18:33:12 -04:00
& mut self ,
scope : & mut v8 ::HandleScope ,
main : bool ,
name : ModuleName ,
2023-04-04 08:46:31 -04:00
source : ModuleCode ,
2023-03-21 18:33:12 -04:00
is_dynamic_import : bool ,
) -> Result < ModuleId , ModuleError > {
2023-04-04 08:46:31 -04:00
let name_str = name . v8 ( scope ) ;
let source_str = source . v8 ( scope ) ;
2021-05-19 14:53:43 -04:00
let origin = bindings ::module_origin ( scope , name_str ) ;
let source = v8 ::script_compiler ::Source ::new ( source_str , Some ( & origin ) ) ;
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let maybe_module = v8 ::script_compiler ::compile_module ( tc_scope , source ) ;
if tc_scope . has_caught ( ) {
assert! ( maybe_module . is_none ( ) ) ;
2022-04-26 09:28:42 -04:00
let exception = tc_scope . exception ( ) . unwrap ( ) ;
let exception = v8 ::Global ::new ( tc_scope , exception ) ;
return Err ( ModuleError ::Exception ( exception ) ) ;
2021-05-19 14:53:43 -04:00
}
let module = maybe_module . unwrap ( ) ;
2021-12-15 13:22:36 -05:00
let mut requests : Vec < ModuleRequest > = vec! [ ] ;
2021-05-19 14:53:43 -04:00
let module_requests = module . get_module_requests ( ) ;
for i in 0 .. module_requests . length ( ) {
let module_request = v8 ::Local ::< v8 ::ModuleRequest > ::try_from (
module_requests . get ( tc_scope , i ) . unwrap ( ) ,
)
. unwrap ( ) ;
let import_specifier = module_request
. get_specifier ( )
. to_rust_string_lossy ( tc_scope ) ;
2021-12-15 13:22:36 -05:00
let import_assertions = module_request . get_import_assertions ( ) ;
let assertions = parse_import_assertions (
tc_scope ,
import_assertions ,
ImportAssertionsKind ::StaticImport ,
) ;
// FIXME(bartomieju): there are no stack frames if exception
// is thrown here
validate_import_assertions ( tc_scope , & assertions ) ;
if tc_scope . has_caught ( ) {
2022-04-26 09:28:42 -04:00
let exception = tc_scope . exception ( ) . unwrap ( ) ;
let exception = v8 ::Global ::new ( tc_scope , exception ) ;
return Err ( ModuleError ::Exception ( exception ) ) ;
2021-12-15 13:22:36 -05:00
}
2023-05-03 20:44:59 -04:00
let module_specifier = match self . loader . resolve (
2023-01-10 08:35:44 -05:00
& import_specifier ,
2023-03-21 18:33:12 -04:00
name . as_ref ( ) ,
2023-01-10 08:35:44 -05:00
if is_dynamic_import {
ResolutionKind ::DynamicImport
} else {
ResolutionKind ::Import
} ,
) {
Ok ( s ) = > s ,
Err ( e ) = > return Err ( ModuleError ::Other ( e ) ) ,
} ;
2022-05-07 12:46:35 -04:00
let asserted_module_type =
get_asserted_module_type_from_assertions ( & assertions ) ;
2021-12-15 13:22:36 -05:00
let request = ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : module_specifier . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type ,
2021-12-15 13:22:36 -05:00
} ;
requests . push ( request ) ;
2021-05-19 14:53:43 -04:00
}
2021-09-17 21:44:53 -04:00
if main {
2023-01-19 07:47:41 -05:00
let maybe_main_module = self . info . iter ( ) . find ( | module | module . main ) ;
2021-09-17 21:44:53 -04:00
if let Some ( main_module ) = maybe_main_module {
2022-04-26 09:28:42 -04:00
return Err ( ModuleError ::Other ( generic_error (
2021-09-17 21:44:53 -04:00
format! ( " Trying to create \" main \" module ( {:?} ), when one already exists ( {:?} ) " ,
2023-03-21 18:33:12 -04:00
name . as_ref ( ) ,
2021-09-17 21:44:53 -04:00
main_module . name ,
2022-04-26 09:28:42 -04:00
) ) ) ) ;
2021-09-17 21:44:53 -04:00
}
}
2021-05-19 14:53:43 -04:00
let handle = v8 ::Global ::< v8 ::Module > ::new ( tc_scope , module ) ;
2021-12-21 09:53:46 -05:00
let id = self . create_module_info (
2023-04-04 08:46:31 -04:00
name ,
2021-12-21 09:53:46 -05:00
ModuleType ::JavaScript ,
handle ,
main ,
requests ,
) ;
Ok ( id )
}
2023-05-28 14:44:41 -04:00
pub ( crate ) fn clear ( & mut self ) {
2023-06-01 01:35:14 -04:00
* self = Self ::new ( self . loader . clone ( ) )
2023-05-28 14:44:41 -04:00
}
pub ( crate ) fn get_handle_by_name (
& self ,
name : impl AsRef < str > ,
) -> Option < v8 ::Global < v8 ::Module > > {
let id = self
. get_id ( name . as_ref ( ) , AssertedModuleType ::JavaScriptOrWasm )
. or_else ( | | self . get_id ( name . as_ref ( ) , AssertedModuleType ::Json ) ) ? ;
self . get_handle ( id )
}
pub ( crate ) fn inject_handle (
& mut self ,
name : ModuleName ,
module_type : ModuleType ,
handle : v8 ::Global < v8 ::Module > ,
) {
self . create_module_info ( name , module_type , handle , false , vec! [ ] ) ;
}
2021-12-21 09:53:46 -05:00
fn create_module_info (
& mut self ,
2023-04-04 08:46:31 -04:00
name : FastString ,
2021-12-21 09:53:46 -05:00
module_type : ModuleType ,
handle : v8 ::Global < v8 ::Module > ,
main : bool ,
requests : Vec < ModuleRequest > ,
) -> ModuleId {
2023-01-19 07:47:41 -05:00
let id = self . handles . len ( ) ;
2023-04-04 08:46:31 -04:00
let ( name1 , name2 ) = name . into_cheap_copy ( ) ;
self
. by_name_mut ( module_type . into ( ) )
. insert ( name1 , SymbolicModule ::Mod ( id ) ) ;
2023-01-19 07:47:41 -05:00
self . handles . push ( handle ) ;
self . info . push ( ModuleInfo {
2019-03-26 11:56:34 -04:00
id ,
2023-01-19 07:47:41 -05:00
main ,
2023-04-04 08:46:31 -04:00
name : name2 ,
2023-01-19 07:47:41 -05:00
requests ,
module_type ,
} ) ;
2021-05-19 14:53:43 -04:00
2021-12-21 09:53:46 -05:00
id
2021-05-19 14:53:43 -04:00
}
2021-12-21 09:53:46 -05:00
fn get_requested_modules ( & self , id : ModuleId ) -> Option < & Vec < ModuleRequest > > {
2023-01-19 07:47:41 -05:00
self . info . get ( id ) . map ( | i | & i . requests )
2021-05-19 14:53:43 -04:00
}
2021-12-21 09:53:46 -05:00
fn is_registered (
2021-12-15 13:22:36 -05:00
& self ,
2023-04-04 08:46:31 -04:00
specifier : impl AsRef < str > ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ,
2021-12-15 13:22:36 -05:00
) -> bool {
2023-04-04 08:46:31 -04:00
if let Some ( id ) = self . get_id ( specifier . as_ref ( ) , asserted_module_type ) {
2023-01-19 07:47:41 -05:00
let info = self . get_info_by_id ( id ) . unwrap ( ) ;
2022-05-07 12:46:35 -04:00
return asserted_module_type = = info . module_type . into ( ) ;
2021-12-15 13:22:36 -05:00
}
false
2019-03-26 11:56:34 -04:00
}
2019-04-16 15:13:42 -04:00
2023-04-04 08:46:31 -04:00
pub ( crate ) fn by_name (
& self ,
asserted_module_type : AssertedModuleType ,
) -> & HashMap < ModuleName , SymbolicModule > {
match asserted_module_type {
AssertedModuleType ::Json = > & self . by_name_json ,
AssertedModuleType ::JavaScriptOrWasm = > & self . by_name_js ,
}
}
pub ( crate ) fn by_name_mut (
& mut self ,
asserted_module_type : AssertedModuleType ,
) -> & mut HashMap < ModuleName , SymbolicModule > {
match asserted_module_type {
AssertedModuleType ::Json = > & mut self . by_name_json ,
AssertedModuleType ::JavaScriptOrWasm = > & mut self . by_name_js ,
}
}
2022-05-07 12:46:35 -04:00
fn alias (
& mut self ,
2023-04-04 08:46:31 -04:00
name : FastString ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ,
2023-04-04 08:46:31 -04:00
target : FastString ,
2022-05-07 12:46:35 -04:00
) {
2023-04-04 08:46:31 -04:00
debug_assert_ne! ( name , target ) ;
self
. by_name_mut ( asserted_module_type )
. insert ( name , SymbolicModule ::Alias ( target ) ) ;
2019-04-18 21:33:50 -04:00
}
2020-09-14 23:49:12 -04:00
#[ cfg(test) ]
2022-05-07 12:46:35 -04:00
fn is_alias (
& self ,
name : & str ,
asserted_module_type : AssertedModuleType ,
) -> bool {
2023-04-04 08:46:31 -04:00
let cond = self . by_name ( asserted_module_type ) . get ( name ) ;
2021-02-23 09:22:55 -05:00
matches! ( cond , Some ( SymbolicModule ::Alias ( _ ) ) )
2019-04-18 21:33:50 -04:00
}
2021-12-21 09:53:46 -05:00
pub ( crate ) fn get_handle (
& self ,
id : ModuleId ,
) -> Option < v8 ::Global < v8 ::Module > > {
2023-01-19 07:47:41 -05:00
self . handles . get ( id ) . cloned ( )
2020-11-21 10:23:35 -05:00
}
2021-12-21 09:53:46 -05:00
pub ( crate ) fn get_info (
2020-11-21 10:23:35 -05:00
& self ,
global : & v8 ::Global < v8 ::Module > ,
) -> Option < & ModuleInfo > {
2023-01-19 07:47:41 -05:00
if let Some ( id ) = self . handles . iter ( ) . position ( | module | module = = global ) {
2020-11-21 10:23:35 -05:00
return self . info . get ( id ) ;
2020-01-08 09:06:04 -05:00
}
2020-11-21 10:23:35 -05:00
None
2020-01-08 09:06:04 -05:00
}
2021-04-28 12:28:46 -04:00
2023-01-19 07:47:41 -05:00
pub ( crate ) fn get_info_by_id ( & self , id : ModuleId ) -> Option < & ModuleInfo > {
2021-04-28 12:28:46 -04:00
self . info . get ( id )
}
2021-05-19 14:53:43 -04:00
2021-12-21 09:53:46 -05:00
pub ( crate ) async fn load_main (
2021-06-28 21:03:02 -04:00
module_map_rc : Rc < RefCell < ModuleMap > > ,
2023-04-04 08:46:31 -04:00
specifier : impl AsRef < str > ,
2021-11-16 09:02:28 -05:00
) -> Result < RecursiveModuleLoad , Error > {
2023-04-04 08:46:31 -04:00
let load =
RecursiveModuleLoad ::main ( specifier . as_ref ( ) , module_map_rc . clone ( ) ) ;
2021-06-19 10:14:43 -04:00
load . prepare ( ) . await ? ;
Ok ( load )
2021-05-19 14:53:43 -04:00
}
2021-12-21 09:53:46 -05:00
pub ( crate ) async fn load_side (
2021-09-17 21:44:53 -04:00
module_map_rc : Rc < RefCell < ModuleMap > > ,
2023-04-04 08:46:31 -04:00
specifier : impl AsRef < str > ,
2021-11-16 09:02:28 -05:00
) -> Result < RecursiveModuleLoad , Error > {
2023-04-04 08:46:31 -04:00
let load =
RecursiveModuleLoad ::side ( specifier . as_ref ( ) , module_map_rc . clone ( ) ) ;
2021-09-17 21:44:53 -04:00
load . prepare ( ) . await ? ;
Ok ( load )
}
2021-05-19 14:53:43 -04:00
// Initiate loading of a module graph imported using `import()`.
2021-12-21 09:53:46 -05:00
pub ( crate ) fn load_dynamic_import (
2021-06-28 21:03:02 -04:00
module_map_rc : Rc < RefCell < ModuleMap > > ,
2021-05-19 14:53:43 -04:00
specifier : & str ,
referrer : & str ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ,
2021-05-19 14:53:43 -04:00
resolver_handle : v8 ::Global < v8 ::PromiseResolver > ,
) {
let load = RecursiveModuleLoad ::dynamic_import (
specifier ,
referrer ,
2022-05-07 12:46:35 -04:00
asserted_module_type ,
2021-06-28 21:03:02 -04:00
module_map_rc . clone ( ) ,
) ;
module_map_rc
. borrow_mut ( )
. dynamic_import_map
. insert ( load . id , resolver_handle ) ;
2023-02-08 12:13:33 -05:00
2023-05-03 20:44:59 -04:00
let loader = module_map_rc . borrow ( ) . loader . clone ( ) ;
let resolve_result =
loader . resolve ( specifier , referrer , ResolutionKind ::DynamicImport ) ;
2021-06-19 10:14:43 -04:00
let fut = match resolve_result {
Ok ( module_specifier ) = > {
2021-12-15 13:22:36 -05:00
if module_map_rc
. borrow ( )
2023-04-04 08:46:31 -04:00
. is_registered ( module_specifier , asserted_module_type )
2021-12-15 13:22:36 -05:00
{
2021-06-19 10:14:43 -04:00
async move { ( load . id , Ok ( load ) ) } . boxed_local ( )
} else {
async move { ( load . id , load . prepare ( ) . await . map ( | ( ) | load ) ) }
. boxed_local ( )
}
}
Err ( error ) = > async move { ( load . id , Err ( error ) ) } . boxed_local ( ) ,
} ;
2021-06-28 21:03:02 -04:00
module_map_rc
. borrow_mut ( )
. preparing_dynamic_imports
. push ( fut ) ;
2021-05-19 14:53:43 -04:00
}
2021-12-21 09:53:46 -05:00
pub ( crate ) fn has_pending_dynamic_imports ( & self ) -> bool {
2021-05-19 14:53:43 -04:00
! ( self . preparing_dynamic_imports . is_empty ( )
& & self . pending_dynamic_imports . is_empty ( ) )
}
/// Called by `module_resolve_callback` during module instantiation.
2021-12-21 09:53:46 -05:00
pub ( crate ) fn resolve_callback < ' s > (
2021-05-19 14:53:43 -04:00
& self ,
scope : & mut v8 ::HandleScope < ' s > ,
specifier : & str ,
referrer : & str ,
2021-12-15 13:22:36 -05:00
import_assertions : HashMap < String , String > ,
2021-05-19 14:53:43 -04:00
) -> Option < v8 ::Local < ' s , v8 ::Module > > {
2023-05-03 20:44:59 -04:00
let resolved_specifier = self
. loader
. resolve ( specifier , referrer , ResolutionKind ::Import )
. expect ( " Module should have been already resolved " ) ;
2021-05-19 14:53:43 -04:00
2022-05-07 12:46:35 -04:00
let module_type =
get_asserted_module_type_from_assertions ( & import_assertions ) ;
2021-12-15 13:22:36 -05:00
if let Some ( id ) = self . get_id ( resolved_specifier . as_str ( ) , module_type ) {
2021-05-19 14:53:43 -04:00
if let Some ( handle ) = self . get_handle ( id ) {
return Some ( v8 ::Local ::new ( scope , handle ) ) ;
}
}
None
}
2019-03-26 11:56:34 -04:00
}
2023-06-01 01:35:14 -04:00
impl Default for ModuleMap {
fn default ( ) -> Self {
Self ::new ( Rc ::new ( NoopModuleLoader ) )
}
}
2019-03-26 11:56:34 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
2023-04-04 08:46:31 -04:00
use crate ::ascii_str ;
2020-09-06 15:44:29 -04:00
use crate ::JsRuntime ;
2023-05-31 10:19:06 -04:00
use crate ::JsRuntimeForSnapshot ;
2020-09-11 09:18:49 -04:00
use crate ::RuntimeOptions ;
2022-09-06 08:35:04 -04:00
use crate ::Snapshot ;
2022-03-14 13:44:15 -04:00
use deno_ops ::op ;
2023-05-06 10:01:05 -04:00
use futures ::future ::poll_fn ;
2019-11-16 19:17:47 -05:00
use futures ::future ::FutureExt ;
2021-07-06 23:48:01 -04:00
use parking_lot ::Mutex ;
2019-04-16 15:13:42 -04:00
use std ::fmt ;
2019-11-16 19:17:47 -05:00
use std ::future ::Future ;
2021-05-19 14:53:43 -04:00
use std ::io ;
2021-06-28 21:03:02 -04:00
use std ::path ::PathBuf ;
2023-01-14 23:18:58 -05:00
use std ::sync ::atomic ::AtomicUsize ;
use std ::sync ::atomic ::Ordering ;
2020-02-14 19:18:36 -05:00
use std ::sync ::Arc ;
2022-03-14 13:44:15 -04:00
// deno_ops macros generate code assuming deno_core in scope.
mod deno_core {
pub use crate ::* ;
}
2019-03-26 11:56:34 -04:00
2020-09-05 20:34:02 -04:00
#[ derive(Default) ]
2019-03-26 11:56:34 -04:00
struct MockLoader {
2019-06-05 16:35:38 -04:00
pub loads : Arc < Mutex < Vec < String > > > ,
2019-03-26 11:56:34 -04:00
}
impl MockLoader {
2020-09-05 20:34:02 -04:00
fn new ( ) -> Rc < Self > {
Default ::default ( )
2019-03-26 11:56:34 -04:00
}
}
2019-04-18 21:33:50 -04:00
fn mock_source_code ( url : & str ) -> Option < ( & 'static str , & 'static str ) > {
2021-12-21 09:53:46 -05:00
const A_SRC : & str = r #"
import { b } from " /b.js " ;
import { c } from " /c.js " ;
if ( b ( ) ! = 'b' ) throw Error ( ) ;
if ( c ( ) ! = 'c' ) throw Error ( ) ;
if ( ! import . meta . main ) throw Error ( ) ;
if ( import . meta . url ! = ' file :///a.js') throw Error();
" #;
const B_SRC : & str = r #"
import { c } from " /c.js " ;
if ( c ( ) ! = 'c' ) throw Error ( ) ;
export function b ( ) { return 'b' ; }
if ( import . meta . main ) throw Error ( ) ;
if ( import . meta . url ! = ' file :///b.js') throw Error();
" #;
const C_SRC : & str = r #"
import { d } from " /d.js " ;
export function c ( ) { return 'c' ; }
if ( d ( ) ! = 'd' ) throw Error ( ) ;
if ( import . meta . main ) throw Error ( ) ;
if ( import . meta . url ! = ' file :///c.js') throw Error();
" #;
const D_SRC : & str = r #"
export function d ( ) { return 'd' ; }
if ( import . meta . main ) throw Error ( ) ;
if ( import . meta . url ! = ' file :///d.js') throw Error();
" #;
const CIRCULAR1_SRC : & str = r #"
import " /circular2.js " ;
Deno . core . print ( " circular1 " ) ;
" #;
const CIRCULAR2_SRC : & str = r #"
import " /circular3.js " ;
Deno . core . print ( " circular2 " ) ;
" #;
const CIRCULAR3_SRC : & str = r #"
import " /circular1.js " ;
import " /circular2.js " ;
Deno . core . print ( " circular3 " ) ;
" #;
const REDIRECT1_SRC : & str = r #"
import " ./redirect2.js " ;
Deno . core . print ( " redirect1 " ) ;
" #;
const REDIRECT2_SRC : & str = r #"
import " ./redirect3.js " ;
Deno . core . print ( " redirect2 " ) ;
" #;
const REDIRECT3_SRC : & str = r # "Deno.core.print("redirect3");"# ;
const MAIN_SRC : & str = r #"
// never_ready.js never loads.
import " /never_ready.js " ;
// slow.js resolves after one tick.
import " /slow.js " ;
" #;
const SLOW_SRC : & str = r #"
// Circular import of never_ready.js
// Does this trigger two ModuleLoader calls? It shouldn't.
import " /never_ready.js " ;
import " /a.js " ;
" #;
const BAD_IMPORT_SRC : & str = r # "import "foo";"# ;
2019-04-18 21:33:50 -04:00
// (code, real_module_name)
2019-06-12 19:55:59 -04:00
let spec : Vec < & str > = url . split ( " file:// " ) . collect ( ) ;
match spec [ 1 ] {
" /a.js " = > Some ( ( A_SRC , " file:///a.js " ) ) ,
" /b.js " = > Some ( ( B_SRC , " file:///b.js " ) ) ,
" /c.js " = > Some ( ( C_SRC , " file:///c.js " ) ) ,
" /d.js " = > Some ( ( D_SRC , " file:///d.js " ) ) ,
" /circular1.js " = > Some ( ( CIRCULAR1_SRC , " file:///circular1.js " ) ) ,
" /circular2.js " = > Some ( ( CIRCULAR2_SRC , " file:///circular2.js " ) ) ,
" /circular3.js " = > Some ( ( CIRCULAR3_SRC , " file:///circular3.js " ) ) ,
" /redirect1.js " = > Some ( ( REDIRECT1_SRC , " file:///redirect1.js " ) ) ,
// pretend redirect - real module name is different than one requested
" /redirect2.js " = > Some ( ( REDIRECT2_SRC , " file:///dir/redirect2.js " ) ) ,
" /dir/redirect3.js " = > Some ( ( REDIRECT3_SRC , " file:///redirect3.js " ) ) ,
" /slow.js " = > Some ( ( SLOW_SRC , " file:///slow.js " ) ) ,
" /never_ready.js " = > {
2019-08-07 12:55:39 -04:00
Some ( ( " should never be Ready " , " file:///never_ready.js " ) )
2019-06-12 19:55:59 -04:00
}
" /main.js " = > Some ( ( MAIN_SRC , " file:///main.js " ) ) ,
" /bad_import.js " = > Some ( ( BAD_IMPORT_SRC , " file:///bad_import.js " ) ) ,
2019-10-19 17:19:19 -04:00
// deliberately empty code.
" /main_with_code.js " = > Some ( ( " " , " file:///main_with_code.js " ) ) ,
2019-04-16 15:13:42 -04:00
_ = > None ,
}
}
#[ derive(Debug, PartialEq) ]
enum MockError {
ResolveErr ,
LoadErr ,
}
impl fmt ::Display for MockError {
fn fmt ( & self , _f : & mut fmt ::Formatter ) -> fmt ::Result {
unimplemented! ( )
}
}
2021-11-16 09:02:28 -05:00
impl std ::error ::Error for MockError {
fn cause ( & self ) -> Option < & dyn std ::error ::Error > {
2019-04-16 15:13:42 -04:00
unimplemented! ( )
}
}
struct DelayedSourceCodeFuture {
url : String ,
counter : u32 ,
}
impl Future for DelayedSourceCodeFuture {
2021-11-16 09:02:28 -05:00
type Output = Result < ModuleSource , Error > ;
2019-04-16 15:13:42 -04:00
2019-11-16 19:17:47 -05:00
fn poll ( self : Pin < & mut Self > , cx : & mut Context ) -> Poll < Self ::Output > {
let inner = self . get_mut ( ) ;
inner . counter + = 1 ;
if inner . url = = " file:///never_ready.js " {
return Poll ::Pending ;
2019-08-07 12:55:39 -04:00
}
2019-11-16 19:17:47 -05:00
if inner . url = = " file:///slow.js " & & inner . counter < 2 {
2019-08-07 12:55:39 -04:00
// TODO(ry) Hopefully in the future we can remove current task
2023-05-06 10:01:05 -04:00
// notification.
2019-11-16 19:17:47 -05:00
cx . waker ( ) . wake_by_ref ( ) ;
return Poll ::Pending ;
2019-04-16 15:13:42 -04:00
}
2019-11-16 19:17:47 -05:00
match mock_source_code ( & inner . url ) {
2023-04-04 08:46:31 -04:00
Some ( src ) = > Poll ::Ready ( Ok ( ModuleSource ::for_test_with_redirect (
src . 0 ,
inner . url . as_str ( ) ,
src . 1 ,
) ) ) ,
2019-11-16 19:17:47 -05:00
None = > Poll ::Ready ( Err ( MockError ::LoadErr . into ( ) ) ) ,
2019-04-16 15:13:42 -04:00
}
}
}
2020-03-02 13:12:49 -05:00
impl ModuleLoader for MockLoader {
2019-06-09 09:08:20 -04:00
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2019-06-12 19:55:59 -04:00
let referrer = if referrer = = " . " {
" file:/// "
} else {
referrer
} ;
2021-12-15 13:22:36 -05:00
let output_specifier = match resolve_import ( specifier , referrer ) {
2021-02-17 13:47:18 -05:00
Ok ( specifier ) = > specifier ,
Err ( .. ) = > return Err ( MockError ::ResolveErr . into ( ) ) ,
} ;
2019-06-12 19:55:59 -04:00
2022-08-21 13:31:14 -04:00
if mock_source_code ( output_specifier . as_ref ( ) ) . is_some ( ) {
2019-04-18 21:33:50 -04:00
Ok ( output_specifier )
2019-04-16 15:13:42 -04:00
} else {
2019-07-10 18:53:48 -04:00
Err ( MockError ::ResolveErr . into ( ) )
2019-04-16 15:13:42 -04:00
}
2019-03-26 11:56:34 -04:00
}
2019-06-12 19:55:59 -04:00
fn load (
& self ,
module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2020-01-25 12:53:16 -05:00
_is_dyn_import : bool ,
2020-03-02 13:12:49 -05:00
) -> Pin < Box < ModuleSourceFuture > > {
2021-07-06 23:48:01 -04:00
let mut loads = self . loads . lock ( ) ;
2019-06-12 19:55:59 -04:00
loads . push ( module_specifier . to_string ( ) ) ;
let url = module_specifier . to_string ( ) ;
2019-11-16 19:17:47 -05:00
DelayedSourceCodeFuture { url , counter : 0 } . boxed ( )
2019-03-26 11:56:34 -04:00
}
}
#[ test ]
fn test_recursive_load ( ) {
2020-01-08 09:06:04 -05:00
let loader = MockLoader ::new ( ) ;
let loads = loader . loads . clone ( ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2021-12-15 13:22:36 -05:00
let spec = resolve_url ( " file:///a.js " ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
let a_id_fut = runtime . load_main_module ( & spec , None ) ;
2021-12-21 09:53:46 -05:00
let a_id = futures ::executor ::block_on ( a_id_fut ) . unwrap ( ) ;
2020-12-20 09:14:19 -05:00
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-07-30 07:36:43 -04:00
let _ = runtime . mod_evaluate ( a_id ) ;
2021-05-26 15:07:12 -04:00
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
2021-07-06 23:48:01 -04:00
let l = loads . lock ( ) ;
2020-01-08 09:06:04 -05:00
assert_eq! (
l . to_vec ( ) ,
vec! [
" file:///a.js " ,
" file:///b.js " ,
" file:///c.js " ,
" file:///d.js "
]
) ;
2019-03-26 11:56:34 -04:00
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) ;
2021-05-19 14:53:43 -04:00
let modules = module_map_rc . borrow ( ) ;
2020-01-08 09:06:04 -05:00
assert_eq! (
2022-05-07 12:46:35 -04:00
modules . get_id ( " file:///a.js " , AssertedModuleType ::JavaScriptOrWasm ) ,
2021-12-15 13:22:36 -05:00
Some ( a_id )
) ;
let b_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///b.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
let c_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///c.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
let d_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///d.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
assert_eq! (
modules . get_requested_modules ( a_id ) ,
2020-01-25 12:53:16 -05:00
Some ( & vec! [
2021-12-15 13:22:36 -05:00
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///b.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ,
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///c.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ,
2020-01-25 12:53:16 -05:00
] )
) ;
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( b_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///c.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} , ] )
2020-01-25 12:53:16 -05:00
) ;
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( c_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///d.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} , ] )
2020-01-08 09:06:04 -05:00
) ;
2021-12-15 13:22:36 -05:00
assert_eq! ( modules . get_requested_modules ( d_id ) , Some ( & vec! [ ] ) ) ;
2019-03-26 11:56:34 -04:00
}
2021-05-19 14:53:43 -04:00
#[ test ]
fn test_mods ( ) {
#[ derive(Default) ]
struct ModsLoader {
pub count : Arc < AtomicUsize > ,
}
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-05-19 14:53:43 -04:00
self . count . fetch_add ( 1 , Ordering ::Relaxed ) ;
assert_eq! ( specifier , " ./b.js " ) ;
assert_eq! ( referrer , " file:///a.js " ) ;
2021-12-15 13:22:36 -05:00
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
2021-05-19 14:53:43 -04:00
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-05-19 14:53:43 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
unreachable! ( )
}
}
let loader = Rc ::new ( ModsLoader ::default ( ) ) ;
let resolve_count = loader . count . clone ( ) ;
2022-03-14 13:44:15 -04:00
static DISPATCH_COUNT : AtomicUsize = AtomicUsize ::new ( 0 ) ;
2021-05-19 14:53:43 -04:00
2022-03-14 13:44:15 -04:00
#[ op ]
2022-05-13 04:36:31 -04:00
fn op_test ( control : u8 ) -> u8 {
2022-03-14 13:44:15 -04:00
DISPATCH_COUNT . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-05-19 14:53:43 -04:00
assert_eq! ( control , 42 ) ;
2022-05-13 04:36:31 -04:00
43
2022-03-14 13:44:15 -04:00
}
2021-05-19 14:53:43 -04:00
2023-03-17 14:22:15 -04:00
deno_core ::extension! ( test_ext , ops = [ op_test ] ) ;
2021-12-29 09:21:42 -05:00
2021-05-19 14:53:43 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
2023-06-13 11:45:06 -04:00
extensions : vec ! [ test_ext ::init ( ) ] ,
2021-05-19 14:53:43 -04:00
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-05-19 14:53:43 -04:00
" setup.js " ,
r #"
function assert ( cond ) {
if ( ! cond ) {
throw Error ( " assert " ) ;
}
}
" #,
)
. unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( DISPATCH_COUNT . load ( Ordering ::Relaxed ) , 0 ) ;
2021-05-19 14:53:43 -04:00
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) . clone ( ) ;
2021-05-19 14:53:43 -04:00
let ( mod_a , mod_b ) = {
let scope = & mut runtime . handle_scope ( ) ;
let mut module_map = module_map_rc . borrow_mut ( ) ;
2023-04-04 08:46:31 -04:00
let specifier_a = ascii_str! ( " file:///a.js " ) ;
2021-05-19 14:53:43 -04:00
let mod_a = module_map
2021-12-15 13:22:36 -05:00
. new_es_module (
2021-05-19 14:53:43 -04:00
scope ,
true ,
2023-04-04 08:46:31 -04:00
specifier_a ,
ascii_str! (
r #"
2021-05-19 14:53:43 -04:00
import { b } from ' . / b . js '
if ( b ( ) ! = 'b' ) throw Error ( ) ;
let control = 42 ;
2022-08-11 09:56:56 -04:00
Deno . core . ops . op_test ( control ) ;
2023-03-21 18:33:12 -04:00
" #
2023-04-04 08:46:31 -04:00
) ,
2023-01-10 08:35:44 -05:00
false ,
2021-05-19 14:53:43 -04:00
)
. unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( DISPATCH_COUNT . load ( Ordering ::Relaxed ) , 0 ) ;
2021-12-15 13:22:36 -05:00
let imports = module_map . get_requested_modules ( mod_a ) ;
2021-05-19 14:53:43 -04:00
assert_eq! (
imports ,
2021-12-15 13:22:36 -05:00
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///b.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} , ] )
2021-05-19 14:53:43 -04:00
) ;
let mod_b = module_map
2021-12-15 13:22:36 -05:00
. new_es_module (
2021-05-19 14:53:43 -04:00
scope ,
false ,
2023-04-04 08:46:31 -04:00
ascii_str! ( " file:///b.js " ) ,
ascii_str! ( " export function b() { return 'b' } " ) ,
2023-01-10 08:35:44 -05:00
false ,
2021-05-19 14:53:43 -04:00
)
. unwrap ( ) ;
2021-12-15 13:22:36 -05:00
let imports = module_map . get_requested_modules ( mod_b ) . unwrap ( ) ;
2021-05-19 14:53:43 -04:00
assert_eq! ( imports . len ( ) , 0 ) ;
( mod_a , mod_b )
} ;
runtime . instantiate_module ( mod_b ) . unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( DISPATCH_COUNT . load ( Ordering ::Relaxed ) , 0 ) ;
2021-05-19 14:53:43 -04:00
assert_eq! ( resolve_count . load ( Ordering ::SeqCst ) , 1 ) ;
runtime . instantiate_module ( mod_a ) . unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( DISPATCH_COUNT . load ( Ordering ::Relaxed ) , 0 ) ;
2021-05-19 14:53:43 -04:00
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-07-30 07:36:43 -04:00
let _ = runtime . mod_evaluate ( mod_a ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( DISPATCH_COUNT . load ( Ordering ::Relaxed ) , 1 ) ;
2021-05-19 14:53:43 -04:00
}
2021-12-15 13:22:36 -05:00
#[ test ]
fn test_json_module ( ) {
#[ derive(Default) ]
struct ModsLoader {
pub count : Arc < AtomicUsize > ,
}
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-12-15 13:22:36 -05:00
) -> Result < ModuleSpecifier , Error > {
self . count . fetch_add ( 1 , Ordering ::Relaxed ) ;
assert_eq! ( specifier , " ./b.json " ) ;
assert_eq! ( referrer , " file:///a.js " ) ;
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-12-15 13:22:36 -05:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
unreachable! ( )
}
}
let loader = Rc ::new ( ModsLoader ::default ( ) ) ;
let resolve_count = loader . count . clone ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-12-15 13:22:36 -05:00
" setup.js " ,
r #"
function assert ( cond ) {
if ( ! cond ) {
throw Error ( " assert " ) ;
}
}
" #,
)
. unwrap ( ) ;
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) . clone ( ) ;
2021-12-15 13:22:36 -05:00
let ( mod_a , mod_b ) = {
let scope = & mut runtime . handle_scope ( ) ;
let mut module_map = module_map_rc . borrow_mut ( ) ;
2023-04-04 08:46:31 -04:00
let specifier_a = ascii_str! ( " file:///a.js " ) ;
2021-12-15 13:22:36 -05:00
let mod_a = module_map
. new_es_module (
scope ,
true ,
2023-04-04 08:46:31 -04:00
specifier_a ,
ascii_str! (
r #"
2021-12-15 13:22:36 -05:00
import jsonData from ' . / b . json ' assert { type : " json " } ;
assert ( jsonData . a = = " b " ) ;
assert ( jsonData . c . d = = 10 ) ;
2023-03-21 18:33:12 -04:00
" #
2023-04-04 08:46:31 -04:00
) ,
2023-01-10 08:35:44 -05:00
false ,
2021-12-15 13:22:36 -05:00
)
. unwrap ( ) ;
let imports = module_map . get_requested_modules ( mod_a ) ;
assert_eq! (
imports ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///b.json " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::Json ,
2021-12-15 13:22:36 -05:00
} , ] )
) ;
let mod_b = module_map
. new_json_module (
scope ,
2023-04-04 08:46:31 -04:00
ascii_str! ( " file:///b.json " ) ,
ascii_str! ( " { \" a \" : \" b \" , \" c \" : { \" d \" : 10}} " ) ,
2021-12-15 13:22:36 -05:00
)
. unwrap ( ) ;
let imports = module_map . get_requested_modules ( mod_b ) . unwrap ( ) ;
assert_eq! ( imports . len ( ) , 0 ) ;
( mod_a , mod_b )
} ;
runtime . instantiate_module ( mod_b ) . unwrap ( ) ;
assert_eq! ( resolve_count . load ( Ordering ::SeqCst ) , 1 ) ;
runtime . instantiate_module ( mod_a ) . unwrap ( ) ;
let receiver = runtime . mod_evaluate ( mod_a ) ;
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
futures ::executor ::block_on ( receiver ) . unwrap ( ) . unwrap ( ) ;
}
2023-05-06 10:01:05 -04:00
#[ tokio::test ]
async fn dyn_import_err ( ) {
2021-05-19 14:53:43 -04:00
#[ derive(Clone, Default) ]
struct DynImportErrLoader {
pub count : Arc < AtomicUsize > ,
}
impl ModuleLoader for DynImportErrLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-05-19 14:53:43 -04:00
self . count . fetch_add ( 1 , Ordering ::Relaxed ) ;
assert_eq! ( specifier , " /foo.js " ) ;
assert_eq! ( referrer , " file:///dyn_import2.js " ) ;
2021-12-15 13:22:36 -05:00
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
2021-05-19 14:53:43 -04:00
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-05-19 14:53:43 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
async { Err ( io ::Error ::from ( io ::ErrorKind ::NotFound ) . into ( ) ) } . boxed ( )
}
}
2023-02-06 08:49:26 -05:00
let loader = Rc ::new ( DynImportErrLoader ::default ( ) ) ;
let count = loader . count . clone ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2021-05-19 14:53:43 -04:00
2023-02-06 08:49:26 -05:00
// Test an erroneous dynamic import where the specified module isn't found.
2023-05-06 10:01:05 -04:00
poll_fn ( move | cx | {
2021-05-19 14:53:43 -04:00
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-05-19 14:53:43 -04:00
" file:///dyn_import2.js " ,
r #"
( async ( ) = > {
await import ( " /foo.js " ) ;
} ) ( ) ;
" #,
)
. unwrap ( ) ;
// We should get an error here.
2021-05-26 15:07:12 -04:00
let result = runtime . poll_event_loop ( cx , false ) ;
2021-05-19 14:53:43 -04:00
if let Poll ::Ready ( Ok ( _ ) ) = result {
unreachable! ( ) ;
}
2021-06-28 21:03:02 -04:00
assert_eq! ( count . load ( Ordering ::Relaxed ) , 4 ) ;
2023-05-06 10:01:05 -04:00
Poll ::Ready ( ( ) )
2021-05-19 14:53:43 -04:00
} )
2023-05-06 10:01:05 -04:00
. await ;
2021-05-19 14:53:43 -04:00
}
#[ derive(Clone, Default) ]
struct DynImportOkLoader {
pub prepare_load_count : Arc < AtomicUsize > ,
pub resolve_count : Arc < AtomicUsize > ,
pub load_count : Arc < AtomicUsize > ,
}
impl ModuleLoader for DynImportOkLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-05-19 14:53:43 -04:00
let c = self . resolve_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-06-28 21:03:02 -04:00
assert! ( c < 7 ) ;
2021-05-19 14:53:43 -04:00
assert_eq! ( specifier , " ./b.js " ) ;
assert_eq! ( referrer , " file:///dyn_import3.js " ) ;
2021-12-15 13:22:36 -05:00
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
2021-05-19 14:53:43 -04:00
Ok ( s )
}
fn load (
& self ,
specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-05-19 14:53:43 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
self . load_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
2023-04-04 08:46:31 -04:00
let info =
ModuleSource ::for_test ( " export function b() { return 'b' } " , specifier ) ;
2021-05-19 14:53:43 -04:00
async move { Ok ( info ) } . boxed ( )
}
fn prepare_load (
& self ,
_module_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < String > ,
_is_dyn_import : bool ,
2021-11-16 09:02:28 -05:00
) -> Pin < Box < dyn Future < Output = Result < ( ) , Error > > > > {
2021-05-19 14:53:43 -04:00
self . prepare_load_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
async { Ok ( ( ) ) } . boxed_local ( )
}
}
2023-05-06 10:01:05 -04:00
#[ tokio::test ]
async fn dyn_import_ok ( ) {
2023-02-06 08:49:26 -05:00
let loader = Rc ::new ( DynImportOkLoader ::default ( ) ) ;
let prepare_load_count = loader . prepare_load_count . clone ( ) ;
let resolve_count = loader . resolve_count . clone ( ) ;
let load_count = loader . load_count . clone ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2023-05-06 10:01:05 -04:00
poll_fn ( move | cx | {
2021-05-19 14:53:43 -04:00
// Dynamically import mod_b
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-05-19 14:53:43 -04:00
" file:///dyn_import3.js " ,
r #"
( async ( ) = > {
let mod = await import ( " ./b.js " ) ;
if ( mod . b ( ) ! = = 'b' ) {
throw Error ( " bad1 " ) ;
}
// And again!
mod = await import ( " ./b.js " ) ;
if ( mod . b ( ) ! = = 'b' ) {
throw Error ( " bad2 " ) ;
}
} ) ( ) ;
" #,
)
. unwrap ( ) ;
2021-05-26 15:07:12 -04:00
assert! ( matches! (
runtime . poll_event_loop ( cx , false ) ,
Poll ::Ready ( Ok ( _ ) )
) ) ;
2022-06-25 14:56:29 -04:00
assert_eq! ( prepare_load_count . load ( Ordering ::Relaxed ) , 1 ) ;
2021-06-28 21:03:02 -04:00
assert_eq! ( resolve_count . load ( Ordering ::Relaxed ) , 7 ) ;
assert_eq! ( load_count . load ( Ordering ::Relaxed ) , 1 ) ;
2021-05-26 15:07:12 -04:00
assert! ( matches! (
runtime . poll_event_loop ( cx , false ) ,
Poll ::Ready ( Ok ( _ ) )
) ) ;
2021-06-28 21:03:02 -04:00
assert_eq! ( resolve_count . load ( Ordering ::Relaxed ) , 7 ) ;
assert_eq! ( load_count . load ( Ordering ::Relaxed ) , 1 ) ;
2023-05-06 10:01:05 -04:00
Poll ::Ready ( ( ) )
2021-05-19 14:53:43 -04:00
} )
2023-05-06 10:01:05 -04:00
. await ;
2021-05-19 14:53:43 -04:00
}
2023-05-06 10:01:05 -04:00
#[ tokio::test ]
async fn dyn_import_borrow_mut_error ( ) {
2021-05-19 14:53:43 -04:00
// https://github.com/denoland/deno/issues/6054
2023-02-06 08:49:26 -05:00
let loader = Rc ::new ( DynImportOkLoader ::default ( ) ) ;
let prepare_load_count = loader . prepare_load_count . clone ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2023-05-06 10:01:05 -04:00
poll_fn ( move | cx | {
2021-05-19 14:53:43 -04:00
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-05-19 14:53:43 -04:00
" file:///dyn_import3.js " ,
r #"
( async ( ) = > {
let mod = await import ( " ./b.js " ) ;
if ( mod . b ( ) ! = = 'b' ) {
throw Error ( " bad " ) ;
}
} ) ( ) ;
" #,
)
. unwrap ( ) ;
// First poll runs `prepare_load` hook.
2021-05-26 15:07:12 -04:00
let _ = runtime . poll_event_loop ( cx , false ) ;
2021-05-19 14:53:43 -04:00
assert_eq! ( prepare_load_count . load ( Ordering ::Relaxed ) , 1 ) ;
// Second poll triggers error
2021-05-26 15:07:12 -04:00
let _ = runtime . poll_event_loop ( cx , false ) ;
2023-05-06 10:01:05 -04:00
Poll ::Ready ( ( ) )
2021-05-19 14:53:43 -04:00
} )
2023-05-06 10:01:05 -04:00
. await ;
2021-05-19 14:53:43 -04:00
}
2021-06-28 21:03:02 -04:00
// Regression test for https://github.com/denoland/deno/issues/3736.
#[ test ]
fn dyn_concurrent_circular_import ( ) {
#[ derive(Clone, Default) ]
struct DynImportCircularLoader {
pub resolve_count : Arc < AtomicUsize > ,
pub load_count : Arc < AtomicUsize > ,
}
impl ModuleLoader for DynImportCircularLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-06-28 21:03:02 -04:00
self . resolve_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-12-15 13:22:36 -05:00
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
2021-06-28 21:03:02 -04:00
Ok ( s )
}
fn load (
& self ,
specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-06-28 21:03:02 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
self . load_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
let filename = PathBuf ::from ( specifier . to_string ( ) )
. file_name ( )
. unwrap ( )
. to_string_lossy ( )
. to_string ( ) ;
let code = match filename . as_str ( ) {
" a.js " = > " import './b.js'; " ,
" b.js " = > " import './c.js'; \n import './a.js'; " ,
" c.js " = > " import './d.js'; " ,
" d.js " = > " // pass " ,
_ = > unreachable! ( ) ,
} ;
2023-04-04 08:46:31 -04:00
let info = ModuleSource ::for_test ( code , specifier ) ;
2021-06-28 21:03:02 -04:00
async move { Ok ( info ) } . boxed ( )
}
}
let loader = Rc ::new ( DynImportCircularLoader ::default ( ) ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
runtime
2023-04-04 08:46:31 -04:00
. execute_script_static (
2021-06-28 21:03:02 -04:00
" file:///entry.js " ,
" import('./b.js'); \n import('./a.js'); " ,
)
. unwrap ( ) ;
let result = futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) ;
assert! ( result . is_ok ( ) ) ;
}
2019-03-26 11:56:34 -04:00
#[ test ]
fn test_circular_load ( ) {
2020-01-08 09:06:04 -05:00
let loader = MockLoader ::new ( ) ;
let loads = loader . loads . clone ( ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2019-08-07 12:55:39 -04:00
2020-01-08 09:06:04 -05:00
let fut = async move {
2021-12-15 13:22:36 -05:00
let spec = resolve_url ( " file:///circular1.js " ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
let result = runtime . load_main_module ( & spec , None ) . await ;
2020-01-08 09:06:04 -05:00
assert! ( result . is_ok ( ) ) ;
let circular1_id = result . unwrap ( ) ;
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-07-30 07:36:43 -04:00
let _ = runtime . mod_evaluate ( circular1_id ) ;
2021-05-26 15:07:12 -04:00
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
2019-08-07 12:55:39 -04:00
2021-07-06 23:48:01 -04:00
let l = loads . lock ( ) ;
2020-01-08 09:06:04 -05:00
assert_eq! (
l . to_vec ( ) ,
vec! [
" file:///circular1.js " ,
" file:///circular2.js " ,
" file:///circular3.js "
]
) ;
2019-08-07 12:55:39 -04:00
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) ;
2021-05-19 14:53:43 -04:00
let modules = module_map_rc . borrow ( ) ;
2019-08-07 12:55:39 -04:00
2021-12-15 13:22:36 -05:00
assert_eq! (
2022-05-07 12:46:35 -04:00
modules
. get_id ( " file:///circular1.js " , AssertedModuleType ::JavaScriptOrWasm ) ,
2021-12-15 13:22:36 -05:00
Some ( circular1_id )
) ;
let circular2_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///circular2.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
2019-08-07 12:55:39 -04:00
2020-01-08 09:06:04 -05:00
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( circular1_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///circular2.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ] )
2020-01-08 09:06:04 -05:00
) ;
2019-08-07 12:55:39 -04:00
2020-01-08 09:06:04 -05:00
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( circular2_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///circular3.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ] )
2020-01-08 09:06:04 -05:00
) ;
2021-12-15 13:22:36 -05:00
assert! ( modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///circular3.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. is_some ( ) ) ;
let circular3_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///circular3.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
2020-01-08 09:06:04 -05:00
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( circular3_id ) ,
2020-01-08 09:06:04 -05:00
Some ( & vec! [
2021-12-15 13:22:36 -05:00
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///circular1.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ,
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///circular2.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
}
2020-01-08 09:06:04 -05:00
] )
) ;
}
2020-02-03 18:08:44 -05:00
. boxed_local ( ) ;
2020-01-08 09:06:04 -05:00
futures ::executor ::block_on ( fut ) ;
2019-04-18 21:33:50 -04:00
}
#[ test ]
fn test_redirect_load ( ) {
2020-01-08 09:06:04 -05:00
let loader = MockLoader ::new ( ) ;
let loads = loader . loads . clone ( ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2020-01-08 09:06:04 -05:00
2022-05-07 12:46:35 -04:00
let fut = async move {
let spec = resolve_url ( " file:///redirect1.js " ) . unwrap ( ) ;
let result = runtime . load_main_module ( & spec , None ) . await ;
assert! ( result . is_ok ( ) ) ;
let redirect1_id = result . unwrap ( ) ;
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2022-05-07 12:46:35 -04:00
let _ = runtime . mod_evaluate ( redirect1_id ) ;
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
let l = loads . lock ( ) ;
assert_eq! (
l . to_vec ( ) ,
vec! [
" file:///redirect1.js " ,
" file:///redirect2.js " ,
" file:///dir/redirect3.js "
]
) ;
2019-08-07 12:55:39 -04:00
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) ;
2022-05-07 12:46:35 -04:00
let modules = module_map_rc . borrow ( ) ;
2019-08-07 12:55:39 -04:00
2022-05-07 12:46:35 -04:00
assert_eq! (
modules
. get_id ( " file:///redirect1.js " , AssertedModuleType ::JavaScriptOrWasm ) ,
Some ( redirect1_id )
) ;
2019-08-07 12:55:39 -04:00
2022-05-07 12:46:35 -04:00
let redirect2_id = modules
. get_id (
" file:///dir/redirect2.js " ,
AssertedModuleType ::JavaScriptOrWasm ,
)
. unwrap ( ) ;
assert! ( modules . is_alias (
" file:///redirect2.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ) ;
assert! ( ! modules . is_alias (
" file:///dir/redirect2.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ) ;
assert_eq! (
modules
. get_id ( " file:///redirect2.js " , AssertedModuleType ::JavaScriptOrWasm ) ,
Some ( redirect2_id )
) ;
2019-08-07 12:55:39 -04:00
2022-05-07 12:46:35 -04:00
let redirect3_id = modules
. get_id ( " file:///redirect3.js " , AssertedModuleType ::JavaScriptOrWasm )
. unwrap ( ) ;
assert! ( modules . is_alias (
" file:///dir/redirect3.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ) ;
assert! ( ! modules . is_alias (
" file:///redirect3.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ) ;
assert_eq! (
modules . get_id (
" file:///dir/redirect3.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ,
Some ( redirect3_id )
) ;
}
. boxed_local ( ) ;
2020-01-08 09:06:04 -05:00
futures ::executor ::block_on ( fut ) ;
2019-03-26 11:56:34 -04:00
}
2019-04-16 15:13:42 -04:00
2023-05-06 10:01:05 -04:00
#[ tokio::test ]
async fn slow_never_ready_modules ( ) {
2023-02-06 08:49:26 -05:00
let loader = MockLoader ::new ( ) ;
let loads = loader . loads . clone ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2023-05-06 10:01:05 -04:00
poll_fn ( move | cx | {
2021-12-15 13:22:36 -05:00
let spec = resolve_url ( " file:///main.js " ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
let mut recursive_load =
runtime . load_main_module ( & spec , None ) . boxed_local ( ) ;
2019-04-16 15:13:42 -04:00
2021-12-04 08:19:06 -05:00
let result = recursive_load . poll_unpin ( cx ) ;
2019-11-16 19:17:47 -05:00
assert! ( result . is_pending ( ) ) ;
2019-08-07 12:55:39 -04:00
// TODO(ry) Arguably the first time we poll only the following modules
// should be loaded:
// "file:///main.js",
// "file:///never_ready.js",
// "file:///slow.js"
// But due to current task notification in DelayedSourceCodeFuture they
2023-05-06 10:01:05 -04:00
// all get loaded in a single poll.
2019-08-07 12:55:39 -04:00
for _ in 0 .. 10 {
2021-12-04 08:19:06 -05:00
let result = recursive_load . poll_unpin ( cx ) ;
2019-11-16 19:17:47 -05:00
assert! ( result . is_pending ( ) ) ;
2021-07-06 23:48:01 -04:00
let l = loads . lock ( ) ;
2019-08-07 12:55:39 -04:00
assert_eq! (
l . to_vec ( ) ,
vec! [
" file:///main.js " ,
" file:///never_ready.js " ,
" file:///slow.js " ,
" file:///a.js " ,
" file:///b.js " ,
" file:///c.js " ,
" file:///d.js "
]
) ;
}
2023-05-06 10:01:05 -04:00
Poll ::Ready ( ( ) )
2019-08-07 12:55:39 -04:00
} )
2023-05-06 10:01:05 -04:00
. await ;
2019-04-16 15:13:42 -04:00
}
2023-05-06 10:01:05 -04:00
#[ tokio::test ]
async fn loader_disappears_after_error ( ) {
2023-02-06 08:49:26 -05:00
let loader = MockLoader ::new ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2023-05-06 10:01:05 -04:00
let spec = resolve_url ( " file:///bad_import.js " ) . unwrap ( ) ;
let result = runtime . load_main_module ( & spec , None ) . await ;
let err = result . unwrap_err ( ) ;
assert_eq! (
err . downcast_ref ::< MockError > ( ) . unwrap ( ) ,
& MockError ::ResolveErr
) ;
2019-04-16 15:13:42 -04:00
}
2019-04-19 11:18:46 -04:00
2019-10-19 17:19:19 -04:00
#[ test ]
fn recursive_load_main_with_code ( ) {
2023-04-04 08:46:31 -04:00
const MAIN_WITH_CODE_SRC : FastString = ascii_str! (
r #"
2021-12-21 09:53:46 -05:00
import { b } from " /b.js " ;
import { c } from " /c.js " ;
if ( b ( ) ! = 'b' ) throw Error ( ) ;
if ( c ( ) ! = 'c' ) throw Error ( ) ;
if ( ! import . meta . main ) throw Error ( ) ;
if ( import . meta . url ! = ' file :///main_with_code.js') throw Error();
2023-04-04 08:46:31 -04:00
" #
) ;
2021-12-21 09:53:46 -05:00
2020-01-08 09:06:04 -05:00
let loader = MockLoader ::new ( ) ;
let loads = loader . loads . clone ( ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
2020-01-08 09:06:04 -05:00
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
2021-12-15 13:22:36 -05:00
let spec = resolve_url ( " file:///main_with_code.js " ) . unwrap ( ) ;
2020-09-06 15:44:29 -04:00
let main_id_fut = runtime
2023-04-04 08:46:31 -04:00
. load_main_module ( & spec , Some ( MAIN_WITH_CODE_SRC ) )
2020-02-03 18:08:44 -05:00
. boxed_local ( ) ;
2021-12-21 09:53:46 -05:00
let main_id = futures ::executor ::block_on ( main_id_fut ) . unwrap ( ) ;
2020-12-20 09:14:19 -05:00
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-07-30 07:36:43 -04:00
let _ = runtime . mod_evaluate ( main_id ) ;
2021-05-26 15:07:12 -04:00
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
2020-01-08 09:06:04 -05:00
2021-07-06 23:48:01 -04:00
let l = loads . lock ( ) ;
2020-01-08 09:06:04 -05:00
assert_eq! (
l . to_vec ( ) ,
vec! [ " file:///b.js " , " file:///c.js " , " file:///d.js " ]
) ;
2019-10-19 17:19:19 -04:00
2023-05-28 15:13:53 -04:00
let module_map_rc = runtime . module_map ( ) ;
2021-05-19 14:53:43 -04:00
let modules = module_map_rc . borrow ( ) ;
2019-10-19 17:19:19 -04:00
2021-12-15 13:22:36 -05:00
assert_eq! (
2022-05-07 12:46:35 -04:00
modules . get_id (
" file:///main_with_code.js " ,
AssertedModuleType ::JavaScriptOrWasm
) ,
2021-12-15 13:22:36 -05:00
Some ( main_id )
) ;
let b_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///b.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
let c_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///c.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
let d_id = modules
2022-05-07 12:46:35 -04:00
. get_id ( " file:///d.js " , AssertedModuleType ::JavaScriptOrWasm )
2021-12-15 13:22:36 -05:00
. unwrap ( ) ;
2019-10-19 17:19:19 -04:00
2020-01-08 09:06:04 -05:00
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( main_id ) ,
2020-01-25 12:53:16 -05:00
Some ( & vec! [
2021-12-15 13:22:36 -05:00
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///b.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ,
ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///c.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
}
2020-01-25 12:53:16 -05:00
] )
) ;
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( b_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///c.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ] )
2020-01-25 12:53:16 -05:00
) ;
assert_eq! (
2021-12-15 13:22:36 -05:00
modules . get_requested_modules ( c_id ) ,
Some ( & vec! [ ModuleRequest {
2023-02-21 15:12:22 -05:00
specifier : " file:///d.js " . to_string ( ) ,
2022-05-07 12:46:35 -04:00
asserted_module_type : AssertedModuleType ::JavaScriptOrWasm ,
2021-12-15 13:22:36 -05:00
} ] )
2020-01-08 09:06:04 -05:00
) ;
2021-12-15 13:22:36 -05:00
assert_eq! ( modules . get_requested_modules ( d_id ) , Some ( & vec! [ ] ) ) ;
2019-10-19 17:19:19 -04:00
}
2021-09-17 21:44:53 -04:00
#[ test ]
fn main_and_side_module ( ) {
struct ModsLoader { }
2021-12-15 13:22:36 -05:00
let main_specifier = resolve_url ( " file:///main_module.js " ) . unwrap ( ) ;
let side_specifier = resolve_url ( " file:///side_module.js " ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
2023-01-10 08:35:44 -05:00
_kind : ResolutionKind ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2021-12-15 13:22:36 -05:00
let s = resolve_import ( specifier , referrer ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
Ok ( s )
}
fn load (
& self ,
module_specifier : & ModuleSpecifier ,
2023-04-04 08:46:31 -04:00
_maybe_referrer : Option < & ModuleSpecifier > ,
2021-09-17 21:44:53 -04:00
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
let module_source = match module_specifier . as_str ( ) {
2023-04-04 08:46:31 -04:00
" file:///main_module.js " = > ModuleSource ::for_test (
" if (!import.meta.main) throw Error(); " ,
" file:///main_module.js " ,
) ,
" file:///side_module.js " = > ModuleSource ::for_test (
" if (import.meta.main) throw Error(); " ,
" file:///side_module.js " ,
) ,
2021-09-17 21:44:53 -04:00
_ = > unreachable! ( ) ,
} ;
2023-04-04 08:46:31 -04:00
async move { Ok ( module_source ) } . boxed ( )
2021-09-17 21:44:53 -04:00
}
}
let loader = Rc ::new ( ModsLoader { } ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
let main_id_fut = runtime
. load_main_module ( & main_specifier , None )
. boxed_local ( ) ;
2021-12-21 09:53:46 -05:00
let main_id = futures ::executor ::block_on ( main_id_fut ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-09-17 21:44:53 -04:00
let _ = runtime . mod_evaluate ( main_id ) ;
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
// Try to add another main module - it should error.
let side_id_fut = runtime
. load_main_module ( & side_specifier , None )
. boxed_local ( ) ;
2021-12-21 09:53:46 -05:00
futures ::executor ::block_on ( side_id_fut ) . unwrap_err ( ) ;
2021-09-17 21:44:53 -04:00
// And now try to load it as a side module
let side_id_fut = runtime
. load_side_module ( & side_specifier , None )
. boxed_local ( ) ;
2021-12-21 09:53:46 -05:00
let side_id = futures ::executor ::block_on ( side_id_fut ) . unwrap ( ) ;
2021-09-17 21:44:53 -04:00
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2021-09-17 21:44:53 -04:00
let _ = runtime . mod_evaluate ( side_id ) ;
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
}
2022-09-06 08:35:04 -04:00
#[ test ]
fn dynamic_imports_snapshot ( ) {
//TODO: Once the issue with the ModuleNamespaceEntryGetter is fixed, we can maintain a reference to the module
// and use it when loading the snapshot
let snapshot = {
2023-04-04 08:46:31 -04:00
const MAIN_WITH_CODE_SRC : FastString = ascii_str! (
r #"
2022-09-06 08:35:04 -04:00
await import ( " ./b.js " ) ;
2023-04-04 08:46:31 -04:00
" #
) ;
2022-09-06 08:35:04 -04:00
let loader = MockLoader ::new ( ) ;
2023-05-31 10:19:06 -04:00
let mut runtime = JsRuntimeForSnapshot ::new (
RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ,
Default ::default ( ) ,
) ;
2022-09-06 08:35:04 -04:00
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
let spec = resolve_url ( " file:///main_with_code.js " ) . unwrap ( ) ;
let main_id_fut = runtime
2023-04-04 08:46:31 -04:00
. load_main_module ( & spec , Some ( MAIN_WITH_CODE_SRC ) )
2022-09-06 08:35:04 -04:00
. boxed_local ( ) ;
let main_id = futures ::executor ::block_on ( main_id_fut ) . unwrap ( ) ;
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2022-09-06 08:35:04 -04:00
let _ = runtime . mod_evaluate ( main_id ) ;
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
runtime . snapshot ( )
} ;
let snapshot = Snapshot ::JustCreated ( snapshot ) ;
let mut runtime2 = JsRuntime ::new ( RuntimeOptions {
startup_snapshot : Some ( snapshot ) ,
.. Default ::default ( )
} ) ;
//Evaluate the snapshot with an empty function
2023-04-04 08:46:31 -04:00
runtime2 . execute_script_static ( " check.js " , " true " ) . unwrap ( ) ;
2022-09-06 08:35:04 -04:00
}
#[ test ]
fn import_meta_snapshot ( ) {
let snapshot = {
2023-04-04 08:46:31 -04:00
const MAIN_WITH_CODE_SRC : ModuleCode = ascii_str! (
r #"
2022-09-06 08:35:04 -04:00
if ( import . meta . url ! = ' file :///main_with_code.js') throw Error();
globalThis . meta = import . meta ;
globalThis . url = import . meta . url ;
2023-04-04 08:46:31 -04:00
" #
) ;
2022-09-06 08:35:04 -04:00
let loader = MockLoader ::new ( ) ;
2023-05-31 10:19:06 -04:00
let mut runtime = JsRuntimeForSnapshot ::new (
RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ,
Default ::default ( ) ,
) ;
2022-09-06 08:35:04 -04:00
// In default resolution code should be empty.
// Instead we explicitly pass in our own code.
// The behavior should be very similar to /a.js.
let spec = resolve_url ( " file:///main_with_code.js " ) . unwrap ( ) ;
let main_id_fut = runtime
2023-04-04 08:46:31 -04:00
. load_main_module ( & spec , Some ( MAIN_WITH_CODE_SRC ) )
2022-09-06 08:35:04 -04:00
. boxed_local ( ) ;
let main_id = futures ::executor ::block_on ( main_id_fut ) . unwrap ( ) ;
2023-01-27 10:43:16 -05:00
#[ allow(clippy::let_underscore_future) ]
2022-09-06 08:35:04 -04:00
let _ = runtime . mod_evaluate ( main_id ) ;
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
runtime . snapshot ( )
} ;
let snapshot = Snapshot ::JustCreated ( snapshot ) ;
let mut runtime2 = JsRuntime ::new ( RuntimeOptions {
startup_snapshot : Some ( snapshot ) ,
.. Default ::default ( )
} ) ;
runtime2
2023-04-04 08:46:31 -04:00
. execute_script_static (
2022-09-06 08:35:04 -04:00
" check.js " ,
" if (globalThis.url !== 'file:///main_with_code.js') throw Error('x') " ,
)
. unwrap ( ) ;
}
2019-03-26 11:56:34 -04:00
}