char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
e7411ff
main
1use std:: cell:: RefCell ; 2 3use v8::{ ValueDeserializerHelper , ValueSerializerHelper }; 4 5struct Clone { 6classify : v8:: Global < v8:: Function >, 7clone_host : v8:: Global < v8:: Function >, 8error : v8:: Global < v8:: Function >, 9prepare_transfers : Option < v8:: Global < v8:: Function >>, 10validate_transfers : Option < v8:: Global < v8:: Function >>, 11commit_transfers : Option < v8:: Global < v8:: Function >>, 12hosts : RefCell < Vec < v8:: Global < v8:: Object >>>, 13shared : RefCell < Vec < v8:: SharedRef < v8:: BackingStore >>>, 14modules : RefCell < Vec < v8:: CompiledWasmModule >>, 15} 16 17impl Clone { 18fn fail < ' s >( & self , scope : & mut v8:: PinScope < ' s , ' _ >, message : & str ) { 19let message = v8:: String :: new ( scope, message). unwrap (); 20self . throw ( scope, message); 21} 22 23fn throw < ' s >( & self , scope : & mut v8:: PinScope < ' s , ' _ >, message : v8:: Local < ' s , v8:: String >) { 24let error = v8:: Local :: new ( scope, & self . error ); 25let receiver = v8:: undefined ( scope). into (); 26if let Some ( exception) = error. call ( scope, receiver, & [ message. into ()]) { 27 scope. throw_exception ( exception); 28} 29} 30 31fn kind < ' s >( 32& self , 33scope : & mut v8:: PinScope < ' s , ' _ >, 34value : v8:: Local < ' s , v8:: Value >, 35) ->Option < u32 > { 36let classify = v8:: Local :: new ( scope, & self . classify ); 37let receiver = v8:: undefined ( scope). into (); 38 classify 39. call ( scope, receiver, & [ value]) ? 40. uint32_value ( scope) 41} 42} 43 44impl v8:: ValueSerializerImpl for & Clone { 45fn throw_data_clone_error < ' s >( 46& self , 47scope : & mut v8:: PinScope < ' s , ' _ >, 48message : v8:: Local < ' s , v8:: String >, 49) { 50self . throw ( scope, message); 51} 52 53fn has_custom_host_object ( & self , _: & v8:: Isolate ) ->bool { 54true 55} 56 57fn is_host_object < ' s >( 58& self , 59scope : & mut v8:: PinScope < ' s , ' _ >, 60object : v8:: Local < ' s , v8:: Object >, 61) ->Option < bool > { 62self . kind ( scope, object. into ()). map ( |kind| kind !=0 ) 63} 64 65fn write_host_object < ' s >( 66& self , 67scope : & mut v8:: PinScope < ' s , ' _ >, 68object : v8:: Local < ' s , v8:: Object >, 69serializer : & dyn v8:: ValueSerializerHelper , 70) ->Option < bool > { 71let clone_host = v8:: Local :: new ( scope, & self . clone_host ); 72let receiver = v8:: undefined ( scope). into (); 73let value = clone_host. call ( scope, receiver, & [ object. into ()]) ?; 74let Ok ( value) = v8:: Local ::< v8:: Object >:: try_from ( value) else { 75self . fail ( scope, "This object cannot be cloned" ); 76return None ; 77}; 78let mut hosts =self . hosts . borrow_mut (); 79let Ok ( id) = u32:: try_from ( hosts. len ()) else { 80drop ( hosts); 81self . fail ( scope, "Too many serialized host objects" ); 82return None ; 83}; 84 hosts. push ( v8:: Global :: new ( scope, value)); 85 serializer. write_uint32 ( id); 86Some ( true ) 87} 88 89fn get_shared_array_buffer_id < ' s >( 90& self , 91scope : & mut v8:: PinScope < ' s , ' _ >, 92buffer : v8:: Local < ' s , v8:: SharedArrayBuffer >, 93) ->Option < u32 > { 94let mut shared =self . shared . borrow_mut (); 95let Ok ( id) = u32:: try_from ( shared. len ()) else { 96drop ( shared); 97self . fail ( scope, "Too many serialized shared buffers" ); 98return None ; 99}; 100 shared. push ( buffer. get_backing_store ()); 101Some ( id) 102} 103 104fn get_wasm_module_transfer_id ( 105& self , 106scope : & mut v8:: PinScope < ' _ , ' _ >, 107module : v8:: Local < v8:: WasmModuleObject >, 108) ->Option < u32 > { 109let mut modules =self . modules . borrow_mut (); 110let Ok ( id) = u32:: try_from ( modules. len ()) else { 111drop ( modules); 112self . fail ( scope, "Too many serialized WebAssembly modules" ); 113return None ; 114}; 115 modules. push ( module. get_compiled_module ()); 116Some ( id) 117} 118} 119 120impl v8:: ValueDeserializerImpl for & Clone { 121fn read_host_object < ' s >( 122& self , 123scope : & mut v8:: PinScope < ' s , ' _ >, 124deserializer : & dyn v8:: ValueDeserializerHelper , 125) ->Option < v8:: Local < ' s , v8:: Object >> { 126let mut id =0 ; 127if !deserializer. read_uint32 ( & mut id) { 128return None ; 129} 130self . hosts 131. borrow () 132. get ( idas usize ) 133. map ( |object| v8:: Local :: new ( scope, object)) 134} 135 136fn get_shared_array_buffer_from_id < ' s >( 137& self , 138scope : & mut v8:: PinScope < ' s , ' _ >, 139id : u32 , 140) ->Option < v8:: Local < ' s , v8:: SharedArrayBuffer >> { 141self . shared 142. borrow () 143. get ( idas usize ) 144. map ( |store| v8:: SharedArrayBuffer :: with_backing_store ( scope, store)) 145} 146 147fn get_wasm_module_from_id < ' s >( 148& self , 149scope : & mut v8:: PinScope < ' s , ' _ >, 150id : u32 , 151) ->Option < v8:: Local < ' s , v8:: WasmModuleObject >> { 152 v8:: WasmModuleObject :: from_compiled_module ( scope, self . modules . borrow (). get ( idas usize ) ?) 153} 154} 155 156pub ( crate ) fn clone < ' s >( 157scope : & mut v8:: PinScope < ' s , ' _ >, 158args : v8:: FunctionCallbackArguments < ' s >, 159mut result : v8:: ReturnValue , 160) { 161if let Some ( value) =run ( scope, args) { 162 result. set ( value); 163} 164} 165 166fn run < ' s >( 167scope : & mut v8:: PinScope < ' s , ' _ >, 168args : v8:: FunctionCallbackArguments < ' s >, 169) ->Option < v8:: Local < ' s , v8:: Value >> { 170let ( Ok ( transfer), Ok ( classify), Ok ( clone_host), Ok ( error)) =( 171 v8:: Local ::< v8:: Array >:: try_from ( args. get ( 1 )), 172 v8:: Local ::< v8:: Function >:: try_from ( args. get ( 2 )), 173 v8:: Local ::< v8:: Function >:: try_from ( args. get ( 3 )), 174 v8:: Local ::< v8:: Function >:: try_from ( args. get ( 4 )), 175) else { 176let message = v8:: String :: new ( 177 scope, 178"Clone requires a transfer array and policy callbacks" , 179) ?; 180let error = v8:: Exception :: type_error ( scope, message); 181 scope. throw_exception ( error); 182return None ; 183}; 184let hook_values =[ args. get ( 5 ), args. get ( 6 ), args. get ( 7 )]; 185let mut hooks =if hook_values. iter (). all ( |value| value. is_undefined ()) { 186[ None , None , None ] 187} else { 188let Ok ( prepare) = v8:: Local ::< v8:: Function >:: try_from ( hook_values[ 0 ]) else { 189let error = v8:: Exception :: type_error ( 190 scope, 191 v8:: String :: new ( scope, "Invalid transfer hooks" ). unwrap (), 192); 193 scope. throw_exception ( error); 194return None ; 195}; 196let Ok ( validate) = v8:: Local ::< v8:: Function >:: try_from ( hook_values[ 1 ]) else { 197let error = v8:: Exception :: type_error ( 198 scope, 199 v8:: String :: new ( scope, "Invalid transfer hooks" ). unwrap (), 200); 201 scope. throw_exception ( error); 202return None ; 203}; 204let Ok ( commit) = v8:: Local ::< v8:: Function >:: try_from ( hook_values[ 2 ]) else { 205let error = v8:: Exception :: type_error ( 206 scope, 207 v8:: String :: new ( scope, "Invalid transfer hooks" ). unwrap (), 208); 209 scope. throw_exception ( error); 210return None ; 211}; 212[ 213Some ( v8:: Global :: new ( scope, prepare)), 214Some ( v8:: Global :: new ( scope, validate)), 215Some ( v8:: Global :: new ( scope, commit)), 216] 217}; 218let policy =Clone { 219classify : v8:: Global :: new ( scope, classify), 220clone_host : v8:: Global :: new ( scope, clone_host), 221error : v8:: Global :: new ( scope, error), 222prepare_transfers : hooks[ 0 ]. take (), 223validate_transfers : hooks[ 1 ]. take (), 224commit_transfers : hooks[ 2 ]. take (), 225hosts : RefCell :: new ( Vec :: new ()), 226shared : RefCell :: new ( Vec :: new ()), 227modules : RefCell :: new ( Vec :: new ()), 228}; 229let mut buffers =Vec :: new (); 230let seen = v8:: Set :: new ( scope); 231for indexin 0 ..transfer. length () { 232let value = transfer. get_index ( scope, index) ?; 233if seen. has ( scope, value) ?{ 234 policy. fail ( scope, "Transfer list contains a duplicate value" ); 235return None ; 236} 237 seen. add ( scope, value) ?; 238if let Ok ( buffer) = v8:: Local ::< v8:: ArrayBuffer >:: try_from ( value) { 239 buffers. push ( buffer); 240} else if policy. kind ( scope, value) ? !=2 { 241 policy. fail ( scope, "Transfer list contains a non-transferable value" ); 242return None ; 243} 244} 245let receiver = v8:: undefined ( scope). into (); 246if let Some ( callback) =& policy. prepare_transfers { 247let prepare_transfers = v8:: Local :: new ( scope, callback); 248 prepare_transfers. call ( scope, receiver, & [ transfer. into ()]) ?; 249} 250if let Some ( callback) =& policy. validate_transfers { 251let validate_transfers = v8:: Local :: new ( scope, callback); 252 validate_transfers. call ( scope, receiver, & [ transfer. into ()]) ?; 253} 254let serializer = v8:: ValueSerializer :: new ( scope, Box :: new ( & policy)); 255for ( id, buffer) in buffers. iter (). enumerate () { 256 serializer. transfer_array_buffer ( idas u32 , * buffer); 257} 258 serializer. write_header (); 259let context = scope. get_current_context (); 260if serializer. write_value ( context, args. get ( 0 )) !=Some ( true ) { 261return None ; 262} 263if let Some ( callback) =& policy. validate_transfers { 264let validate_transfers = v8:: Local :: new ( scope, callback); 265 validate_transfers. call ( scope, receiver, & [ transfer. into ()]) ?; 266} 267let bytes = serializer. release (); 268let deserializer = v8:: ValueDeserializer :: new ( scope, Box :: new ( & policy), & bytes); 269// Detachment follows successful serialization, in transfer-list order. Getters may 270// have resized or detached later buffers, so this phase can fail after earlier moves. 271for ( id, buffer) in buffers. iter (). enumerate () { 272if buffer. was_detached () { 273 policy. fail ( scope, "Cannot transfer a detached ArrayBuffer" ); 274return None ; 275} 276// V8's Detach silently succeeds on non-detachable buffers (e.g. Wasm memory). 277if !buffer. is_detachable () { 278let message = v8:: String :: new ( scope, "Cannot detach this ArrayBuffer" ) ?; 279let error = v8:: Exception :: type_error ( scope, message); 280 scope. throw_exception ( error); 281return None ; 282} 283let store = buffer. get_backing_store (); 284if buffer. detach ( None ) !=Some ( true ) { 285return None ; 286} 287let moved = v8:: ArrayBuffer :: with_backing_store ( scope, & store); 288 deserializer. transfer_array_buffer ( idas u32 , moved); 289} 290let value ={ 291 v8:: tc_scope!( let caught, scope); 292let value =if deserializer. read_header ( context) ==Some ( true ) { 293 deserializer. read_value ( context) 294} else { 295None 296}; 297if value. is_none () { 298 caught. reset (); 299} 300 value 301}; 302if value. is_none () { 303// V8 cannot reconstruct views invalidated by a transfer-time resize. 304 policy. fail ( 305 scope, 306"not yet implemented: deserializing this cloned value" , 307); 308return None ; 309} 310if let Some ( callback) =& policy. commit_transfers { 311let commit_transfers = v8:: Local :: new ( scope, callback); 312 commit_transfers. call ( scope, receiver, & [ transfer. into ()]) ?; 313} 314 value 315}