char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
d71572b
main
1use std:: cell::{ Cell , OnceCell , RefCell }; 2use std:: collections:: HashMap ; 3use std:: rc:: Rc ; 4 5use futures:: future:: LocalBoxFuture ; 6 7use crate :: Error ; 8 9/// Shared module text that retains the original String buffer. 10# [ derive ( Clone , Debug )] 11pub struct ModuleSource ( Rc < String >); 12 13impl From < String > for ModuleSource { 14fn from ( source : String ) ->Self { 15Self ( Rc :: new ( source)) 16} 17} 18 19impl From < & str > for ModuleSource { 20fn from ( source : & str ) ->Self { 21Self :: from ( source. to_owned ()) 22} 23} 24 25impl std:: ops:: Deref for ModuleSource { 26type Target =str ; 27 28fn deref ( & self ) ->& str { 29& self . 0 30} 31} 32 33/// Execution format for a prepared module. 34# [ derive ( Clone , Copy , PartialEq , Eq , Hash , Debug )] 35pub enum ModuleFormat { 36Esm , 37CommonJs , 38Json , 39} 40 41pub trait ModuleLoader { 42fn resolve ( & self , specifier : & str , referrer : & str ) ->Result < String , Error >; 43fn load ( & self , name : & str ) ->Result < ModuleSource , Error >; 44 45/// Format metadata for synchronous `require` and ESM interop. 46/// Defaults to ESM so existing loaders keep working unchanged. 47fn module_format ( & self , _name : & str ) ->Result < ModuleFormat , Error > { 48Ok ( ModuleFormat :: Esm ) 49} 50 51/// Synchronous `require` resolution. Defaults to ESM `resolve`. 52fn require_resolve ( & self , specifier : & str , referrer : & str ) ->Result < String , Error > { 53self . resolve ( specifier, referrer) 54} 55 56/// Statically discovered named exports for a CommonJS module. 57/// Must not evaluate source; defaults to no named exports. 58fn cjs_named_exports ( & self , _name : & str ) ->Result < Vec < String >, Error > { 59Ok ( Vec :: new ()) 60} 61 62/// Acquire a module's static closure before V8 links it. Linking itself never awaits. 63fn prepare ( 64& self , 65specifier : String , 66referrer : String , 67) ->LocalBoxFuture < ' static , Result < String , Error >> { 68let result =if referrer. is_empty () { 69Ok ( specifier) 70} else { 71self . resolve ( & specifier, & referrer) 72}; 73Box :: pin ( futures:: future:: ready ( result)) 74} 75 76/// Called before using a loader-backed module, including cached modules and queued imports. 77fn check_access ( & self , _name : & str ) ->Result <(), Error > { 78Ok (()) 79} 80 81/// Resolve synchronously without acquiring or evaluating the target module. 82fn resolve_import_meta ( & self , specifier : & str , referrer : & str ) ->Result < String , Error > { 83let name =self . resolve ( specifier, referrer) ?; 84if name. starts_with ( "ext:" ) { 85return Err ( Error :: Module ( "Extension modules are private" . into ())); 86} 87Ok ( self . module_url ( & name). unwrap_or ( name)) 88} 89 90/// URL exposed as `import.meta.url`, if this loader has one. 91fn module_url ( & self , _name : & str ) ->Option < String > { 92None 93} 94 95/// Map a generated position to its source; lines and UTF-16 columns are one-based. 96fn map_source_location ( 97& self , 98_name : & str , 99_line : u32 , 100_column : u32 , 101) ->Option <( String , u32 , u32 )> { 102None 103} 104} 105 106pub ( crate ) struct ImportPreparation { 107pub resolver : v8:: Global < v8:: PromiseResolver >, 108pub result : Result < String , Error >, 109} 110 111pub ( crate ) fn prepare_entry ( isolate : & mut v8:: Isolate , name : & str ) ->Result < String , Error > { 112if name. starts_with ( "ext:" ) { 113return Err ( Error :: Module ( "Extension modules are private" . into ())); 114} 115let map = isolate. get_slot ::< Rc < ModuleMap >>(). unwrap (); 116if map. is_builtin ( name) { 117return Ok ( name. to_owned ()); 118} 119match & map. loader { 120Some ( loader) => futures:: executor:: block_on ( loader. prepare ( name. to_owned (), String :: new ())), 121None =>Err ( Error :: Module ( "No module loader configured" . into ())), 122} 123} 124 125type Exports =Vec <( String , v8:: Global < v8:: Value >)>; 126 127mod commonjs; 128 129# [ derive ( Clone , Copy , PartialEq , Eq , Hash , Debug )] 130struct ModuleId ( u32 ); 131 132struct Record { 133name : Rc < str >, 134module : v8:: Global < v8:: Module >, 135builtin : Option < Exports >, 136dependencies : Vec < ModuleId >, 137visited : u32 , 138cjs_exports : Option < Vec < String >>, 139} 140 141struct ModuleMap { 142loader : Option < Rc < dyn ModuleLoader >>, 143records : RefCell < Vec < Record >>, 144index : RefCell < HashMap < Rc < str >, ModuleId >>, 145// Identity hashes collide, so each bucket holds candidates confirmed by Local == Global. 146by_identity : RefCell < HashMap < i32 , Vec < ModuleId >>>, 147epoch : Cell < u32 >, 148main : OnceCell < String >, 149repl_referrer : RefCell < Option < String >>, 150// Per-isolate CommonJS state shared by `require` and ESM wrappers. 151// No global registry: everything lives in the isolate slot. 152cjs_modules : RefCell < HashMap < Rc < str >, v8:: Global < v8:: Object >>>, 153cjs_deps : RefCell < HashMap < Rc < str >, Vec < String >>>, 154json_modules : RefCell < HashMap < Rc < str >, v8:: Global < v8:: Value >>>, 155} 156 157impl ModuleMap { 158fn id_of ( & self , name : & str ) ->Option < ModuleId > { 159self . index . borrow (). get ( name). copied () 160} 161 162fn is_builtin ( & self , name : & str ) ->bool { 163let Some ( id) =self . id_of ( name) else { 164return false ; 165}; 166self . records . borrow ()[ id. 0 as usize ]. builtin . is_some () 167} 168 169fn next_epoch ( & self ) ->u32 { 170let mut epoch =self . epoch . get (). wrapping_add ( 1 ); 171if epoch ==0 { 172for recordin self . records . borrow_mut (). iter_mut () { 173 record. visited =0 ; 174} 175 epoch =1 ; 176} 177self . epoch . set ( epoch); 178 epoch 179} 180 181fn insert ( 182& self , 183name : Rc < str >, 184module : v8:: Global < v8:: Module >, 185builtin : Option < Exports >, 186hash : i32 , 187) ->ModuleId { 188// Only called on cache miss: register() rejects duplicates and load() 189// returns early when the name is already indexed. 190let id =ModuleId ( self . records . borrow (). len () as u32 ); 191self . records . borrow_mut (). push ( Record { 192name : name. clone (), 193 module, 194 builtin, 195dependencies : Vec :: new (), 196visited : 0 , 197cjs_exports : None , 198}); 199self . index . borrow_mut (). insert ( name, id); 200self . by_identity 201. borrow_mut () 202. entry ( hash) 203. or_default () 204. push ( id); 205 id 206} 207} 208 209pub ( crate ) fn clear ( isolate : & mut v8:: Isolate ) { 210 isolate. remove_slot ::< Rc < ModuleMap >>(); 211} 212 213pub ( crate ) fn external_references () ->Vec < v8:: ExternalReference > { 214use v8:: MapFnTo ; 215vec! [ v8:: ExternalReference { 216function : resolve_import_meta. map_fn_to (), 217}] 218} 219 220pub ( crate ) fn has_modules ( isolate : & v8:: Isolate ) ->bool { 221 !isolate 222. get_slot ::< Rc < ModuleMap >>() 223. unwrap () 224. records 225. borrow () 226. is_empty () 227} 228 229pub ( crate ) fn init ( isolate : & mut v8:: Isolate , loader : Option < Rc < dyn ModuleLoader >>) { 230 isolate. set_slot ( Rc :: new ( ModuleMap { 231 loader, 232records : RefCell :: new ( Vec :: new ()), 233index : RefCell :: new ( HashMap :: new ()), 234by_identity : RefCell :: new ( HashMap :: new ()), 235epoch : Cell :: new ( 0 ), 236main : OnceCell :: new (), 237repl_referrer : RefCell :: new ( None ), 238cjs_modules : RefCell :: new ( HashMap :: new ()), 239cjs_deps : RefCell :: new ( HashMap :: new ()), 240json_modules : RefCell :: new ( HashMap :: new ()), 241})); 242 isolate. set_host_import_module_dynamically_callback ( import_dynamic); 243 isolate. set_host_initialize_import_meta_object_callback ( import_meta); 244} 245 246pub ( crate ) fn set_repl_referrer ( scope : & mut v8:: PinScope , referrer : Option < & str >) ->Option <()> { 247let map = scope. get_slot ::< Rc < ModuleMap >>() ?; 248* map. repl_referrer . borrow_mut () = referrer. map ( str:: to_owned); 249Some (()) 250} 251 252pub ( crate ) fn register ( 253scope : & mut v8:: PinScope , 254name : & str , 255exports : v8:: Local < v8:: Object >, 256) ->Option <()> { 257check_guest_name ( scope, name) ?; 258let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 259if map. id_of ( name). is_some () { 260return fail ( scope, format! ( "Module already registered: {name}" )); 261} 262let keys = exports. get_own_property_names ( scope, Default :: default ()) ?; 263let mut names =Vec :: new (); 264let mut values =Exports :: new (); 265for indexin 0 ..keys. length () { 266let key = keys. get_index ( scope, index) ?; 267let name = key. to_rust_string_lossy ( scope); 268let value = exports. get ( scope, key) ?; 269 names. push ( v8:: String :: new ( scope, & name) ?); 270 values. push (( name, v8:: Global :: new ( scope, value))); 271} 272let module_name = v8:: String :: new ( scope, name) ?; 273let module = v8:: Module :: create_synthetic_module ( scope, module_name, & names, evaluate_builtin); 274let hash = module. get_identity_hash (). get (); 275 map. insert ( 276 name. into (), 277 v8:: Global :: new ( scope, module), 278Some ( values), 279 hash, 280); 281Some (()) 282} 283 284fn evaluate_builtin < ' s >( 285context : v8:: Local < ' s , v8:: Context >, 286module : v8:: Local < ' s , v8:: Module >, 287) ->Option < v8:: Local < ' s , v8:: Value >> { 288// SAFETY: V8 invokes synthetic module evaluation with this context entered. 289 v8:: callback_scope!( unsafe scope, context); 290let id =module_id ( scope, module); 291let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 292// Only synthetic builtin modules carry this evaluation callback, so the 293// record always has exports; panic like the old builtins-map indexing did. 294let records = map. records . borrow (); 295let exports = records[ id. 0 as usize ]. builtin . as_ref (). unwrap (); 296for ( key, value) in exports{ 297let key = v8:: String :: new ( scope, key) ?; 298let value = v8:: Local :: new ( scope, value); 299 module. set_synthetic_module_export ( scope, key, value) ?; 300} 301let resolver = v8:: PromiseResolver :: new ( scope) ?; 302 resolver. resolve ( scope, v8:: undefined ( scope). into ()) ?; 303Some ( resolver. get_promise ( scope). into ()) 304} 305 306pub ( crate ) fn map_source_location_op ( 307scope : & mut v8:: PinScope , 308args : v8:: FunctionCallbackArguments , 309mut result : v8:: ReturnValue , 310) { 311let name = args. get ( 0 ). to_rust_string_lossy ( scope); 312let Some ( line) = args. get ( 1 ). uint32_value ( scope) else { 313return ; 314}; 315let Some ( column) = args. get ( 2 ). uint32_value ( scope) else { 316return ; 317}; 318if let Some ( location) =map_source_location ( scope, & name, line, column) { 319 result. set ( serde_v8:: to_v8 ( scope, location). unwrap ()); 320} 321} 322 323pub ( crate ) fn map_source_location ( 324scope : & mut v8:: PinScope , 325name : & str , 326line : u32 , 327column : u32 , 328) ->Option <( String , u32 , u32 )> { 329 scope 330. get_slot ::< Rc < ModuleMap >>() ? 331. loader 332. as_ref () ? 333. map_source_location ( name, line, column) 334} 335 336fn fail_syntax < T >( scope : & mut v8:: PinScope , message : impl AsRef < str >) ->Option < T > { 337let message = v8:: String :: new ( scope, message. as_ref ()). unwrap (); 338let error = v8:: Exception :: syntax_error ( scope, message); 339 scope. throw_exception ( error); 340None 341} 342 343fn fail < T >( scope : & mut v8:: PinScope , message : impl AsRef < str >) ->Option < T > { 344let message = v8:: String :: new ( scope, message. as_ref ()). unwrap (); 345let error = v8:: Exception :: error ( scope, message); 346 scope. throw_exception ( error); 347None 348} 349 350fn check_guest_name ( scope : & mut v8:: PinScope , name : & str ) ->Option <()> { 351if name. starts_with ( "ext:" ) { 352fail ( scope, "Extension modules are private" ) 353} else { 354Some (()) 355} 356} 357 358fn load < ' s >( scope : & mut v8:: PinScope < ' s , ' _ >, name : & str ) ->Option < v8:: Local < ' s , v8:: Module >> { 359check_guest_name ( scope, name) ?; 360let Some ( map) = scope. get_slot ::< Rc < ModuleMap >>(). cloned () else { 361return fail ( scope, "No module loader configured" ); 362}; 363let is_builtin = map. is_builtin ( name); 364if !is_builtin 365 &&let Some ( loader) =& map. loader 366 &&let Err ( error) = loader. check_access ( name) 367{ 368return match error{ 369Error :: Syntax ( message) =>fail_syntax ( scope, message), 370 error =>fail ( scope, error. to_string ()), 371}; 372} 373if let Some ( id) = map. id_of ( name) { 374// Local::new only creates a handle; no loader or V8 callback runs 375// while borrowed, so holding the borrow avoids a Global clone. 376let records = map. records . borrow (); 377return Some ( v8:: Local :: new ( scope, & records[ id. 0 as usize ]. module )); 378} 379 380let Some ( loader) = map. loader . clone () else { 381return fail ( scope, "No module loader configured" ); 382}; 383let format =match loader. module_format ( name) { 384Ok ( format) => format, 385Err ( Error :: Syntax ( message)) =>return fail_syntax ( scope, message), 386Err ( error) =>return fail ( scope, error. to_string ()), 387}; 388match format{ 389ModuleFormat :: Esm =>{ 390let source =match loader. load ( name) { 391Ok ( source) => source, 392Err ( Error :: Syntax ( message)) =>return fail_syntax ( scope, message), 393Err ( error) =>return fail ( scope, error. to_string ()), 394}; 395let mut source =crate :: source ( scope, name, & source, true , None ); 396let module = v8:: script_compiler:: compile_module ( scope, & mut source) ?; 397let hash = module. get_identity_hash (). get (); 398 map. insert ( name. into (), v8:: Global :: new ( scope, module), None , hash); 399Some ( module) 400} 401ModuleFormat :: CommonJs => commonjs:: load_synthetic ( scope, & map, & loader, name), 402ModuleFormat :: Json =>fail ( 403 scope, 404format! ( "JSON modules are not supported as ESM (use require() instead): {name}" ), 405), 406} 407} 408 409fn resolve < ' s >( 410context : v8:: Local < ' s , v8:: Context >, 411specifier : v8:: Local < ' s , v8:: String >, 412attributes : v8:: Local < ' s , v8:: FixedArray >, 413referrer : v8:: Local < ' s , v8:: Module >, 414) ->Option < v8:: Local < ' s , v8:: Module >> { 415// SAFETY: V8 invokes this callback with the supplied context entered. 416 v8:: callback_scope!( unsafe scope, context); 417if attributes. length () !=0 { 418return fail ( scope, "Import attributes are not supported yet" ); 419} 420let referrer_id =module_id ( scope, referrer); 421let referrer = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). records . borrow () 422[ referrer_id. 0 as usize ] 423. name 424. to_string (); 425let specifier = specifier. to_rust_string_lossy ( scope); 426let name =resolve_name ( scope, & specifier, & referrer) ?; 427let module =load ( scope, & name) ?; 428let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 429// load() inserts on cache miss, so the name is always indexed here. 430let dependency = map. id_of ( & name). unwrap (); 431 map. records . borrow_mut ()[ referrer_id. 0 as usize ] 432. dependencies 433. push ( dependency); 434Some ( module) 435} 436 437fn evaluate < ' s >( 438scope : & mut v8:: PinScope < ' s , ' _ >, 439name : & str , 440) ->Option <( v8:: Local < ' s , v8:: Module >, v8:: Local < ' s , v8:: Promise >)> { 441let module =load ( scope, name) ?; 442let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 443let root = map. id_of ( name). unwrap (); 444if module. get_status () == v8:: ModuleStatus :: Uninstantiated { 445// Clear failed attempts before linking, not on repeated resolution within a diamond or cycle. 446// Local::new and get_status only inspect handles; no loader or V8 447// callback runs while borrowed, so no Global clones are needed. 448let uninstantiated ={ 449let records = map. records . borrow (); 450 records 451. iter () 452. enumerate () 453. filter_map ( |( index, record) |{ 454( v8:: Local :: new ( scope, & record. module ). get_status () 455 == v8:: ModuleStatus :: Uninstantiated ) 456. then_some ( ModuleId ( indexas u32 )) 457}) 458. collect ::< Vec < _ >>() 459}; 460if !uninstantiated. is_empty () { 461let mut records = map. records . borrow_mut (); 462for idin uninstantiated{ 463 records[ id. 0 as usize ]. dependencies . clear (); 464} 465} 466if module. instantiate_module ( scope, resolve) !=Some ( true ) { 467return None ; 468} 469} 470// Check the recorded links, not a fresh resolution that could identify different modules. 471let epoch = map. next_epoch (); 472let mut pending =vec! [ root]; 473while let Some ( id) = pending. pop () { 474let ( status, is_builtin, dependencies, record_name) ={ 475let mut records = map. records . borrow_mut (); 476let record =& mut records[ id. 0 as usize ]; 477if record. visited == epoch{ 478continue ; 479} 480 record. visited = epoch; 481// Local::new and get_status only inspect handles; the borrow is 482// dropped before the loader check below, which may reenter. 483let status = v8:: Local :: new ( scope, & record. module ). get_status (); 484( 485 status, 486 record. builtin . is_some (), 487 record. dependencies . clone (), 488 record. name . clone (), 489) 490}; 491// Cached CJS closures must not bypass a later pledge even when the 492// synthetic wrapper is already evaluated: check transitively. 493// Traverse ESM dependencies even for evaluated modules so an ESM 494// intermediary cannot hide a revoked CJS leaf. 495if !is_builtin 496 &&let Some ( loader) =& map. loader 497 && commonjs:: check_closure ( scope, & map, loader, & record_name). is_none () 498{ 499return None ; 500} 501 pending. extend ( dependencies); 502if status != v8:: ModuleStatus :: Instantiated { 503continue ; 504} 505if !is_builtin 506 &&let Some ( loader) =& map. loader 507 &&let Err ( error) = loader. check_access ( & record_name) 508{ 509return match error{ 510Error :: Syntax ( message) =>fail_syntax ( scope, message), 511 error =>fail ( scope, error. to_string ()), 512}; 513} 514} 515// V8 caches the cycle root's evaluation promise, including pending top-level await. 516let value = module. evaluate ( scope) ?; 517let promise = v8:: Local ::< v8:: Promise >:: try_from ( value). unwrap (); 518 promise. mark_as_handled (); 519Some (( module, promise)) 520} 521 522pub ( crate ) fn execute < ' s >( 523scope : & mut v8:: PinScope < ' s , ' _ >, 524name : & str , 525) ->Option < v8:: Local < ' s , v8:: Promise >> { 526let Some ( map) = scope. get_slot ::< Rc < ModuleMap >>(). cloned () else { 527return fail ( scope, "No module loader configured" ); 528}; 529check_guest_name ( scope, name) ?; 530 map. main . get_or_init ( || name. to_owned ()); 531evaluate ( scope, name). map ( |( _, promise) | promise) 532} 533 534pub ( crate ) fn namespace < ' s >( 535scope : & mut v8:: PinScope < ' s , ' _ >, 536name : & str , 537) ->Option < v8:: Local < ' s , v8:: Value >> { 538let map = scope. get_slot ::< Rc < ModuleMap >>() ?; 539let id = map. id_of ( name) ?; 540let records = map. records . borrow (); 541// Local::new only creates a handle; no loader or V8 callback runs while borrowed. 542Some ( v8:: Local :: new ( scope, & records[ id. 0 as usize ]. module ). get_module_namespace ()) 543} 544 545fn module_id ( scope : & mut v8:: PinScope , module : v8:: Local < v8:: Module >) ->ModuleId { 546let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 547let hash = module. get_identity_hash (). get (); 548let by_identity = map. by_identity . borrow (); 549let records = map. records . borrow (); 550// Local::new and handle equality only inspect handles; no loader or V8 551// callback runs while borrowed, so the bucket and Globals need no clones. 552if let Some ( candidates) = by_identity. get ( & hash) { 553for idin candidates{ 554if v8:: Local :: new ( scope, & records[ id. 0 as usize ]. module ) == module{ 555return * id; 556} 557} 558} 559unreachable! ( "module not in map" ) 560} 561 562fn module_name ( scope : & mut v8:: PinScope , module : v8:: Local < v8:: Module >) ->String { 563let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 564let id =module_id ( scope, module); 565 map. records . borrow ()[ id. 0 as usize ]. name . to_string () 566} 567 568fn resolve_name ( scope : & mut v8:: PinScope , specifier : & str , referrer : & str ) ->Option < String > { 569check_guest_name ( scope, specifier) ?; 570let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 571if map. is_builtin ( specifier) { 572return Some ( specifier. to_owned ()); 573} 574let Some ( loader) = map. loader . clone () else { 575return fail ( scope, "No module loader configured" ); 576}; 577match loader. resolve ( specifier, referrer) { 578Ok ( name) =>{ 579check_guest_name ( scope, & name) ?; 580Some ( name) 581} 582Err ( error) =>fail ( scope, error. to_string ()), 583} 584} 585 586unsafe extern "C" fn import_meta ( 587context : v8:: Local < v8:: Context >, 588module : v8:: Local < v8:: Module >, 589meta : v8:: Local < v8:: Object >, 590) { 591// SAFETY: V8 supplies the module's entered context. 592 v8:: callback_scope!( unsafe scope, context); 593let extension =crate :: extensions:: module_name ( scope, module); 594let name = extension 595. clone () 596. unwrap_or_else ( ||module_name ( scope, module)); 597let referrer = v8:: String :: new ( scope, & name). unwrap (); 598let resolve = v8:: Function :: builder ( resolve_import_meta) 599. data ( referrer. into ()) 600. length ( 1 ) 601. constructor_behavior ( v8:: ConstructorBehavior :: Throw ) 602. build ( scope) 603. unwrap (); 604let function_name = v8:: String :: new ( scope, "resolve" ). unwrap (); 605 resolve. set_name ( function_name); 606 meta. create_data_property ( scope, function_name. into (), resolve. into ()) 607. unwrap (); 608if extension. is_some () { 609for ( key, value) in [ 610( "main" , v8:: Boolean :: new ( scope, false ). into ()), 611( "url" , v8:: String :: new ( scope, & name). unwrap (). into ()), 612] { 613let key = v8:: String :: new ( scope, key). unwrap (); 614 meta. create_data_property ( scope, key. into (), value). unwrap (); 615} 616return ; 617} 618let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 619let main = map. main . get () ==Some ( & name); 620let key = v8:: String :: new ( scope, "main" ). unwrap (); 621let value = v8:: Boolean :: new ( scope, main); 622 meta. create_data_property ( scope, key. into (), value. into ()) 623. unwrap (); 624if let Some ( url) = map 625. loader 626. as_ref () 627. and_then ( |loader| loader. module_url ( & name)) 628{ 629let key = v8:: String :: new ( scope, "url" ). unwrap (); 630let value = v8:: String :: new ( scope, & url). unwrap (); 631 meta. create_data_property ( scope, key. into (), value. into ()) 632. unwrap (); 633if let Ok ( url) = url:: Url :: parse ( & url) 634 &&let Ok ( path) = url. to_file_path () 635 &&let Some ( filename) = path. to_str () 636 &&let Some ( dirname) = path. parent (). and_then ( std:: path:: Path :: to_str) 637{ 638for ( key, value) in [( "filename" , filename), ( "dirname" , dirname)] { 639let key = v8:: String :: new ( scope, key). unwrap (); 640let value = v8:: String :: new ( scope, value). unwrap (); 641 meta. create_data_property ( scope, key. into (), value. into ()) 642. unwrap (); 643} 644} 645} 646} 647 648fn resolve_import_meta ( 649scope : & mut v8:: PinScope , 650args : v8:: FunctionCallbackArguments , 651mut result : v8:: ReturnValue , 652) { 653let Some ( specifier) = args. get ( 0 ). to_string ( scope) else { 654return ; 655}; 656let specifier = specifier. to_rust_string_lossy ( scope); 657let referrer = args. data (). to_rust_string_lossy ( scope); 658if referrer. starts_with ( "ext:" ) { 659if let Some ( name) =crate :: extensions:: resolve_name ( scope, & specifier, & referrer) 660 &&let Some ( value) = v8:: String :: new ( scope, & name) 661{ 662 result. set ( value. into ()); 663} 664return ; 665} 666let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 667let is_builtin = map. is_builtin ( & specifier); 668let loader = map. loader . clone (); 669let resolved =if specifier. starts_with ( "ext:" ) { 670Err ( Error :: Module ( "Extension modules are private" . into ())) 671} else if is_builtin{ 672Ok ( specifier) 673} else if let Some ( loader) =& loader{ 674 loader. resolve_import_meta ( & specifier, & referrer) 675} else { 676Err ( Error :: Module ( "No module loader configured" . into ())) 677}; 678match resolved{ 679Ok ( name) if !name. starts_with ( "ext:" ) =>{ 680if let Some ( value) = v8:: String :: new ( scope, & name) { 681 result. set ( value. into ()); 682} 683} 684 other =>{ 685let message =match other{ 686Err ( error) => error. to_string (), 687 _ =>"Extension modules are private" . into (), 688}; 689let message = v8:: String :: new ( scope, & message). unwrap (); 690let error = v8:: Exception :: type_error ( scope, message); 691 scope. throw_exception ( error); 692} 693} 694} 695 696fn import_dynamic < ' s >( 697scope : & mut v8:: PinScope < ' s , ' _ >, 698options : v8:: Local < ' s , v8:: Data >, 699referrer : v8:: Local < ' s , v8:: Value >, 700specifier : v8:: Local < ' s , v8:: String >, 701attributes : v8:: Local < ' s , v8:: FixedArray >, 702) ->Option < v8:: Local < ' s , v8:: Promise >> { 703let resolver = v8:: PromiseResolver :: new ( scope) ?; 704let promise = resolver. get_promise ( scope); 705 v8:: tc_scope!( let scope, scope); 706let queued =( ||{ 707let specifier = specifier. to_rust_string_lossy ( scope); 708check_guest_name ( scope, & specifier) ?; 709// eval inherits host options. This marker only restricts imports; it never grants access. 710if crate :: extensions:: dynamic_imports_disabled ( scope, options) { 711return fail ( 712 scope, 713"not yet implemented: dynamic imports from extension modules" , 714); 715} 716if attributes. length () !=0 { 717return fail ( scope, "Import attributes are not supported yet" ); 718} 719let referrer = referrer. to_rust_string_lossy ( scope); 720let map = scope. get_slot ::< Rc < ModuleMap >>(). unwrap (). clone (); 721let referrer =if referrer. is_empty () { 722 map. repl_referrer . borrow (). clone (). unwrap_or ( referrer) 723} else { 724 referrer 725}; 726let is_builtin = map. is_builtin ( & specifier); 727let loader = map. loader . clone (); 728let future =if is_builtin{ 729Box :: pin ( futures:: future:: ready ( Ok ( specifier))) 730as LocalBoxFuture < ' static , Result < String , Error >> 731} else { 732let Some ( loader) = loaderelse { 733return fail ( scope, "No module loader configured" ); 734}; 735 loader. prepare ( specifier, referrer) 736}; 737let resolver = v8:: Global :: new ( scope, resolver); 738crate :: op:: enqueue_import ( 739 scope, 740Box :: pin ( async move { ImportPreparation { resolver, result : future. await } }), 741); 742Some (()) 743})(); 744if queued. is_none () { 745let error = scope. exception () ?; 746 scope. reset (); 747 resolver. reject ( scope, error); 748} 749Some ( promise) 750} 751 752pub ( crate ) fn import_prepared ( scope : & mut v8:: PinScope , preparation : ImportPreparation ) { 753let resolver = v8:: Local :: new ( scope, preparation. resolver ); 754 v8:: tc_scope!( let scope, scope); 755let queued =( ||{ 756let name =match preparation. result { 757Ok ( name) => name, 758Err ( Error :: Syntax ( message)) =>{ 759let message = v8:: String :: new ( scope, & message) ?; 760let error = v8:: Exception :: syntax_error ( scope, message); 761 scope. throw_exception ( error); 762return None ; 763} 764Err ( error) =>return fail ( scope, error. to_string ()), 765}; 766check_guest_name ( scope, & name) ?; 767let name = v8:: String :: new ( scope, & name) ?; 768let data = v8:: Array :: new_with_elements ( scope, & [ name. into (), resolver. into ()]); 769let job = v8:: Function :: builder ( import_job) 770. data ( data. into ()) 771. constructor_behavior ( v8:: ConstructorBehavior :: Throw ) 772. build ( scope) ?; 773// Evaluation must not reenter a synchronously evaluating module (including itself). 774 scope. enqueue_microtask ( job); 775Some (()) 776})(); 777if queued. is_none () 778 &&let Some ( error) = scope. exception () 779{ 780 scope. reset (); 781 resolver. reject ( scope, error); 782} 783} 784 785fn import_job < ' s >( 786scope : & mut v8:: PinScope < ' s , ' _ >, 787args : v8:: FunctionCallbackArguments < ' s >, 788_result : v8:: ReturnValue , 789) { 790let data = v8:: Local ::< v8:: Array >:: try_from ( args. data ()). unwrap (); 791let name = data 792. get_index ( scope, 0 ) 793. unwrap () 794. to_rust_string_lossy ( scope); 795// SAFETY: Private callback data contains the resolver created by import_dynamic. 796// import_prepared stores it at index 1; guests cannot change that data. 797let resolver =unsafe { 798 v8:: Local ::< v8:: PromiseResolver >:: cast_unchecked ( data. get_index ( scope, 1 ). unwrap ()) 799}; 800 v8:: tc_scope!( let scope, scope); 801let started =( ||{ 802let ( module, evaluation) =evaluate ( scope, & name) ?; 803let namespace = module. get_module_namespace (); 804let data = v8:: Array :: new_with_elements ( scope, & [ resolver. into (), namespace]); 805let fulfilled = v8:: Function :: builder ( import_fulfilled) 806. data ( data. into ()) 807. constructor_behavior ( v8:: ConstructorBehavior :: Throw ) 808. build ( scope) ?; 809let rejected = v8:: Function :: builder ( import_rejected) 810. data ( resolver. into ()) 811. constructor_behavior ( v8:: ConstructorBehavior :: Throw ) 812. build ( scope) ?; 813 evaluation 814. then2 ( scope, fulfilled, rejected) ? 815. mark_as_handled (); 816Some (()) 817})(); 818if started. is_none () 819 &&let Some ( error) = scope. exception () 820{ 821 scope. reset (); 822 resolver. reject ( scope, error); 823} 824} 825 826fn import_fulfilled < ' s >( 827scope : & mut v8:: PinScope < ' s , ' _ >, 828args : v8:: FunctionCallbackArguments < ' s >, 829_result : v8:: ReturnValue , 830) { 831let data = v8:: Local ::< v8:: Array >:: try_from ( args. data ()). unwrap (); 832// SAFETY: Private callback data contains the original import resolver. 833// import_job stores it at index 0; guests cannot change that data. 834let resolver =unsafe { 835 v8:: Local ::< v8:: PromiseResolver >:: cast_unchecked ( data. get_index ( scope, 0 ). unwrap ()) 836}; 837let namespace = data. get_index ( scope, 1 ). unwrap (); 838 resolver. resolve ( scope, namespace); 839} 840 841fn import_rejected < ' s >( 842scope : & mut v8:: PinScope < ' s , ' _ >, 843args : v8:: FunctionCallbackArguments < ' s >, 844_result : v8:: ReturnValue , 845) { 846// SAFETY: Private callback data contains the original import resolver. 847// import_job installs the PromiseResolver directly; guests cannot change that data. 848let resolver =unsafe { v8:: Local ::< v8:: PromiseResolver >:: cast_unchecked ( args. data ()) }; 849 resolver. reject ( scope, args. get ( 0 )); 850}