char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
81eaeb3
main
1use std:: cell:: RefCell ; 2use std:: io; 3use std:: rc:: Rc ; 4use std:: sync::{ Arc , Weak }; 5 6use avril_core::{ Extension , Op , Runtime , RuntimeOptions , message, v8}; 7 8struct Payload ( u32 ); 9 10fn runtime () ->( Runtime , Rc < RefCell < Vec < Weak < Payload >>>>) { 11let messages =Rc :: new ( RefCell :: new ( Vec ::< Option < message:: Message >>:: new ())); 12let payloads =Rc :: new ( RefCell :: new ( Vec :: new ())); 13let runtime =Runtime :: new ( RuntimeOptions { 14extensions : vec! [ Extension { 15name : "ext:test" , 16dependencies : & [], 17ops : vec! [ 18Op :: with_scope ( "encode_payload" , { 19let payloads = payloads. clone (); 20move |scope, value : u32 |{ 21let payload =Arc :: new ( Payload ( value)); 22 payloads. borrow_mut (). push ( Arc :: downgrade ( & payload)); 23 message:: serialize_host ( scope, payload) 24} 25}), 26Op :: with_scope ( "decode_payload" , |scope, id : u32 |{ 27Ok ::< _ , io:: Error >( message:: deserialize_host ::< Payload >( scope, id) ?. 0 ) 28}), 29Op :: raw ( "send" , { 30let messages = messages. clone (); 31move |scope, args|{ 32let args = v8:: Local ::< v8:: Array >:: try_from ( args. get ( 0 )). ok () ?; 33let value = args. get_index ( scope, 0 ) ?; 34let mut callbacks =Vec :: new (); 35for indexin 1 ..6 { 36 callbacks. push ( v8:: Local ::< v8:: Function >:: try_from ( args. get_index ( scope, index) ?). ok () ?); 37} 38let raw_transfer = args. get_index ( scope, 6 ) ?; 39let transfer = v8:: Local ::< v8:: Array >:: try_from ( raw_transfer). unwrap_or_else ( |_| v8:: Array :: new ( scope, 0 )); 40let packet = message:: serialize ( scope, value, transfer, callbacks. try_into (). ok () ?) ?; 41let mut messages = messages. borrow_mut (); 42let id = messages. len () as u32 ; 43 messages. push ( Some ( packet)); 44Some ( v8:: Integer :: new_from_unsigned ( scope, id). into ()) 45} 46}), 47Op :: new ( "duplicate" , { 48let messages = messages. clone (); 49move |id : usize |{ 50let mut messages = messages. borrow_mut (); 51let copy = messages[ id]. as_ref (). unwrap (). try_clone (); 52let id = copy. map ( |message|{ 53let id = messages. len (); 54 messages. push ( Some ( message)); 55 id 56}); 57Ok ::< _ , std:: convert:: Infallible >( id) 58} 59}), 60Op :: raw ( "receive" , move |scope, args|{ 61let args = v8:: Local ::< v8:: Array >:: try_from ( args. get ( 0 )). ok () ?; 62let id = args. get_index ( scope, 0 ) ?. uint32_value ( scope) ?; 63let host = v8:: Local ::< v8:: Function >:: try_from ( args. get_index ( scope, 1 ) ?). ok () ?; 64let error = v8:: Local ::< v8:: Function >:: try_from ( args. get_index ( scope, 2 ) ?). ok () ?; 65let packet = messages. borrow_mut (). get_mut ( idas usize ) ?. take () ?; 66 message:: deserialize ( scope, packet, host, error) 67}), 68], 69modules : & [( "ext:test/main.js" , r#" 70import {encode_payload,decode_payload,send,receive,duplicate} from 'ext:core/ops'; 71const hosts=new WeakMap(); 72globalThis.makeHost=value=>{const host={};hosts.set(host,value);return host;}; 73globalThis.hostValue=host=>hosts.get(host); 74globalThis.encodePayload=encode_payload; 75globalThis.decodePayload=decode_payload; 76const error=message=>new Error(message); 77globalThis.send=(value,encode=host=>encode_payload(hosts.get(host)))=> 78send([value,host=>hosts.has(host)?1:0,encode,error,()=>{},()=>{}]); 79globalThis.receive=(id,decode=index=>makeHost(decode_payload(index)))=>receive([id,decode,error]); 80globalThis.duplicate=duplicate; 81globalThis.sendTransfer=(value,transfer)=>send([value,()=>0,()=>{},error,()=>{},()=>{},transfer]); 82"# )], 83entry_point : Some ( "ext:test/main.js" ), 84}], 85 ..Default :: default () 86}). unwrap (); 87( runtime, payloads) 88} 89 90# [ test ] 91fn opaque_payloads_live_until_messages_are_consumed_or_dropped () { 92let ( mut runtime, payloads) =runtime (); 93 runtime 94. execute_script ::<()>( "send" , "globalThis.id=send(makeHost(42));" ) 95. unwrap (); 96assert! ( payloads. borrow ()[ 0 ]. upgrade (). is_some ()); 97assert_eq! ( 98 runtime 99. execute_script ::< u32 >( "receive" , "hostValue(receive(id))" ) 100. unwrap (), 10142 102); 103assert! ( payloads. borrow ()[ 0 ]. upgrade (). is_none ()); 104 runtime 105. execute_script ::<()>( "queued" , "send(makeHost(7));" ) 106. unwrap (); 107assert! ( payloads. borrow ()[ 1 ]. upgrade (). is_some ()); 108drop ( runtime); 109assert! ( payloads. borrow ()[ 1 ]. upgrade (). is_none ()); 110} 111 112# [ test ] 113fn failed_and_nested_messages_do_not_leak_or_replace_the_active_payloads () { 114let ( mut runtime, payloads) =runtime (); 115 runtime 116. execute_script ::<()>( 117"nested" , 118r#" 119for(const call of [()=>encodePayload(1),()=>decodePayload(0)]) { 120try{call();throw Error('Allowed outside callback');} 121catch(e){if(!e.message.includes('No matching host serialization callback'))throw e;} 122} 123const id=send(makeHost(42),host=>{ 124try{send({host:makeHost(7),fail(){}});throw Error('Accepted function');} 125catch(e){if(e.message==='Accepted function')throw e;} 126const inner=send(makeHost(11)); 127if(hostValue(receive(inner))!==11)throw Error('Nested payload'); 128return encodePayload(hostValue(host)); 129}); 130const result=receive(id,index=>{ 131const inner=send(makeHost(13)); 132if(hostValue(receive(inner))!==13)throw Error('Nested decode'); 133try{decodePayload(999);throw Error('Accepted invalid index');} 134catch(e){if(!e.message.includes('Invalid host payload'))throw e;} 135return makeHost(decodePayload(index)); 136}); 137if(hostValue(result)!==42)throw Error('Outer payload'); 138try{send({host:makeHost(17),fail(){}});throw Error('Accepted function');} 139catch(e){if(e.message==='Accepted function')throw e;} 140try{encodePayload(1);throw Error('Callback context leaked');} 141catch(e){if(!e.message.includes('No matching host serialization callback'))throw e;} 142"# , 143) 144. unwrap (); 145assert! ( !payloads. borrow (). is_empty ()); 146assert! ( 147 payloads 148. borrow () 149. iter () 150. all ( |payload| payload. upgrade (). is_none ()) 151); 152} 153 154# [ test ] 155fn only_messages_without_transfers_can_be_duplicated () { 156let ( mut runtime, _) =runtime (); 157 runtime. execute_script ::<()>( "copy" , r#" 158const original = send({bytes:new Uint8Array([1,2])}); 159const copy = duplicate(original); 160const a = receive(original), b = receive(copy); 161a.bytes[0] = 9; 162if (b.bytes[0] !== 1) throw Error('Aliased nonshared buffer'); 163const buffer = new ArrayBuffer(4); 164const transferred = sendTransfer(buffer,[buffer]); 165if (buffer.byteLength !== 0 || duplicate(transferred) !== null) throw Error('Duplicated transfer'); 166if (receive(transferred).byteLength !== 4) throw Error('Lost transferred buffer'); 167const unused = new ArrayBuffer(8); 168const withUnusedTransfer = sendTransfer('value',[unused]); 169if (duplicate(withUnusedTransfer) !== null) throw Error('Ignored unused transfer'); 170"# ). unwrap (); 171}