char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
44fe2c6
main
1use avril_core::{ Extension , Op }; 2use std:: convert:: Infallible ; 3 4# [ cfg ( test )] 5pub fn init () ->Extension { 6with_broker ( std:: sync:: Arc :: default ()) 7} 8 9pub fn with_broker ( broker : std:: sync:: Arc < avril_host:: messaging:: Broker >) ->Extension { 10let metadata =( 11match std:: env:: consts:: OS { 12"windows" =>"Win32" . into (), 13"macos" =>"MacIntel" . into (), 14"linux" =>format! ( "Linux {}" , std:: env:: consts:: ARCH ), 15 os => os. into (), 16}, 17 std:: thread:: available_parallelism () 18. map ( |parallelism| parallelism. get ()) 19. unwrap_or ( 1 ), 20concat! ( "avril/" , env! ( "CARGO_PKG_VERSION" )), 21); 22# [ derive ( Clone , serde :: Serialize )] 23struct Brand { 24brand : String , 25version : String , 26} 27# [ derive ( Clone , serde :: Serialize )] 28# [ serde ( rename_all = "camelCase" )] 29struct UserAgentData { 30brands : Vec < Brand >, 31mobile : bool , 32platform : String , 33architecture : String , 34bitness : String , 35model : String , 36platform_version : String , 37ua_full_version : String , 38full_version_list : Vec < Brand >, 39wow64 : bool , 40} 41const FULL_VERSION : & str =env! ( "CARGO_PKG_VERSION" ); 42let major =FULL_VERSION . split ( '.' ). next (). unwrap_or ( FULL_VERSION ); 43let user_agent_data =UserAgentData { 44brands : vec! [ Brand { 45brand : "avril" . into (), 46version : major. into (), 47}], 48mobile : false , 49platform : match std:: env:: consts:: OS { 50"windows" =>"Windows" . into (), 51"macos" =>"macOS" . into (), 52"linux" =>"Linux" . into (), 53 os => os. into (), 54}, 55architecture : match std:: env:: consts:: ARCH { 56"x86" |"i686" |"x86_64" =>"x86" . into (), 57"arm" |"aarch64" =>"arm" . into (), 58 arch => arch. into (), 59}, 60bitness : match std:: env:: consts:: ARCH { 61"x86_64" |"aarch64" =>"64" . into (), 62 _ =>"32" . into (), 63}, 64model : String :: new (), 65platform_version : String :: new (), 66ua_full_version : FULL_VERSION . into (), 67full_version_list : vec! [ Brand { 68brand : "avril" . into (), 69version : FULL_VERSION . into (), 70}], 71wow64 : false , 72}; 73let mut ops = avril_host:: messaging:: ops ( broker); 74 ops. push ( Op :: new ( "op_navigator_metadata" , move |(): () |{ 75Ok ::< _ , Infallible >( metadata. clone ()) 76})); 77 ops. push ( Op :: new ( "op_navigator_user_agent_data" , move |(): () |{ 78Ok ::< _ , Infallible >( user_agent_data. clone ()) 79})); 80Extension { 81name : "ext:web" , 82dependencies : & [ "ext:timers" ], 83 ops, 84modules : & [ 85( 86"ext:web/main.js" , 87include_str! ( concat! ( env! ( "OUT_DIR" ), "/main.js" )), 88), 89( 90"ext:web/idl.js" , 91include_str! ( concat! ( env! ( "OUT_DIR" ), "/idl.js" )), 92), 93( 94"ext:web/interface.js" , 95include_str! ( concat! ( env! ( "OUT_DIR" ), "/interface.js" )), 96), 97( 98"ext:web/events.js" , 99include_str! ( concat! ( env! ( "OUT_DIR" ), "/events.js" )), 100), 101( 102"ext:web/messaging.js" , 103include_str! ( concat! ( env! ( "OUT_DIR" ), "/messaging.js" )), 104), 105( 106"ext:web/broadcast.js" , 107include_str! ( concat! ( env! ( "OUT_DIR" ), "/broadcast.js" )), 108), 109( 110"ext:web/serialization.js" , 111include_str! ( concat! ( env! ( "OUT_DIR" ), "/serialization.js" )), 112), 113( 114"ext:web/base64.js" , 115include_str! ( concat! ( env! ( "OUT_DIR" ), "/base64.js" )), 116), 117( 118"ext:web/navigator.js" , 119include_str! ( concat! ( env! ( "OUT_DIR" ), "/navigator.js" )), 120), 121( 122"ext:web/performance.js" , 123include_str! ( concat! ( env! ( "OUT_DIR" ), "/performance.js" )), 124), 125], 126entry_point : Some ( "ext:web/main.js" ), 127} 128} 129 130# [ cfg ( test )] 131mod tests{ 132use avril_core::{ Extension , Op , Runtime , RuntimeOptions }; 133use std:: convert:: Infallible ; 134 135fn runtime () ->Runtime { 136Runtime :: new ( RuntimeOptions { 137extensions : vec! [ 138super :: init (), 139 avril_ext_timers:: init (). unwrap (), 140Extension { 141name : "ext:gc-test" , 142dependencies : & [], 143ops : vec! [ Op :: with_scope ( "collect" , |scope, (): () |{ 144 scope. low_memory_notification (); 145Ok ::< _ , Infallible >(()) 146})], 147modules : & [( 148"ext:gc-test/main.js" , 149"import {collect} from 'ext:core/ops'; globalThis.collect=collect;" , 150)], 151entry_point : Some ( "ext:gc-test/main.js" ), 152}, 153], 154 ..Default :: default () 155}) 156. unwrap () 157} 158 159fn collect ( runtime : & mut Runtime ) { 160for _in 0 ..4 { 161 runtime. execute_script ::<()>( "gc" , "collect();" ). unwrap (); 162 runtime. run_event_loop (). unwrap (); 163} 164} 165 166# [ test ] 167fn unobserved_channels_and_started_ports_can_be_collected () { 168let mut runtime =runtime (); 169 runtime 170. execute_script ::<()>( 171"objects" , 172r#" 173globalThis.cleaned = {channels:0,ports:0,control:0}; 174globalThis.registry = new FinalizationRegistry(kind => cleaned[kind]++); 175(() => { 176for (let i=0;i<100;i++) { 177registry.register(new BroadcastChannel('gc'), 'channels'); 178const channel = new MessageChannel(); 179channel.port1.start(); 180registry.register(channel.port1, 'ports'); 181registry.register({}, 'control'); 182} 183})(); 184"# , 185) 186. unwrap (); 187collect ( & mut runtime); 188assert! ( 189 runtime 190. execute_script ::< bool >( 191"counts" , 192"cleaned.channels > 50 && cleaned.ports > 50 && cleaned.control > 50" 193) 194. unwrap () 195); 196} 197 198# [ test ] 199fn channel_listener_lifetimes_include_once_abort_and_borrowed_methods () { 200let mut runtime =runtime (); 201 runtime 202. execute_script ::<()>( 203"objects" , 204r#" 205globalThis.cleaned = []; 206globalThis.registry = new FinalizationRegistry(kind => cleaned.push(kind)); 207globalThis.sender = new BroadcastChannel('lifetime'); 208globalThis.controller = new AbortController(); 209(() => { 210const channel = new BroadcastChannel('lifetime'); 211channel.onmessage = () => {}; 212registry.register(channel,'handler'); 213globalThis.weak = new WeakRef(channel); 214const once = new BroadcastChannel('lifetime'); 215EventTarget.prototype.addEventListener.call(once,'message',()=>{}, {once:true}); 216registry.register(once,'once'); 217const aborted = new BroadcastChannel('lifetime'); 218aborted.addEventListener('message',()=>{}, {signal:controller.signal}); 219registry.register(aborted,'abort'); 220})(); 221"# , 222) 223. unwrap (); 224collect ( & mut runtime); 225assert! ( 226 runtime 227. execute_script ::< bool >( 228"alive" , 229"weak.deref() !== undefined && cleaned.length === 0" 230) 231. unwrap () 232); 233 runtime 234. execute_script ::<()>( 235"release" , 236r#" 237weak.deref().onmessage = null; 238controller.abort(); 239sender.postMessage('event'); 240"# , 241) 242. unwrap (); 243 runtime. run_event_loop (). unwrap (); 244collect ( & mut runtime); 245assert! ( 246 runtime 247. execute_script ::< bool >( 248"released" , 249"['handler','once','abort'].every(kind => cleaned.includes(kind))" 250) 251. unwrap () 252); 253 runtime 254. execute_script ::<()>( "close" , "sender.close();" ) 255. unwrap (); 256} 257 258# [ test ] 259fn collection_can_race_pending_port_deliveries () { 260let mut runtime =runtime (); 261 runtime 262. execute_script ::<()>( 263"deliveries" , 264r#" 265globalThis.cleaned = 0; 266globalThis.registry = new FinalizationRegistry(()=>cleaned++); 267globalThis.done = false; 268(async()=> { 269const peers = []; 270for (let round=0;round<10;round++) { 271(()=> { 272for (let i=0;i<100;i++) { 273const channel = new MessageChannel(); 274if (i % 2) { 275channel.port1.onmessage = ()=>{}; 276channel.port1.onmessage = null; 277} else channel.port1.start(); 278registry.register(channel.port1,0); 279channel.port2.postMessage({data:new Uint8Array(64)}); 280peers.push(channel.port2); 281} 282})(); 283await new Promise(resolve=>setTimeout(()=>{collect();resolve();},0)); 284collect(); 285} 286peers.forEach(peer=>peer.close()); 287done = true; 288})(); 289"# , 290) 291. unwrap (); 292 runtime. run_event_loop (). unwrap (); 293collect ( & mut runtime); 294assert! ( 295 runtime 296. execute_script ::< bool >( "done" , "done && cleaned > 500" ) 297. unwrap () 298); 299} 300 301# [ test ] 302fn ports_with_listeners_stay_alive_until_the_peer_closes () { 303let mut runtime =runtime (); 304 runtime 305. execute_script ::<()>( 306"objects" , 307r#" 308globalThis.cleaned = false; 309globalThis.registry = new FinalizationRegistry(()=> cleaned=true); 310(() => { 311const channel = new MessageChannel(); 312channel.port1.onmessage = ()=> {}; 313registry.register(channel.port1,0); 314globalThis.weak = new WeakRef(channel.port1); 315globalThis.peer = channel.port2; 316})(); 317"# , 318) 319. unwrap (); 320collect ( & mut runtime); 321assert! ( 322 runtime 323. execute_script ::< bool >( "alive" , "weak.deref() !== undefined && !cleaned" ) 324. unwrap () 325); 326 runtime 327. execute_script ::<()>( "close" , "peer.close();" ) 328. unwrap (); 329 runtime. run_event_loop (). unwrap (); 330collect ( & mut runtime); 331assert! ( 332 runtime 333. execute_script ::< bool >( "released" , "cleaned" ) 334. unwrap () 335); 336} 337}