char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f90c0ce
main
1mod extensions; 2pub mod message; 3mod microtasks; 4mod modules; 5mod op; 6mod repl; 7mod reporting; 8mod snapshot; 9mod stack_trace; 10mod structured_clone; 11mod tasks; 12mod wasm_streaming; 13 14use std:: cell:: RefCell ; 15use std:: collections:: HashMap ; 16use std:: rc:: Rc ; 17use std:: sync:: OnceLock ; 18use std:: task:: Context ; 19 20use serde_json:: json; 21use v8:: inspector:: StringView ; 22 23use serde:: de:: DeserializeOwned ; 24 25pub use modules::{ ModuleFormat , ModuleLoader , ModuleSource }; 26pub use op:: Op ; 27pub use serde_v8; 28pub use snapshot:: Snapshot ; 29pub use v8; 30pub use wasm_streaming::{ WasmStreamingRegistry , WasmStreamingStart , WasmStreamingStartError }; 31 32/// Bootstrap modules with a private graph, separate from guest modules and public builtins. 33pub struct Extension { 34pub name : &' static str , 35/// Extensions that must be present and whose entry points run first. 36pub dependencies : &' static [ &' static str ], 37pub ops : Vec < Op >, 38/// Canonical specifiers and sources, registered before any entry point executes. 39pub modules : &' static [( &' static str , &' static str )], 40/// An embedded module to evaluate before guest execution. 41/// Live setup registered through `ext:core/bootstrap` runs after all entries. 42pub entry_point : Option < &' static str >, 43} 44 45/// A guest module and the embedded source/declaration pair that defines it. 46pub struct PublicModule { 47pub name : &' static str , 48pub module : ( &' static str , &' static str ), 49pub declaration : &' static str , 50} 51 52impl PublicModule { 53pub fn register ( & self , runtime : & mut Runtime ) ->Result <(), Error > { 54 runtime. register_extension_module ( self . name , self . module . 0 ) 55} 56} 57 58/// Embed matching JavaScript and declarations from one build output stem. 59/// Use the row's `module` in `Extension::modules` as well as registering the row. 60# [ macro_export ] 61macro_rules ! public_module{ 62( $name: literal, $extension: literal, $output: literal $(,) ?) =>{ 63 $crate:: PublicModule { 64name : $name, 65module : ( 66concat! ( $extension, "/" , $output, ".js" ), 67include_str! ( concat! ( env! ( "OUT_DIR" ), "/" , $output, ".js" )), 68), 69declaration : include_str! ( concat! ( env! ( "OUT_DIR" ), "/" , $output, ".d.ts" )), 70} 71}; 72} 73 74# [ derive ( Default )] 75pub struct RuntimeOptions { 76pub extensions : Vec < Extension >, 77pub module_loader : Option < Rc < dyn ModuleLoader >>, 78} 79 80# [ derive ( Debug , thiserror :: Error )] 81pub enum Error { 82# [ error ( "{0}" )] 83JavaScript ( String ), 84# [ error ( "Duplicate op: {0}" )] 85DuplicateOp ( &' static str ), 86# [ error ( "{0}" )] 87Module ( String ), 88# [ error ( "SyntaxError: {0}" )] 89Syntax ( String ), 90# [ error ( transparent )] 91Serde ( # [ from ] serde_v8:: Error ), 92# [ error ( transparent )] 93Snapshot ( # [ from ] SnapshotError ), 94} 95 96# [ derive ( Debug , thiserror :: Error )] 97pub enum SnapshotError { 98# [ error ( "Snapshot has no layout matching these extensions" )] 99IncompatibleLayout , 100# [ error ( "Snapshot layouts must be distinct" )] 101DuplicateLayout , 102# [ error ( "Cannot snapshot active runtime state: {0}" )] 103ActiveState ( &' static str ), 104# [ error ( "V8 could not create a snapshot" )] 105CreationFailed , 106# [ error ( "Invalid snapshot artifact: {0}" )] 107InvalidArtifact ( &' static str ), 108# [ error ( "Invalid snapshot metadata: {0}" )] 109Metadata ( # [ from ] serde_json:: Error ), 110# [ error ( "Invalid snapshot data: {0}" )] 111Data ( # [ from ] v8:: DataError ), 112# [ error ( "Live runtimes in this process use a different V8 read-only heap" )] 113ReadOnlyHeapInUse , 114# [ error ( "Runtimes cannot start in a snapshot generator process" )] 115GeneratorProcess , 116# [ error ( "Snapshot generation must initialize V8 before any runtime in its process" )] 117RuntimeProcess , 118} 119 120pub struct Runtime { 121// Global handles must be dropped before their isolate. 122repl : Option < repl:: Inspector >, 123module_evaluation : Option < v8:: Global < v8:: Promise >>, 124context : v8:: Global < v8:: Context >, 125isolate : snapshot:: IsolateOwner , 126} 127 128pub ( crate ) fn source < ' s >( 129scope : & mut v8:: PinScope < ' s , ' _ >, 130name : & str , 131code : & str , 132is_module : bool , 133host_options : Option < v8:: Local < ' s , v8:: Data >>, 134) -> v8:: script_compiler:: Source { 135let resource = v8:: String :: new ( scope, name). unwrap (); 136let origin = v8:: ScriptOrigin :: new ( 137 scope, 138 resource. into (), 1390 , 1400 , 141false , 142 -1 , 143None , 144false , 145false , 146 is_module, 147 host_options, 148); 149let code = v8:: String :: new ( scope, code). unwrap (); 150 v8:: script_compiler:: Source :: new ( code, Some ( & origin)) 151} 152 153# [ derive ( Clone , Copy , PartialEq , Eq )] 154pub ( crate ) enum Process { 155Runtime , 156Generator , 157} 158 159/// The first caller decides how this process initializes V8. 160pub ( crate ) fn initialize_v8 ( process : Process ) ->Process { 161static PROCESS : OnceLock < Process > =OnceLock :: new (); 162* PROCESS . get_or_init ( ||{ 163// Snapshot creators must reuse V8's read-only heap rather than extend it. 164let mut flags =String :: from ( "--no-extensible-ro-snapshot" ); 165if process ==Process :: Generator { 166// Seeds identity hashes and V8's mmap hints, which shape artifact bytes. 167 flags. push_str ( " --random-seed=1534" ); 168} 169 v8:: V8 :: set_flags_from_string ( & flags); 170let platform = v8:: new_default_platform ( 0 , false ). make_shared (); 171 v8:: V8 :: initialize_platform ( platform); 172 v8:: V8 :: initialize (); 173 process 174}) 175} 176 177/// Initialize V8 for reproducible snapshot generation. 178/// 179/// This must run before any other avril-core use, in a dedicated process such as a 180/// build script: it seeds V8's process-wide random number generators, so runtimes 181/// are refused afterwards with [`SnapshotError::GeneratorProcess`]. 182pub fn initialize_snapshot_generator () ->Result <(), SnapshotError > { 183match initialize_v8 ( Process :: Generator ) { 184Process :: Generator =>Ok (()), 185Process :: Runtime =>Err ( SnapshotError :: RuntimeProcess ), 186} 187} 188 189fn runtime_process () ->Result <(), SnapshotError > { 190match initialize_v8 ( Process :: Runtime ) { 191Process :: Runtime =>Ok (()), 192Process :: Generator =>Err ( SnapshotError :: GeneratorProcess ), 193} 194} 195 196pub ( crate ) enum ContextSource < ' a > { 197/// A new context for cold startup or snapshot generation. 198Fresh , 199Layout ( &' a snapshot:: Layout ), 200} 201 202impl Runtime { 203/// Bootstrap on V8's built-in heap. 204pub fn new ( options : RuntimeOptions ) ->Result < Self , Error > { 205runtime_process () ?; 206let isolate = snapshot:: IsolateOwner :: new ( snapshot:: Source :: BuiltIn ) ?; 207Self :: bootstrap ( isolate, options, ContextSource :: Fresh ) ?. start () 208} 209 210/// Restore the snapshot layout matching these extensions, attach fresh native 211/// state, then run live initializers. 212pub fn from_snapshot ( options : RuntimeOptions , snapshot : & Snapshot ) ->Result < Self , Error > { 213runtime_process () ?; 214let layout = snapshot. layout ( & snapshot:: fingerprint ( & options. extensions ) ?) ?; 215let isolate = snapshot:: IsolateOwner :: new ( snapshot:: Source :: Snapshot ( snapshot)) ?; 216Self :: bootstrap ( isolate, options, ContextSource :: Layout ( layout)) ?. start () 217} 218 219/// Evaluate each layout's static extension graph, without live initializers, 220/// into one snapshot whose contexts share a read-only heap. 221/// 222/// Static entries must not retain native resources, pending work, or global 223/// handles outside core-owned state: V8 aborts the process rather than 224/// serialize a heap still referenced by foreign handles. 225/// 226/// Run generation in a short-lived build process, after 227/// [`initialize_snapshot_generator`] for reproducible bytes. rusty_v8 cannot 228/// dispose an unfinished snapshot creator: on failure, core-owned native state 229/// is released, but the creator and V8's built-in read-only heap are leaked 230/// (with a warning) until process exit, so later snapshot restores in this 231/// process fail with [`SnapshotError::ReadOnlyHeapInUse`]. 232pub fn create_snapshot ( layouts : Vec < RuntimeOptions >) ->Result < Snapshot , Error > { 233initialize_v8 ( Process :: Runtime ); 234 snapshot:: create ( layouts) 235} 236 237pub ( crate ) fn bootstrap ( 238mut isolate : snapshot:: IsolateOwner , 239options : RuntimeOptions , 240context : ContextSource , 241) ->Result < Self , Error > { 242let order = extensions:: validate ( & options. extensions ) ?; 243let mut ops =Vec :: new (); 244let mut sources =HashMap :: new (); 245for extensionin & options. extensions { 246 sources. extend ( extension. modules . iter (). copied ()); 247} 248 sources. extend ( extensions:: SOURCES . iter (). copied ()); 249let entries = order 250. iter () 251. filter_map ( |index| options. extensions [ * index]. entry_point ) 252. collect ::< Vec < _ >>(); 253for extensionin options. extensions { 254 ops. extend ( extension. ops ); 255} 256let ops = op:: RegisteredOps :: new ( ops) ?; 257 258let wasm_streaming =Rc :: new ( RefCell :: new ( WasmStreamingRegistry :: default ())); 259 isolate. set_slot ( wasm_streaming. clone ()); 260 isolate. set_wasm_streaming_callback ( |scope, source, stream|{ 261let wasm_streaming = scope 262. get_slot ::< Rc < RefCell < WasmStreamingRegistry >>>() 263. unwrap () 264. clone (); 265let started = wasm_streaming. borrow_mut (). start ( stream); 266match started{ 267Ok ( start) =>{ 268if let Some ( callback) = start. callback { 269let callback = v8:: Local :: new ( scope, callback); 270let id = v8:: Number :: new ( scope, start. id as f64 ); 271let undefined = v8:: undefined ( scope); 272 v8:: tc_scope!( let try_catch, scope); 273let result = 274 callback. call ( try_catch, undefined. into (), & [ source, id. into ()]); 275if result. is_none () { 276let exception = try_catch 277. exception () 278. unwrap_or_else ( || v8:: undefined ( try_catch). into ()); 279 try_catch. reset (); 280let _ = wasm_streaming. borrow_mut (). abort ( start. id , exception); 281} 282} else { 283let message = 284 v8:: String :: new ( scope, "Wasm streaming callback is unavailable" ) 285. unwrap (); 286let exception = v8:: Exception :: type_error ( scope, message); 287let _ = wasm_streaming. borrow_mut (). abort ( start. id , exception); 288} 289} 290Err ( start) =>{ 291let message = v8:: String :: new ( scope, & start. message ). unwrap (); 292let exception = v8:: Exception :: type_error ( scope, message); 293 start. stream . abort ( Some ( exception)); 294} 295} 296}); 297 isolate. set_microtasks_policy ( v8:: MicrotasksPolicy :: Explicit ); 298 isolate. set_slot ( ops. clone ()); 299 op:: init ( & mut isolate); 300 reporting:: init ( & mut isolate); 301 microtasks:: init ( & mut isolate); 302 tasks:: init ( & mut isolate); 303 modules:: init ( & mut isolate, options. module_loader ); 304let capture = isolate. is_creator (); 305let restored =matches! ( context, ContextSource :: Layout ( _)); 306let context ={ 307 v8:: scope!( let scope, & mut * isolate); 308match context{ 309ContextSource :: Layout ( layout) =>{ 310let context = 311 v8:: Context :: from_snapshot ( scope, layout. context , Default :: default ()) 312. ok_or ( SnapshotError :: InvalidArtifact ( "missing layout context" )) ?; 313let scope =& mut v8:: ContextScope :: new ( scope, context); 314 extensions:: restore ( scope, sources, & layout. state ) ?; 315 v8:: Global :: new ( scope, context) 316} 317ContextSource :: Fresh =>{ 318let context = v8:: Context :: new ( scope, Default :: default ()); 319let scope =& mut v8:: ContextScope :: new ( scope, context); 320if capture{ 321 snapshot:: define_stack_trace_limit ( scope). unwrap (); 322} 323let registry = ops. object ( scope). unwrap (); 324 325let mut source =source ( 326 scope, 327"<primordials>" , 328include_str! ( "primordials.js" ), 329false , 330None , 331); 332let primordials = v8:: script_compiler:: compile ( 333 scope, 334& mut source, 335 v8:: script_compiler:: CompileOptions :: NoCompileOptions , 336 v8:: script_compiler:: NoCacheReason :: NoReason , 337) 338. unwrap () 339. run ( scope) 340. unwrap (); 341let primordial_object = v8:: Local ::< v8:: Object >:: try_from ( primordials). unwrap (); 342 extensions:: init ( scope, sources, primordial_object, registry). unwrap (); 343 v8:: Global :: new ( scope, context) 344} 345} 346}; 347 348let mut runtime =Self { 349repl : None , 350module_evaluation : None , 351 context, 352 isolate, 353}; 354 runtime. with_context ( |scope|{ 355let module = extensions:: execute ( scope, "ext:core/stack_trace.js" ) ?; 356let namespace = v8:: Local ::< v8:: Object >:: try_from ( module. get_module_namespace ()) 357. expect ( "Core stack formatter must have a module namespace" ); 358let key = v8:: String :: new ( scope, "formatStackTrace" ) ?; 359let formatter = v8:: Local ::< v8:: Function >:: try_from ( namespace. get ( scope, key. into ()) ?) 360. expect ( "Core stack formatter must export formatStackTrace" ); 361 stack_trace:: init ( scope, formatter); 362 extensions:: execute ( scope, "ext:core/shared_array_buffer" ) ?; 363Some (()) 364}) ?; 365if !restored{ 366for entryin entries{ 367 runtime. with_context ( |scope| extensions:: execute ( scope, entry). map ( |_|())) ?; 368} 369} 370 runtime 371. with_context ( |scope| extensions:: execute ( scope, "ext:core/bootstrap" ). map ( |_|())) ?; 372Ok ( runtime) 373} 374 375/// Run the live initializers registered through `ext:core/bootstrap`, exactly once. 376fn start ( mut self ) ->Result < Self , Error > { 377self . with_context ( |scope|{ 378let module = extensions:: execute ( scope, "ext:core/bootstrap" ) ?; 379let namespace = 380 v8:: Local ::< v8:: Object >:: try_from ( module. get_module_namespace ()). ok () ?; 381let key = v8:: String :: new ( scope, "startRuntime" ) ?; 382let start = 383 v8:: Local ::< v8:: Function >:: try_from ( namespace. get ( scope, key. into ()) ?). ok () ?; 384 start. call ( scope, v8:: undefined ( scope). into (), & []) ?; 385Some (()) 386}) ?; 387Ok ( self ) 388} 389 390fn with_context < T >( 391& mut self , 392execute : impl for < ' s , ' i > FnOnce ( & mut v8:: PinScope < ' s , ' i >) ->Option < T >, 393) ->Result < T , Error > { 394 v8:: scope!( let scope, & mut * self . isolate ); 395let context = v8:: Local :: new ( scope, & self . context ); 396let scope =& mut v8:: ContextScope :: new ( scope, context); 397 v8:: tc_scope!( let scope, scope); 398 reporting:: clear_processed ( scope); 399let result =execute ( scope); 400let execution_exception =if result. is_none () { 401 scope. exception (). map ( |error| v8:: Global :: new ( scope, error)) 402} else { 403None 404}; 405let execution_exception_reported =if let Some ( error) =& execution_exception{ 406let error = v8:: Local :: new ( scope, error); 407 reporting:: was_processed ( scope, error) 408} else { 409false 410}; 411 op:: checkpoint ( scope); 412let exception = microtasks:: process_exceptions ( scope); 413let rejection = op:: process_rejections ( scope); 414let result = result. and_then ( |value|{ 415if let Some ( exception) = exception{ 416let error = v8:: Local :: new ( scope, exception); 417 reporting:: clear_processed ( scope); 418 reporting:: mark_processed_exception ( scope, error); 419 scope. throw_exception ( error); 420None 421} else if let Some ( error) = rejection{ 422let error = v8:: Local :: new ( scope, error); 423if let Some ( execution_exception) =& execution_exception{ 424 reporting:: clear_processed ( scope); 425 scope. throw_exception ( v8:: Local :: new ( scope, execution_exception)); 426} else { 427 scope. throw_exception ( error); 428} 429None 430} else { 431Some ( value) 432} 433}); 434if result. is_none () 435 &&let Some ( error) = scope. exception () 436 && !( execution_exception_reported 437 && execution_exception 438. as_ref () 439. is_some_and ( |reported| v8:: Local :: new ( scope, reported) == error)) 440 &&( execution_exception. is_some () || !reporting:: was_processed ( scope, error)) 441{ 442 reporting:: report_exception_for_value ( scope, error); 443} 444let result = result. ok_or_else ( ||{ 445let exception = scope 446. stack_trace () 447. filter ( |value| !value. is_undefined ()) 448. or_else ( || scope. exception ()); 449let message = exception 450. map ( |value| value. to_rust_string_lossy ( scope)) 451. unwrap_or_else ( ||"JavaScript execution terminated" . into ()); 452Error :: JavaScript ( message) 453}); 454 reporting:: clear_processed ( scope); 455 result 456} 457 458/// Register a built-in module, capturing its export values at registration. 459pub fn register_module ( 460& mut self , 461name : & str , 462exports : impl for < ' s , ' i > FnOnce ( & mut v8:: PinScope < ' s , ' i >) ->Option < v8:: Local < ' s , v8:: Object >>, 463) ->Result <(), Error > { 464self . with_context ( |scope|{ 465let exports =exports ( scope) ?; 466 modules:: register ( scope, name, exports) 467}) 468} 469 470/// Register an extension module's exports as a built-in module, capturing 471/// its export values at registration. The extension module is evaluated if 472/// it has not been already. 473pub fn register_extension_module ( 474& mut self , 475name : & str , 476extension_module : & str , 477) ->Result <(), Error > { 478self . with_context ( |scope|{ 479let module = extensions:: execute ( scope, extension_module) ?; 480let namespace = 481 v8:: Local ::< v8:: Object >:: try_from ( module. get_module_namespace ()). ok () ?; 482 modules:: register ( scope, name, namespace) 483}) 484} 485 486/// Execute a module graph; the first entry is the runtime's main module. 487pub fn execute_module ( & mut self , name : & str ) ->Result <(), Error > { 488let name = modules:: prepare_entry ( & mut self . isolate , name) ?; 489self . with_context ( |scope|{ 490let promise = modules:: execute ( scope, & name) ?; 491 op:: run_event_loop ( scope, Some ( promise), true ) 492}) 493} 494 495/// Evaluate an entry, then pass its namespace to a trusted extension bootstrap. 496pub fn execute_module_with_bootstrap ( 497& mut self , 498name : & str , 499bootstrap : & str , 500export : & str , 501) ->Result <(), Error > { 502let name = modules:: prepare_entry ( & mut self . isolate , name) ?; 503self . with_context ( |scope|{ 504let promise = modules:: execute ( scope, & name) ?; 505 op:: run_event_loop ( scope, Some ( promise), false ) ?; 506let namespace = modules:: namespace ( scope, & name) ?; 507let module = extensions:: execute ( scope, bootstrap) ?; 508let exports = v8:: Local ::< v8:: Object >:: try_from ( module. get_module_namespace ()). ok () ?; 509let key = v8:: String :: new ( scope, export) ?; 510let function = 511 v8:: Local ::< v8:: Function >:: try_from ( exports. get ( scope, key. into ()) ?). ok () ?; 512let undefined = v8:: undefined ( scope); 513let value = function. call ( scope, undefined. into (), & [ namespace]) ?; 514let completion = v8:: Local ::< v8:: Promise >:: try_from ( value). ok (); 515 op:: run_event_loop ( scope, completion, false ) 516}) 517} 518 519/// Start a prepared entry without blocking on top-level await. Polling the event 520/// loop also monitors its evaluation promise. 521pub fn start_module ( & mut self , name : & str ) ->Result <(), Error > { 522if self . module_evaluation . is_some () { 523return Err ( Error :: Module ( "Module evaluation is already pending" . into ())); 524} 525let name = modules:: prepare_entry ( & mut self . isolate , name) ?; 526let promise =self . with_context ( |scope|{ 527 modules:: execute ( scope, & name). map ( |promise| v8:: Global :: new ( scope, promise)) 528}) ?; 529self . module_evaluation =Some ( promise); 530Ok (()) 531} 532 533/// Drive microtasks and native ops until idle, reporting unhandled rejections. 534pub fn run_event_loop ( & mut self ) ->Result <(), Error > { 535let evaluation =self . module_evaluation . take (); 536self . with_context ( |scope|{ 537let promise = evaluation. map ( |promise| v8:: Local :: new ( scope, promise)); 538 op:: run_event_loop ( scope, promise, true ) 539}) 540} 541 542/// Drive one task or native completion and register the caller's wakeup. 543/// A false result does not imply idle: native work may still be pending. 544pub fn poll_event_loop ( & mut self , context : & mut Context < ' _ >) ->Result < bool , Error > { 545self . isolate . set_slot ( context. waker (). clone ()); 546let evaluation =self . module_evaluation . take (); 547let ( progressed, evaluation) =self . with_context ( |scope|{ 548let progressed = op:: poll_event_loop ( scope, context) ?; 549let evaluation =if let Some ( promise) = evaluation{ 550let local = v8:: Local :: new ( scope, & promise); 551match local. state () { 552 v8:: PromiseState :: Pending =>Some ( promise), 553 v8:: PromiseState :: Fulfilled =>None , 554 v8:: PromiseState :: Rejected =>{ 555let error = local. result ( scope); 556 scope. throw_exception ( error); 557return None ; 558} 559} 560} else { 561None 562}; 563Some (( progressed, evaluation)) 564}) ?; 565self . module_evaluation = evaluation; 566if progressed{ 567 context. waker (). wake_by_ref (); 568} 569Ok ( progressed) 570} 571 572pub fn isolate_handle ( & self ) -> v8:: IsolateHandle { 573self . isolate . thread_safe_handle () 574} 575 576pub fn has_pending_background_tasks ( & self ) ->bool { 577self . isolate . has_pending_background_tasks () 578} 579 580pub fn has_pending_work ( & self ) ->bool { 581self . module_evaluation . is_some () 582 || op:: has_pending_work ( & self . isolate ) 583 || tasks:: has_pending ( & self . isolate ) 584 ||self . has_pending_background_tasks () 585} 586 587pub fn enable_repl ( & mut self ) ->Result <(), Error > { 588if self . repl . is_some () { 589return Err ( Error :: JavaScript ( 590"REPL inspector is already enabled" . into (), 591)); 592} 593 594self . repl =Some ( repl:: Inspector :: new ( & mut self . isolate , & self . context )); 595 596let id =self . dispatch_repl_message ( json! ({ "method" : "Runtime.enable" , "params" : {}})) ?; 597let _ =self . take_repl_response ( id); 598let inspector =self . repl . as_mut (). unwrap (); 599let Some ( context_id) = inspector. context_id () else { 600return Err ( Error :: JavaScript ( 601"REPL execution context was not created" . into (), 602)); 603}; 604 inspector. context_id = context_id; 605Ok (()) 606} 607 608pub fn evaluate_repl ( & mut self , expression : & str , source_url : & str ) ->Result < i32 , Error > { 609self . set_repl_referrer ( Some ( source_url)) ?; 610let expression =format! ( "{expression}\n//# sourceURL={source_url}" ); 611self . dispatch_repl_message ( json! ({ 612"method" : "Runtime.evaluate" , 613"params" : { 614"expression" : expression, 615"contextId" : self . repl . as_ref (). map ( |inspector| inspector. context_id ), 616"awaitPromise" : true , 617"replMode" : true , 618"includeCommandLineAPI" : false , 619"objectGroup" : "avril-repl" 620} 621})) 622} 623 624/// Call a function in the REPL context with inspector `CallArgument`s, such as 625/// `{"objectId": ...}` from an earlier result, and resolve to its JSON value. 626pub fn call_repl_function ( 627& mut self , 628declaration : & str , 629arguments : & [ serde_json:: Value ], 630) ->Result < i32 , Error > { 631self . dispatch_repl_message ( json! ({ 632"method" : "Runtime.callFunctionOn" , 633"params" : { 634"functionDeclaration" : declaration, 635"executionContextId" : self . repl . as_ref (). map ( |inspector| inspector. context_id ), 636"arguments" : arguments, 637"awaitPromise" : true , 638"returnByValue" : true , 639"objectGroup" : "avril-repl" 640} 641})) 642} 643 644pub fn has_repl_response ( & self , id : i32 ) ->bool { 645self . repl 646. as_ref () 647. is_some_and ( |inspector| inspector. has_response ( id)) 648} 649 650pub fn take_repl_response ( & mut self , id : i32 ) ->Option < String > { 651self . repl . as_mut () ?. take_response ( id) 652} 653 654fn set_repl_referrer ( & mut self , referrer : Option < & str >) ->Result <(), Error > { 655 v8:: scope!( let scope, & mut * self . isolate ); 656let context = v8:: Local :: new ( scope, & self . context ); 657let scope =& mut v8:: ContextScope :: new ( scope, context); 658 modules:: set_repl_referrer ( scope, referrer) 659. ok_or_else ( ||Error :: JavaScript ( "REPL module map is unavailable" . into ())) 660} 661 662pub fn terminate_repl_execution ( & mut self ) ->Result <(), Error > { 663let id =self . dispatch_repl_message ( json! ({ 664"method" : "Runtime.terminateExecution" , 665"params" : {} 666})) ?; 667let _ =self . take_repl_response ( id); 668Ok (()) 669} 670 671pub fn release_repl_objects ( & mut self ) ->Result <(), Error > { 672let id =self . dispatch_repl_message ( json! ({ 673"method" : "Runtime.releaseObjectGroup" , 674"params" : { "objectGroup" : "avril-repl" } 675})) ?; 676let _ =self . take_repl_response ( id); 677Ok (()) 678} 679 680fn dispatch_repl_message ( & mut self , mut command : serde_json:: Value ) ->Result < i32 , Error > { 681let inspector =self 682. repl 683. as_mut () 684. ok_or_else ( ||Error :: JavaScript ( "REPL inspector is not enabled" . into ())) ?; 685let id = inspector. next_id ; 686 inspector. next_id = inspector 687. next_id 688. checked_add ( 1 ) 689. ok_or_else ( ||Error :: JavaScript ( "REPL inspector command ID space exhausted" . into ())) ?; 690 command[ "id" ] =json! ( id); 691let message = serde_json:: to_string ( & command) 692. map_err ( |error|Error :: JavaScript ( error. to_string ())) ?; 693 inspector 694. session 695. dispatch_protocol_message ( StringView :: from ( message. as_bytes ())); 696Ok ( id) 697} 698 699pub fn disable_repl ( & mut self ) ->Result <(), Error > { 700self . isolate . remove_slot ::< std:: task:: Waker >(); 701let result =self . set_repl_referrer ( None ); 702self . repl . take (); 703 result 704} 705 706/// Evaluate a script and drain microtasks without waiting for native ops. 707pub fn execute_script < T : DeserializeOwned >( 708& mut self , 709name : & str , 710code : & str , 711) ->Result < T , Error > { 712self . with_context ( |scope|{ 713let mut source =source ( scope, name, code, false , None ); 714let value = v8:: script_compiler:: compile ( 715 scope, 716& mut source, 717 v8:: script_compiler:: CompileOptions :: NoCompileOptions , 718 v8:: script_compiler:: NoCacheReason :: NoReason , 719) ? 720. run ( scope) ?; 721 op:: checkpoint ( scope); 722Some ( serde_v8:: from_v8 ( scope, value). map_err ( Error :: from)) 723}) ? 724} 725}