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:: Cell ; 2use std:: io; 3use std:: rc:: Rc ; 4 5use avril_core::{ Extension , Op , Runtime , RuntimeOptions , v8}; 6 7# [ test ] 8fn raw_ops_receive_positional_values_and_preserve_javascript_exceptions () { 9let mut runtime =Runtime :: new ( RuntimeOptions { 10extensions : vec! [ Extension { 11name : "ext:test" , 12dependencies : & [], 13ops : vec! [ 14Op :: raw ( "invoke" , |scope, args|{ 15let callback = v8:: Local ::< v8:: Function >:: try_from ( args. get ( 0 )). ok () ?; 16let values: Vec < _ > =( 2 ..args. length ()). map ( |index| args. get ( index)). collect (); 17 callback. call ( scope, args. get ( 1 ), & values) 18}), 19Op :: raw ( "count" , |scope, args|{ 20Some ( v8:: Integer :: new ( scope, args. length ()). into ()) 21}), 22Op :: raw ( "empty" , |_, _|None ), 23], 24modules : & [( 25"ext:test/main.js" , 26"import {invoke,count,empty} from 'ext:core/ops'; Object.assign(globalThis,{invoke,count,empty});" , 27)], 28entry_point : Some ( "ext:test/main.js" ), 29}], 30 ..Default :: default () 31}) 32. unwrap (); 33 34 runtime 35. execute_script ::<()>( 36"arguments.js" , 37r#" 38const marker = {toJSON() {throw Error('Serialized arguments');}}; 39const symbol = Symbol('opaque'); 40const array = [1, 2]; 41if (count() !== 0 || count(undefined) !== 1 || count(marker, array, symbol) !== 3) { 42throw Error('Argument count'); 43} 44const result = invoke(function(first, second, third, fourth) { 45if (this !== marker || first !== marker || second !== array || third !== symbol 46|| fourth !== undefined || arguments.length !== 4) throw Error('Argument values'); 47return marker; 48}, marker, marker, array, symbol, undefined); 49if (result !== marker || invoke(() => 42) !== 42 || empty() !== undefined) throw Error('Return value'); 50if (invoke((first, second) => first === undefined && second === undefined, null) !== true) { 51throw Error('Missing arguments'); 52} 53try {invoke(() => {throw marker;}, null); throw Error('Lost exception');} 54catch (error) {if (error !== marker) throw error;} 55if (invoke(() => 7, null) !== 7) throw Error('Poisoned bridge'); 56"# , 57) 58. unwrap (); 59} 60 61# [ test ] 62fn captured_op_state_survives_gc_reentrancy_and_unrelated_isolate_slots () { 63struct Probe ( Rc < Cell < u32 >>); 64impl Drop for Probe { 65fn drop ( & mut self ) { 66self . 0 . set ( self . 0 . get () +1 ); 67} 68} 69 70let drops =Rc :: new ( Cell :: new ( 0 )); 71let mut ops: Vec < _ > =[( "one" , 10 ), ( "two" , 20 )] 72. into_iter () 73. map ( |( name, offset) |{ 74let probe =Probe ( drops. clone ()); 75Op :: raw ( name, move |scope, args|{ 76let value = args. get ( 0 ). uint32_value ( scope) ?; 77assert_eq! ( probe. 0 . get (), 0 ); 78Some ( v8:: Integer :: new_from_unsigned ( scope, value + offset). into ()) 79}) 80}) 81. collect (); 82 ops. push ( Op :: raw ( "collect" , |scope, _|{ 83 scope. set_slot ( Rc ::<[ Op ]>:: from ([])); 84 scope. low_memory_notification (); 85None 86})); 87let mut runtime =Runtime :: new ( RuntimeOptions { 88extensions : vec! [ Extension { 89name : "ext:test" , 90dependencies : & [], 91 ops, 92modules : & [( 93"ext:test/main.js" , 94"import {one,two,collect} from 'ext:core/ops'; Object.assign(globalThis,{one,two,collect});" , 95)], 96entry_point : Some ( "ext:test/main.js" ), 97}], 98 ..Default :: default () 99}) 100. unwrap (); 101 runtime 102. execute_script ::<()>( 103"captures.js" , 104r#" 105if (one({valueOf() {collect(); return two(1);}}) !== 31) throw Error('Reentrant state'); 106for (let i = 0; i < 1000; i++) { 107if (one(i) !== i + 10 || two(i) !== i + 20) throw Error('Shared callback state'); 108} 109const marker = {}; 110try {one({valueOf() {collect(); throw marker;}}); throw Error('Lost exception');} 111catch (error) {if (error !== marker) throw error;} 112if (two(2) !== 22) throw Error('Poisoned state'); 113"# , 114) 115. unwrap (); 116assert_eq! ( drops. get (), 0 ); 117drop ( runtime); 118assert_eq! ( drops. get (), 2 ); 119} 120 121# [ test ] 122fn zero_sized_captures_are_dropped_only_with_the_runtime () { 123thread_local! { 124static DROPS : Cell < u32 > =const { Cell :: new ( 0 ) }; 125} 126# [ repr ( align ( 64 ))] 127struct Probe ; 128impl Drop for Probe { 129fn drop ( & mut self ) { 130DROPS . with ( |drops| drops. set ( drops. get () +1 )); 131} 132} 133 134DROPS . with ( |drops| drops. set ( 0 )); 135let probe =Probe ; 136let op =Op :: raw ( "identity" , move |_, args|{ 137let address = std:: hint:: black_box ( & probe) as * const Probe as usize ; 138assert_eq! ( address % std:: mem:: align_of ::< Probe >(), 0 ); 139Some ( args. get ( 0 )) 140}); 141let mut runtime =Runtime :: new ( RuntimeOptions { 142extensions : vec! [ Extension { 143name : "ext:test" , 144dependencies : & [], 145ops : vec! [ op], 146modules : & [( 147"ext:test/main.js" , 148"import {identity} from 'ext:core/ops'; globalThis.identity = identity;" , 149)], 150entry_point : Some ( "ext:test/main.js" ), 151}], 152 ..Default :: default () 153}) 154. unwrap (); 155assert_eq! ( 156 runtime 157. execute_script ::< u32 >( "zero.js" , "identity(42)" ) 158. unwrap (), 15942 160); 161DROPS . with ( |drops|assert_eq! ( drops. get (), 0 )); 162drop ( runtime); 163DROPS . with ( |drops|assert_eq! ( drops. get (), 1 )); 164} 165 166# [ test ] 167fn fallible_raw_ops_return_opaque_values_and_throw_host_errors () { 168let mut runtime =Runtime :: new ( RuntimeOptions { 169extensions : vec! [ Extension { 170name : "ext:test" , 171dependencies : & [], 172ops : vec! [ Op :: try_raw ( "select" , |_, args|{ 173if args. get ( 0 ). is_true () { 174Ok ( args. get ( 1 )) 175} else { 176Err ( io:: Error :: other ( "Selection failed" )) 177} 178})], 179modules : & [( 180"ext:test/main.js" , 181"import {select} from 'ext:core/ops'; globalThis.select = select;" , 182)], 183entry_point : Some ( "ext:test/main.js" ), 184}], 185 ..Default :: default () 186}) 187. unwrap (); 188 runtime 189. execute_script ::<()>( 190"fallible.js" , 191r#" 192const value = {toJSON() {throw Error('Serialized result');}}; 193if (select(true, value) !== value) throw Error('Lost identity'); 194try {select(false, value); throw Error('Expected failure');} 195catch (error) { 196if (!(error instanceof Error) || error.message !== 'Selection failed') throw error; 197} 198if (select(true, value) !== value) throw Error('Poisoned op'); 199"# , 200) 201. unwrap (); 202} 203 204# [ test ] 205fn typed_op_adapters_still_decode_only_the_first_argument () { 206let mut runtime =Runtime :: new ( RuntimeOptions { 207extensions : vec! [ Extension { 208name : "ext:test" , 209dependencies : & [], 210ops : vec! [ 211Op :: new ( "sum" , |( left, right): ( u32 , u32 ) |{ 212Ok ::< _ , io:: Error >( left + right) 213}), 214Op :: with_scope ( "scoped" , |_, ( left, right): ( u32 , u32 ) |{ 215Ok ::< _ , io:: Error >( left + right) 216}), 217Op :: asynchronous ( "asyncSum" , |( left, right): ( u32 , u32 ) |async move { 218Ok ::< _ , io:: Error >( left + right) 219}), 220Op :: asynchronous_unreferenced ( "unreferenced" , |( left, right): ( u32 , u32 ) |async move { 221Ok ::< _ , io:: Error >( left + right) 222}), 223], 224modules : & [( 225"ext:test/main.js" , 226"import {sum,scoped,asyncSum,unreferenced} from 'ext:core/ops'; Object.assign(globalThis,{sum,scoped,asyncSum,unreferenced});" , 227)], 228entry_point : Some ( "ext:test/main.js" ), 229}], 230 ..Default :: default () 231}) 232. unwrap (); 233 234 runtime 235. execute_script ::<()>( 236"typed.js" , 237r#" 238const extra = Symbol('ignored'); 239globalThis.results = [sum([20, 22], extra), scoped([20, 22], extra)]; 240Promise.all([asyncSum([20, 22], extra), unreferenced([20, 22], extra)]) 241.then(values => results.push(...values)); 242"# , 243) 244. unwrap (); 245 runtime. run_event_loop (). unwrap (); 246assert_eq! ( 247 runtime 248. execute_script ::< Vec < u32 >>( "results.js" , "results" ) 249. unwrap (), 250[ 42 , 42 , 42 , 42 ] 251); 252}