char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
6534c3e
main
1use std:: cell:: RefCell ; 2use std:: path:: Path ; 3use std:: rc:: Rc ; 4use std:: sync:: Arc ; 5 6use avril_caps::{ Capabilities , Initial }; 7use avril_core::{ Error , Extension , Op , Runtime , RuntimeOptions }; 8use avril_host:: environment:: Environment ; 9use avril_runtime:: authority:: SourceAuthority ; 10use avril_runtime:: modules:: ModuleGraph ; 11use avril_runtime:: registry::{ Options , PackageStore }; 12 13use super ::{ Control , Settings , WorkerScope }; 14use avril_ext_process:: SignalMode ; 15use avril_ext_stdio:: Stdio ; 16use avril_host:: Host ; 17 18# [ path = "../../../core/tests/support/mod.rs" ] 19mod support; 20 21fn settings ( root : & Path , host : & Host ) ->Settings { 22let store =PackageStore :: new ( 23 host. handle (), 24Options { 25jsr_registry : "https://jsr.invalid/" . parse (). unwrap (), 26npm_registry : "https://npm.invalid/" . parse (). unwrap (), 27cache : root. join ( "cache" ), 28lock : root. join ( "avril.lock" ), 29project_root : root. to_owned (), 30offline : true , 31frozen : false , 32update : false , 33ephemeral_lock : true , 34vendor : None , 35}, 36) 37. unwrap (); 38Settings { 39snapshot : None , 40storage : avril_webstorage:: Persistent :: new ( 41 root. join ( "storage" ), 42& root. join ( "avril.conf" ), 43"main.js" , 44), 45caps : Capabilities :: new ( Initial :: new ( false ), root. to_owned ()), 46environment : Environment :: default (), 47args : vec! [], 48store : Arc :: new ( store), 49runtime : host. handle (), 50broker : Arc :: default (), 51process_signal : SignalMode :: Run , 52} 53} 54 55# [ test ] 56fn static_bootstrap_never_calls_live_ops_in_main_or_worker_runtimes () { 57let directory = tempfile:: tempdir (). unwrap (); 58let host =Host :: new (). unwrap (); 59for workerin [ false , true ] { 60let settings =settings ( directory. path (), & host); 61let graph =Rc :: new ( ModuleGraph :: open_shared ( 62SourceAuthority :: execute ( settings. caps . clone ()), 63 settings. store . clone (), 64)); 65let scope = worker. then ( ||WorkerScope { 66control : Arc :: new ( Control :: default ()), 67inbox : settings. broker . pair (). unwrap (). 1 , 68url : "https://worker.invalid/entry.js?fresh#scope" . into (), 69name : "bootstrap worker" . into (), 70}); 71let mut extensions = 72super :: extensions ( graph, settings, Stdio :: new (). unwrap (), scope, None , None ). unwrap (); 73let calls =Rc :: new ( RefCell :: new ( Vec :: new ())); 74for extensionin & mut extensions{ 75for opin & mut extension. ops { 76// These depend only on the target and codec tables, never on process state. 77if matches! ( op. name , "op_path_platform" |"op_encoding_label" ) { 78continue ; 79} 80let name = op. name ; 81let calls = calls. clone (); 82* op =Op :: try_raw ( name, move |_, _|{ 83 calls. borrow_mut (). push ( name); 84Err ( std:: io:: Error :: other ( format! ( 85"Live op during static bootstrap: {name}" 86))) 87}); 88} 89} 90 extensions. push ( Extension { 91name : "ext:audit" , 92dependencies : & [], 93ops : vec! [], 94modules : & [( 95"ext:audit/main.js" , 96r#" 97import 'ext:path/main.js'; 98import 'ext:path/posix.js'; 99import 'ext:path/win32.js'; 100import 'ext:base/sync.js'; 101import 'ext:file/fs.js'; 102import 'ext:file/fs_sync.js'; 103import 'ext:test/main.js'; 104for (const name of ['Worker', 'URL', 'ReadableStream', 'TextDecoder', 'Performance']) { 105if (typeof globalThis[name] !== 'function') throw Error('Missing static interface: ' + name); 106} 107throw Error('static bootstrap complete'); 108"# , 109)], 110entry_point : Some ( "ext:audit/main.js" ), 111}); 112let result =Runtime :: new ( RuntimeOptions { extensions, ..Default :: default () }); 113assert! ( 114 calls. borrow (). is_empty (), 115"worker={worker}: {:?}" , 116 calls. borrow () 117); 118assert! ( 119matches! ( result, Err ( Error :: JavaScript ( message)) if message. contains ( "static bootstrap complete" )) 120); 121} 122} 123 124# [ test ] 125fn deferred_public_entries_are_loaded_by_registration_not_static_bootstrap () { 126let directory = tempfile:: tempdir (). unwrap (); 127let host =Host :: new (). unwrap (); 128for entryin [ "ext:base/sync.js" , "ext:file/fs.js" , "ext:file/fs_sync.js" ] { 129let settings =settings ( directory. path (), & host); 130let graph =Rc :: new ( ModuleGraph :: open_shared ( 131SourceAuthority :: execute ( settings. caps . clone ()), 132 settings. store . clone (), 133)); 134let mut extensions = 135super :: extensions ( graph, settings, Stdio :: new (). unwrap (), None , None , None ). unwrap (); 136let extension = extensions 137. iter_mut () 138. find ( |extension| extension. modules . iter (). any ( |( name, _) |* name == entry)) 139. unwrap (); 140 extension. modules = extension 141. modules 142. iter () 143. map ( |& ( name, source) |{ 144if name == entry{ 145( name, "throw Error('registered public entry evaluated');" ) 146} else { 147( name, source) 148} 149}) 150. collect ::< Vec < _ >>() 151. leak (); 152let mut runtime =Runtime :: new ( RuntimeOptions { extensions, ..Default :: default () }) 153. expect ( "Deferred public entries must not be static entry points" ); 154assert! ( 155matches! ( crate :: stdlib:: register ( & mut runtime), 156Err ( Error :: JavaScript ( message)) if message. contains ( "registered public entry evaluated" )), 157"{entry} was not evaluated by public registration" 158); 159} 160} 161 162# [ test ] 163fn live_metadata_is_initialized_per_runtime_before_public_exports_are_registered () { 164let directory = tempfile:: tempdir (). unwrap (); 165let host =Host :: new (). unwrap (); 166for labelin [ "first" , "second" ] { 167let mut settings =settings ( directory. path (), & host); 168 settings. args =vec! [ label. into (), "argument with spaces" . into ()]; 169 settings. environment = 170Environment :: new ([( "BOOTSTRAP_VALUE" . into (), label. into ())]). unwrap (); 171let graph =Rc :: new ( ModuleGraph :: open_shared ( 172SourceAuthority :: execute ( settings. caps . clone ()), 173 settings. store . clone (), 174)); 175let mut runtime = 176super :: runtime ( graph, settings, Stdio :: new (). unwrap (), None , None ). unwrap (); 177 runtime. execute_script ::<()>( "metadata.js" , r#" 178import('avril:std').then(({process, runtime, env}) => { 179if (!Object.isFrozen(process) || !Object.isFrozen(process.args) || !Object.isFrozen(runtime)) { 180throw Error('Mutable metadata'); 181} 182const pid = Object.getOwnPropertyDescriptor(process, 'pid'); 183if (pid.get !== undefined || pid.writable || pid.configurable || !pid.enumerable) { 184throw Error('Changed metadata descriptor'); 185} 186if (!(performance.timeOrigin > 0) || !(performance.now() >= 0)) throw Error('Missing clock'); 187if (!(navigator.hardwareConcurrency >= 1) || !navigator.userAgent.startsWith('avril/')) { 188throw Error('Missing navigator metadata'); 189} 190if (!Object.isFrozen(navigator.userAgentData.brands)) throw Error('Mutable brands'); 191globalThis.metadata = [process.pid, process.args, env.get('BOOTSTRAP_VALUE')]; 192env.set('BOOTSTRAP_VALUE', 'changed'); 193navigator.userAgent = 'changed'; 194}); 195"# ). unwrap (); 196 runtime. run_event_loop (). unwrap (); 197assert_eq! ( 198 runtime 199. execute_script ::< serde_json:: Value >( "result.js" , "metadata" ) 200. unwrap (), 201 serde_json:: json!([ std:: process:: id (), [ label, "argument with spaces" ], label]), 202); 203} 204} 205 206# [ test ] 207fn snapshots_restore_main_and_worker_runtime_state () { 208if let Some ( role) = support:: role () { 209let root = support:: directory (); 210if role. starts_with ( "create-" ) { 211let snapshot =super :: create_snapshot (). unwrap (); 212 std:: fs:: write ( root. join ( format! ( "{role}.snapshot" )), snapshot. as_bytes ()). unwrap (); 213} else { 214let host =Host :: new (). unwrap (); 215let snapshot =( role =="restore" ). then ( ||{ 216// SAFETY: Our parent produced this artifact with this same executable. 217Arc :: new ( unsafe { 218 avril_core:: Snapshot :: from_bytes ( 219 std:: fs:: read ( root. join ( "create-first.snapshot" )). unwrap (), 220) 221. unwrap () 222}) 223}); 224for workerin [ false , true ] { 225for labelin [ "first" , "second" ] { 226let mut settings =settings ( & root, & host); 227 settings. snapshot = snapshot. clone (); 228 settings. args =vec! [ label. into ()]; 229 settings. environment = 230Environment :: new ([( "SNAPSHOT_VALUE" . into (), label. into ())]). unwrap (); 231let graph =Rc :: new ( ModuleGraph :: open_shared ( 232SourceAuthority :: execute ( settings. caps . clone ()), 233 settings. store . clone (), 234)); 235let scope = worker. then ( ||WorkerScope { 236control : Arc :: new ( Control :: default ()), 237inbox : settings. broker . pair (). unwrap (). 1 , 238url : format! ( "https://worker.invalid/{label}.js" ), 239name : label. into (), 240}); 241let mut runtime =super :: create_runtime ( 242 graph, 243 settings, 244Stdio :: new (). unwrap (), 245 scope, 246None , 247None , 248) 249. unwrap (); 250let surface = runtime 251. execute_script ::< Vec < String >>( 252"surface.js" , 253include_str! ( "snapshot_surface.js" ), 254) 255. unwrap (); 256 std:: fs:: write ( 257 root. join ( format! ( "surface-{role}-{worker}-{label}.json" )), 258 serde_json:: to_vec_pretty ( & surface). unwrap (), 259) 260. unwrap (); 261 runtime 262. execute_script ::<()>( "snapshot-test.js" , include_str! ( "snapshot_test.js" )) 263. unwrap (); 264 runtime. run_event_loop (). unwrap (); 265assert_eq! ( 266 runtime 267. execute_script ::< serde_json:: Value >( "metadata" , "metadata" ) 268. unwrap (), 269 serde_json:: json!([ 270 std:: process:: id (), 271 label, 272 label, 273if worker{ label} else { "" }, 274if worker{ 275format! ( "https://worker.invalid/{label}.js" ) 276} else { 277String :: new () 278}, 279 !worker 280]), 281); 282assert! ( 283 runtime 284. execute_script ::< bool >( "complete" , "snapshotTestComplete === true" ) 285. unwrap () 286); 287} 288} 289} 290} else { 291let directory = tempfile:: tempdir (). unwrap (); 292for rolein [ "create-first" , "create-second" , "cold" , "restore" ] { 293 support:: child ( 294"assembly::tests::snapshots_restore_main_and_worker_runtime_state" , 295 role, 296 directory. path (), 297); 298} 299assert! ( 300 std:: fs:: read ( directory. path (). join ( "create-first.snapshot" )). unwrap () 301 == std:: fs:: read ( directory. path (). join ( "create-second.snapshot" )). unwrap (), 302"Production snapshot generation must be reproducible across processes" , 303); 304for workerin [ false , true ] { 305for labelin [ "first" , "second" ] { 306let surface = |role|{ 307 std:: fs:: read_to_string ( 308 directory 309. path () 310. join ( format! ( "surface-{role}-{worker}-{label}.json" )), 311) 312. unwrap () 313}; 314assert_eq! ( 315surface ( "cold" ), 316surface ( "restore" ), 317"worker={worker}, label={label}" 318); 319} 320} 321} 322}