char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7325e8c
main
1use std:: cell::{ Cell , RefCell }; 2use std:: collections:: HashMap ; 3use std:: ffi:: c_void; 4use std:: path::{ Path , PathBuf }; 5use std:: rc:: Rc ; 6use std:: sync:: Arc ; 7 8use avril_caps::{ Capabilities , PathTarget }; 9use avril_core:: serde_v8:: ToJsBuffer ; 10use avril_core::{ Extension , Op , PublicModule , v8}; 11use futures:: channel:: oneshot; 12use libffi:: middle::{ Arg , Cif , CodePtr , Ret , Type }; 13 14# [ cfg ( not ( all ( target_pointer_width = "64" , target_endian = "little" )))] 15compile_error! ( "avril-ffi currently requires a 64-bit little-endian target" ); 16 17// Resource IDs cross into JS as Numbers, so they must stay exact safe integers. 18const MAX_ID : u64 =9_007_199_254_740_991 ; 19 20# [ derive ( Clone , Copy , PartialEq , Eq , Debug )] 21enum Kind { 22Void , 23Bool , 24I8 , 25U8 , 26I16 , 27U16 , 28I32 , 29U32 , 30I64 , 31U64 , 32Isize , 33Usize , 34F32 , 35F64 , 36Pointer , 37Buffer , 38Function , 39} 40 41impl Kind { 42fn parse ( name : & str ) ->Result < Self , String > { 43Ok ( match name{ 44"void" =>Self :: Void , 45"bool" =>Self :: Bool , 46"i8" =>Self :: I8 , 47"u8" =>Self :: U8 , 48"i16" =>Self :: I16 , 49"u16" =>Self :: U16 , 50"i32" =>Self :: I32 , 51"u32" =>Self :: U32 , 52"i64" =>Self :: I64 , 53"u64" =>Self :: U64 , 54"isize" =>Self :: Isize , 55"usize" =>Self :: Usize , 56"f32" =>Self :: F32 , 57"f64" =>Self :: F64 , 58"pointer" =>Self :: Pointer , 59"buffer" =>Self :: Buffer , 60"function" =>Self :: Function , 61 _ =>return Err ( format! ( "Unknown native type: {name}" )), 62}) 63} 64 65fn ffi_type ( self ) ->Type { 66match self { 67Self :: Void =>Type :: void (), 68Self :: Bool |Self :: U8 =>Type :: u8 (), 69Self :: I8 =>Type :: i8 (), 70Self :: U16 =>Type :: u16 (), 71Self :: I16 =>Type :: i16 (), 72Self :: U32 =>Type :: u32 (), 73Self :: I32 =>Type :: i32 (), 74Self :: U64 =>Type :: u64 (), 75Self :: I64 =>Type :: i64 (), 76Self :: Usize =>Type :: usize (), 77Self :: Isize =>Type :: isize (), 78Self :: F32 =>Type :: f32 (), 79Self :: F64 =>Type :: f64 (), 80Self :: Pointer |Self :: Buffer |Self :: Function =>Type :: pointer (), 81} 82} 83} 84 85# [ derive ( Clone , Debug )] 86struct Def { 87params : Vec < Kind >, 88result : Kind , 89} 90 91# [ derive ( serde :: Deserialize )] 92struct DefWire { 93symbol : String , 94params : Vec < String >, 95result : String , 96nonblocking : bool , 97} 98 99struct SymbolState { 100address : usize , 101def : Def , 102} 103 104struct LibraryState { 105_library : libloading:: Library , 106symbols : Vec < SymbolState >, 107} 108 109# [ derive ( Clone , Debug )] 110enum Value { 111Void , 112Bool ( bool ), 113I64 ( i64 ), 114U64 ( u64 ), 115F32 ( f32 ), 116F64 ( f64 ), 117Ptr ( u64 ), 118} 119 120struct Completed { 121outcome : Result < Value , String >, 122buffers : Vec < Vec < u8 >>, 123originals : Vec < Vec < u8 >>, 124targets : Vec < v8:: Global < v8:: Value >>, 125} 126 127struct PendingTargets { 128targets : Vec < v8:: Global < v8:: Value >>, 129originals : Vec < Vec < u8 >>, 130} 131 132type CallReceiver = oneshot:: Receiver < Result <( Value , Vec < Vec < u8 >>), String >>; 133 134struct Registry { 135caps : Arc < Capabilities >, 136libraries : RefCell < HashMap < u64 , Arc < LibraryState >>>, 137next_library : Cell < u64 >, 138receivers : RefCell < HashMap < u64 , CallReceiver >>, 139targets : RefCell < HashMap < u64 , PendingTargets >>, 140completed : Rc < RefCell < HashMap < u64 , Completed >>>, 141next_call : Cell < u64 >, 142} 143 144enum CallError { 145Type ( String ), 146Error ( String ), 147} 148 149impl CallError { 150fn type_ ( message : impl Into < String >) ->Self { 151Self :: Type ( message. into ()) 152} 153fn error ( message : impl Into < String >) ->Self { 154Self :: Error ( message. into ()) 155} 156} 157 158type CallResult < T > =Result < T , CallError >; 159 160fn throw < T >( scope : & mut v8:: PinScope , error : & CallError ) ->Option < T > { 161let ( message, type_error) =match error{ 162CallError :: Type ( message) =>( message, true ), 163CallError :: Error ( message) =>( message, false ), 164}; 165let message = v8:: String :: new ( scope, message) ?; 166let exception =if type_error{ 167 v8:: Exception :: type_error ( scope, message) 168} else { 169 v8:: Exception :: error ( scope, message) 170}; 171 scope. throw_exception ( exception); 172None 173} 174 175fn bigint_u64 ( scope : & mut v8:: PinScope , value : v8:: Local < v8:: Value >) ->CallResult < u64 > { 176if !value. is_big_int () { 177return Err ( CallError :: type_ ( "Expected a bigint" )); 178} 179let bigint = value. to_big_int ( scope). unwrap (); 180let ( value, lossless) = bigint. u64_value (); 181if lossless{ 182return Ok ( value); 183} 184Err ( CallError :: type_ ( "Bigint is out of range" )) 185} 186 187fn bigint_i64 ( scope : & mut v8:: PinScope , value : v8:: Local < v8:: Value >) ->CallResult < i64 > { 188if !value. is_big_int () { 189return Err ( CallError :: type_ ( "Expected a bigint" )); 190} 191let bigint = value. to_big_int ( scope). unwrap (); 192let ( value, lossless) = bigint. i64_value (); 193if lossless{ 194return Ok ( value); 195} 196Err ( CallError :: type_ ( "Bigint is out of range" )) 197} 198 199fn number ( scope : & mut v8:: PinScope , value : v8:: Local < v8:: Value >) ->CallResult < f64 > { 200if !value. is_number () { 201return Err ( CallError :: type_ ( "Expected a number" )); 202} 203 value 204. number_value ( scope) 205. ok_or_else ( ||CallError :: type_ ( "Expected a number" )) 206} 207 208fn integer ( 209scope : & mut v8:: PinScope , 210value : v8:: Local < v8:: Value >, 211min : f64 , 212max : f64 , 213) ->CallResult < i64 > { 214if value. is_big_int () { 215let value =bigint_i64 ( scope, value) ?; 216if ( valueas f64 ) < min ||( valueas f64 ) > max{ 217return Err ( CallError :: Type ( "Integer argument is out of range" . into ())); 218} 219return Ok ( value); 220} 221let value =number ( scope, value) ?; 222if value. fract () !=0.0 || value < min || value > max{ 223return Err ( CallError :: Type ( "Integer argument is out of range" . into ())); 224} 225Ok ( valueas i64 ) 226} 227 228fn view_bytes ( 229scope : & mut v8:: PinScope , 230value : v8:: Local < v8:: Value >, 231) ->CallResult <( * mut u8 , usize )> { 232if let Ok ( view) = v8:: Local ::< v8:: ArrayBufferView >:: try_from ( value) { 233return Ok (( view. data () as * mut u8 , view. byte_length ())); 234} 235if let Ok ( buffer) = v8:: Local ::< v8:: ArrayBuffer >:: try_from ( value) { 236let pointer = buffer 237. data () 238. map ( |pointer| pointer. as_ptr () as * mut u8 ) 239. unwrap_or ( std:: ptr:: null_mut ()); 240return Ok (( pointer, buffer. byte_length ())); 241} 242let _ = scope; 243Err ( CallError :: type_ ( 244"Expected an ArrayBuffer or an ArrayBuffer view" , 245)) 246} 247 248// One 8-byte slot per argument; libffi reads only the bytes of the slot's type. 249# [ derive ( Clone , Copy )] 250struct Slot ( u64 ); 251 252impl Slot { 253fn from_i64 ( value : i64 ) ->Self { 254Self ( valueas u64 ) 255} 256fn from_u64 ( value : u64 ) ->Self { 257Self ( value) 258} 259fn from_f32 ( value : f32 ) ->Self { 260Self ( f32:: to_bits ( value) as u64 ) 261} 262fn from_f64 ( value : f64 ) ->Self { 263Self ( f64:: to_bits ( value)) 264} 265fn from_ptr ( value : * mut c_void ) ->Self { 266Self ( valueas usize as u64 ) 267} 268} 269 270fn convert_arg ( 271scope : & mut v8:: PinScope , 272value : v8:: Local < v8:: Value >, 273kind : Kind , 274nonblocking : bool , 275buffers : & mut Vec < Vec < u8 >>, 276) ->CallResult < Slot > { 277Ok ( match kind{ 278Kind :: Bool =>{ 279if !value. is_boolean () { 280return Err ( CallError :: type_ ( "Expected a boolean" )); 281} 282Slot :: from_u64 ( value. boolean_value ( scope) as u64 ) 283} 284Kind :: I8 =>Slot :: from_i64 ( integer ( scope, value, i8:: MIN as f64 , i8:: MAX as f64 ) ?), 285Kind :: U8 =>Slot :: from_i64 ( integer ( scope, value, 0.0 , u8:: MAX as f64 ) ?), 286Kind :: I16 =>Slot :: from_i64 ( integer ( scope, value, i16:: MIN as f64 , i16:: MAX as f64 ) ?), 287Kind :: U16 =>Slot :: from_i64 ( integer ( scope, value, 0.0 , u16:: MAX as f64 ) ?), 288Kind :: I32 =>Slot :: from_i64 ( integer ( scope, value, i32:: MIN as f64 , i32:: MAX as f64 ) ?), 289Kind :: U32 =>Slot :: from_i64 ( integer ( scope, value, 0.0 , u32:: MAX as f64 ) ?), 290Kind :: I64 |Kind :: Isize =>{ 291if value. is_big_int () { 292Slot :: from_i64 ( bigint_i64 ( scope, value) ?) 293} else { 294Slot :: from_i64 ( integer ( 295 scope, 296 value, 297 -9_007_199_254_740_992.0 , 2989_007_199_254_740_992.0 , 299) ?) 300} 301} 302Kind :: U64 |Kind :: Usize =>{ 303if value. is_big_int () { 304Slot :: from_u64 ( bigint_u64 ( scope, value) ?) 305} else { 306Slot :: from_i64 ( integer ( scope, value, 0.0 , 9_007_199_254_740_992.0 ) ?) 307} 308} 309Kind :: F32 =>Slot :: from_f32 ( number ( scope, value) ?as f32 ), 310Kind :: F64 =>Slot :: from_f64 ( number ( scope, value) ?), 311Kind :: Pointer |Kind :: Function =>{ 312if value. is_null_or_undefined () { 313Slot :: from_ptr ( std:: ptr:: null_mut ()) 314} else if value. is_big_int () { 315Slot :: from_u64 ( bigint_u64 ( scope, value) ?) 316} else if let Ok (( pointer, _)) =view_bytes ( scope, value) { 317if nonblocking{ 318return Err ( CallError :: type_ ( 319"A pointer into a view cannot be used in a nonblocking call; pass an UnsafePointer allocation" , 320)); 321} 322Slot :: from_ptr ( pointeras * mut c_void ) 323} else { 324return Err ( CallError :: type_ ( "Expected a pointer" )); 325} 326} 327Kind :: Buffer =>{ 328let ( pointer, length) =view_bytes ( scope, value) ?; 329if nonblocking{ 330let copy =if length ==0 { 331Vec :: new () 332} else { 333// SAFETY: pointer/length describe this view's live bytes; we copy them out now. 334unsafe { std:: slice:: from_raw_parts ( pointer, length) }. to_vec () 335}; 336 buffers. push ( copy); 337let copy = buffers. last_mut (). unwrap (); 338Slot :: from_ptr ( copy. as_mut_ptr () as * mut c_void ) 339} else { 340Slot :: from_ptr ( pointeras * mut c_void ) 341} 342} 343Kind :: Void =>return Err ( CallError :: type_ ( "void is not a parameter type" )), 344}) 345} 346 347fn call_native ( address : usize , def : & Def , slots : & mut [ Slot ]) ->Value { 348let cif =Cif :: new ( 349 def. params . iter (). map ( |kind| kind. ffi_type ()), 350 def. result . ffi_type (), 351); 352let args: Vec < Arg > = slots. iter (). map ( |slot|Arg :: new ( & slot. 0 )). collect (); 353let function =CodePtr :: from_ptr ( addressas * const c_void ); 354// SAFETY: the caller checked address against def when the library (or the 355// UnsafeFnPointer's creator) was opened; slot bytes match def's types. 356unsafe { 357match def. result { 358Kind :: Void =>{ 359 cif. call_return_into ( function, & args, Ret :: void ()); 360Value :: Void 361} 362Kind :: Bool =>Value :: Bool ( cif. call ::< u8 >( function, & args) !=0 ), 363Kind :: I8 =>Value :: I64 ( cif. call ::< i8 >( function, & args) as i64 ), 364Kind :: U8 =>Value :: U64 ( cif. call ::< u8 >( function, & args) as u64 ), 365Kind :: I16 =>Value :: I64 ( cif. call ::< i16 >( function, & args) as i64 ), 366Kind :: U16 =>Value :: U64 ( cif. call ::< u16 >( function, & args) as u64 ), 367Kind :: I32 =>Value :: I64 ( cif. call ::< i32 >( function, & args) as i64 ), 368Kind :: U32 =>Value :: U64 ( cif. call ::< u32 >( function, & args) as u64 ), 369Kind :: I64 |Kind :: Isize =>Value :: I64 ( cif. call ::< i64 >( function, & args)), 370Kind :: U64 |Kind :: Usize =>Value :: U64 ( cif. call ::< u64 >( function, & args)), 371Kind :: F32 =>Value :: F32 ( cif. call ::< f32 >( function, & args)), 372Kind :: F64 =>Value :: F64 ( cif. call ::< f64 >( function, & args)), 373Kind :: Pointer |Kind :: Function =>{ 374Value :: Ptr ( cif. call ::< * mut c_void >( function, & args) as usize as u64 ) 375} 376Kind :: Buffer =>Value :: Ptr ( cif. call ::< * mut c_void >( function, & args) as usize as u64 ), 377} 378} 379} 380 381fn value_to_v8 < ' s >( 382scope : & mut v8:: PinScope < ' s , ' _ >, 383value : & Value , 384result : Kind , 385) ->Option < v8:: Local < ' s , v8:: Value >> { 386Some ( match ( value, result) { 387( Value :: Void , _) => v8:: undefined ( scope). into (), 388( Value :: Bool ( value), _) => v8:: Boolean :: new ( scope, * value). into (), 389( Value :: I64 ( value), Kind :: I64 |Kind :: Isize ) =>{ 390 v8:: BigInt :: new_from_i64 ( scope, * value). into () 391} 392( Value :: U64 ( value), Kind :: U64 |Kind :: Usize ) =>{ 393 v8:: BigInt :: new_from_u64 ( scope, * value). into () 394} 395( Value :: I64 ( value), _) => v8:: Number :: new ( scope, * valueas f64 ). into (), 396( Value :: U64 ( value), _) => v8:: Number :: new ( scope, * valueas f64 ). into (), 397( Value :: F32 ( value), _) => v8:: Number :: new ( scope, * valueas f64 ). into (), 398( Value :: F64 ( value), _) => v8:: Number :: new ( scope, * value). into (), 399( Value :: Ptr ( value), _) => v8:: BigInt :: new_from_u64 ( scope, * value). into (), 400}) 401} 402 403fn parse_def ( wire : DefWire ) ->Result <( String , Def ), String > { 404let params = wire 405. params 406. iter () 407. map ( |name|Kind :: parse ( name)) 408. collect ::< Result < Vec < _ >, _ >>() ?; 409if params. contains ( & Kind :: Void ) { 410return Err ( "void is not a parameter type" . into ()); 411} 412let result =Kind :: parse ( & wire. result ) ?; 413if result ==Kind :: Buffer { 414return Err ( "buffer is not a result type" . into ()); 415} 416let _ = wire. nonblocking ; 417Ok (( wire. symbol , Def { params, result})) 418} 419 420fn resolve_library ( caps : & Capabilities , specifier : & str ) ->Result < libloading:: Library , CallError > { 421// A bare name goes through the loader's search path; only unrestricted 422// ffi authority covers names whose resolution we cannot check beforehand. 423let bare = !specifier. contains ([ '/' , '\\' ]) &&Path :: new ( specifier). components (). count () ==1 ; 424if let Ok ( url) = url:: Url :: parse ( specifier) 425 && url. scheme () =="file" 426{ 427let path = url 428. to_file_path () 429. map_err ( |_|CallError :: error ( "Invalid file URL" )) ?; 430return load_checked ( caps, & path); 431} 432if bare && !specifier. starts_with ( '.' ) { 433if !caps. unrestricted_ffi () { 434return Err ( CallError :: error ( format! ( 435"PermissionDenied: ffi {specifier}" 436))); 437} 438// SAFETY: loading a library runs its initializers; unrestricted ffi 439// authority is exactly permission to run native code in-process. 440return unsafe { libloading:: Library :: new ( specifier) } 441. map_err ( |error|CallError :: error ( error. to_string ())); 442} 443let path =if Path :: new ( specifier). is_absolute () { 444PathBuf :: from ( specifier) 445} else { 446 caps. cwd (). join ( specifier) 447}; 448load_checked ( caps, & path) 449} 450 451fn load_checked ( caps : & Capabilities , path : & Path ) ->Result < libloading:: Library , CallError > { 452let target = 453PathTarget :: for_executable ( path). map_err ( |error|CallError :: error ( error. to_string ())) ?; 454 caps. check_ffi ( & target) 455. map_err ( |error|CallError :: error ( error. to_string ())) ?; 456// SAFETY: see resolve_library. 457unsafe { libloading:: Library :: new ( target. as_path ()) } 458. map_err ( |error|CallError :: error ( error. to_string ())) 459} 460 461fn id < T >( scope : & mut v8:: PinScope , value : v8:: Local < v8:: Value >) ->CallResult < T > 462where 463T : TryFrom < u64 >, 464{ 465let number =number ( scope, value). map_err ( |_|CallError :: type_ ( "Expected an id" )) ?; 466T :: try_from ( numberas u64 ). map_err ( |_|CallError :: type_ ( "Id is out of range" )) 467} 468 469fn snapshot_args < ' s >( 470scope : & mut v8:: PinScope < ' s , ' _ >, 471values : & v8:: Local < ' s , v8:: Array >, 472) ->CallResult < Vec < v8:: Local < ' s , v8:: Value >>> { 473( 0 ..values. length ()) 474. map ( |index| values. get_index ( scope, index)) 475. collect ::< Option < Vec < _ >>>() 476. ok_or_else ( ||CallError :: type_ ( "Could not read the arguments array" )) 477} 478 479fn authorize_any ( registry : & Registry ) ->CallResult <()> { 480 registry 481. caps 482. check_ffi_any () 483. map_err ( |error|CallError :: error ( error. to_string ())) 484} 485 486pub fn init ( caps : Arc < Capabilities >) ->Extension { 487let registry =Rc :: new ( Registry { 488 caps, 489libraries : RefCell :: new ( HashMap :: new ()), 490next_library : Cell :: new ( 1 ), 491receivers : RefCell :: new ( HashMap :: new ()), 492targets : RefCell :: new ( HashMap :: new ()), 493completed : Rc :: new ( RefCell :: new ( HashMap :: new ())), 494next_call : Cell :: new ( 1 ), 495}); 496 497let dlopen_registry = registry. clone (); 498let close_registry = registry. clone (); 499let call_registry = registry. clone (); 500let begin_registry = registry. clone (); 501let await_registry = registry. clone (); 502let finish_registry = registry. clone (); 503let ptr_registry = registry. clone (); 504let address_registry = registry. clone (); 505let read_registry = registry. clone (); 506let write_registry = registry. clone (); 507let bytes_registry = registry. clone (); 508let write_bytes_registry = registry. clone (); 509let cstring_registry = registry. clone (); 510 511Extension { 512name : "ext:ffi" , 513dependencies : & [], 514ops : vec! [ 515Op :: raw ( "op_ffi_dlopen" , move |scope, args|{ 516let specifier = args. get ( 0 ). to_rust_string_lossy ( scope); 517let wires: Vec < DefWire > =match avril_core:: serde_v8:: from_v8 ( scope, args. get ( 1 )) { 518Ok ( wires) => wires, 519Err ( error) =>{ 520return throw ( scope, & CallError :: type_ ( error. to_string ())); 521} 522}; 523let library =match resolve_library ( & dlopen_registry. caps , & specifier) { 524Ok ( library) => library, 525Err ( error) =>return throw ( scope, & error), 526}; 527let mut symbols =Vec :: new (); 528for wirein wires{ 529let ( name, def) =match parse_def ( wire) { 530Ok ( parsed) => parsed, 531Err ( error) =>return throw ( scope, & CallError :: type_ ( error)), 532}; 533// SAFETY: we only record the symbol's address; it is 534// invoked later through libffi with def's signature. 535let address =unsafe { 536match library. get ::< * const c_void >( name. as_bytes ()) { 537Ok ( symbol) =>match symbol. try_as_raw_ptr () { 538Some ( address) if !address. is_null () => addressas usize , 539Some ( _) =>{ 540return throw ( 541 scope, 542& CallError :: error ( "Symbol resolved to a null address" ), 543); 544} 545None =>{ 546return throw ( 547 scope, 548& CallError :: error ( "Could not resolve symbol" ), 549); 550} 551}, 552Err ( error) =>{ 553return throw ( scope, & CallError :: error ( error. to_string ())); 554} 555} 556}; 557 symbols. push ( SymbolState { address, def}); 558} 559let library_id = dlopen_registry. next_library . get (); 560if library_id >MAX_ID { 561return throw ( scope, & CallError :: error ( "FFI library IDs exhausted" )); 562} 563 dlopen_registry. next_library . set ( library_id +1 ); 564 dlopen_registry. libraries . borrow_mut (). insert ( 565 library_id, 566Arc :: new ( LibraryState { _library : library, symbols}), 567); 568Some ( v8:: Number :: new ( scope, library_idas f64 ). into ()) 569}), 570Op :: raw ( "op_ffi_close" , move |scope, args|{ 571if let Ok ( library_id) =id ::< u64 >( scope, args. get ( 0 )) { 572 close_registry. libraries . borrow_mut (). remove ( & library_id); 573} 574Some ( v8:: undefined ( scope). into ()) 575}), 576Op :: raw ( "op_ffi_call" , move |scope, args|{ 577let result =( || ->CallResult < v8:: Local < v8:: Value >> { 578let library_id =id ::< u64 >( scope, args. get ( 0 )) ?; 579let index =id ::< usize >( scope, args. get ( 1 )) ?; 580let library = call_registry 581. libraries 582. borrow () 583. get ( & library_id) 584. cloned () 585. ok_or_else ( ||CallError :: error ( "Library is closed" )) ?; 586let symbol = library 587. symbols 588. get ( index) 589. ok_or_else ( ||CallError :: error ( "Unknown symbol" )) ?; 590let values = v8:: Local ::< v8:: Array >:: try_from ( args. get ( 2 )) 591. map_err ( |_|CallError :: type_ ( "Expected an arguments array" )) ?; 592if values. length () as usize != symbol. def . params . len () { 593return Err ( CallError :: type_ ( "Wrong number of arguments" )); 594} 595// Read every element before converting any: an accessor 596// could run JS, and no view address may be captured yet. 597let elements =snapshot_args ( scope, & values) ?; 598let mut buffers =Vec :: new (); 599let mut slots =Vec :: new (); 600for ( value, kind) in elements. iter (). zip ( & symbol. def . params ) { 601 slots. push ( convert_arg ( scope, * value, * kind, false , & mut buffers) ?); 602} 603let value =call_native ( symbol. address , & symbol. def , & mut slots); 604value_to_v8 ( scope, & value, symbol. def . result ) 605. ok_or_else ( ||CallError :: error ( "Could not convert the result" )) 606})(); 607match result{ 608Ok ( value) =>Some ( value), 609Err ( error) =>throw ( scope, & error), 610} 611}), 612Op :: raw ( "op_ffi_call_ptr" , move |scope, args|{ 613let result =( || ->CallResult < v8:: Local < v8:: Value >> { 614authorize_any ( & ptr_registry) ?; 615let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 616let wire: DefWire = avril_core:: serde_v8:: from_v8 ( scope, args. get ( 1 )) 617. map_err ( |error|CallError :: type_ ( error. to_string ())) ?; 618let ( _, def) =parse_def ( wire). map_err ( CallError :: type_) ?; 619let values = v8:: Local ::< v8:: Array >:: try_from ( args. get ( 2 )) 620. map_err ( |_|CallError :: type_ ( "Expected an arguments array" )) ?; 621if values. length () as usize != def. params . len () { 622return Err ( CallError :: type_ ( "Wrong number of arguments" )); 623} 624let elements =snapshot_args ( scope, & values) ?; 625let mut buffers =Vec :: new (); 626let mut slots =Vec :: new (); 627for ( value, kind) in elements. iter (). zip ( & def. params ) { 628 slots. push ( convert_arg ( scope, * value, * kind, false , & mut buffers) ?); 629} 630let value =call_native ( address, & def, & mut slots); 631value_to_v8 ( scope, & value, def. result ) 632. ok_or_else ( ||CallError :: error ( "Could not convert the result" )) 633})(); 634match result{ 635Ok ( value) =>Some ( value), 636Err ( error) =>throw ( scope, & error), 637} 638}), 639Op :: raw ( "op_ffi_call_async_begin" , move |scope, args|{ 640let result =( || ->CallResult < u64 > { 641let library_id =id ::< u64 >( scope, args. get ( 0 )) ?; 642let index =id ::< usize >( scope, args. get ( 1 )) ?; 643let library = begin_registry 644. libraries 645. borrow () 646. get ( & library_id) 647. cloned () 648. ok_or_else ( ||CallError :: error ( "Library is closed" )) ?; 649let symbol = library 650. symbols 651. get ( index) 652. ok_or_else ( ||CallError :: error ( "Unknown symbol" )) ?; 653let values = v8:: Local ::< v8:: Array >:: try_from ( args. get ( 2 )) 654. map_err ( |_|CallError :: type_ ( "Expected an arguments array" )) ?; 655if values. length () as usize != symbol. def . params . len () { 656return Err ( CallError :: type_ ( "Wrong number of arguments" )); 657} 658let elements =snapshot_args ( scope, & values) ?; 659let mut buffers: Vec < Vec < u8 >> =Vec :: new (); 660let mut targets =Vec :: new (); 661let mut originals =Vec :: new (); 662let mut slots =Vec :: new (); 663for ( value, kind) in elements. iter (). zip ( & symbol. def . params ) { 664if * kind ==Kind :: Buffer { 665 targets. push ( v8:: Global :: new ( scope, * value)); 666} 667 slots. push ( convert_arg ( scope, * value, * kind, true , & mut buffers) ?); 668if * kind ==Kind :: Buffer { 669 originals. push ( buffers. last (). unwrap (). clone ()); 670} 671} 672let call_id = begin_registry. next_call . get (); 673if call_id >MAX_ID { 674return Err ( CallError :: error ( "FFI call IDs exhausted" )); 675} 676 begin_registry. next_call . set ( call_id +1 ); 677let ( sender, receiver) = oneshot:: channel (); 678 begin_registry 679. receivers 680. borrow_mut () 681. insert ( call_id, receiver); 682 begin_registry 683. targets 684. borrow_mut () 685. insert ( call_id, PendingTargets { targets, originals}); 686let address = symbol. address ; 687let def = symbol. def . clone (); 688let library = library. clone (); 689let spawned = 690 std:: thread:: Builder :: new () 691. name ( "avril:ffi" . into ()) 692. spawn ( move ||{ 693// Keeps the library loaded for the call's duration. 694let _library =& library; 695let mut slots = slots; 696let outcome = 697 std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( ||{ 698( call_native ( address, & def, & mut slots), buffers) 699})); 700let result =match outcome{ 701Ok (( value, buffers)) =>Ok (( value, buffers)), 702Err ( _) =>Err ( "Native call panicked" . to_string ()), 703}; 704let _ = sender. send ( result); 705}); 706if let Err ( error) = spawned{ 707 begin_registry. receivers . borrow_mut (). remove ( & call_id); 708 begin_registry. targets . borrow_mut (). remove ( & call_id); 709return Err ( CallError :: error ( error. to_string ())); 710} 711Ok ( call_id) 712})(); 713match result{ 714Ok ( call_id) =>Some ( v8:: Number :: new ( scope, call_idas f64 ). into ()), 715Err ( error) =>throw ( scope, & error), 716} 717}), 718Op :: asynchronous ( "op_ffi_await" , move |call_id : u64 |{ 719let receiver = await_registry. receivers . borrow_mut (). remove ( & call_id); 720let pending = await_registry. targets . borrow_mut (). remove ( & call_id); 721let ( targets, originals) =match pending{ 722Some ( pending) =>( pending. targets , pending. originals ), 723None =>( Vec :: new (), Vec :: new ()), 724}; 725let completed = await_registry. completed . clone (); 726async move { 727let outcome =match receiver{ 728Some ( receiver) =>match receiver. await { 729Ok ( Ok (( value, buffers))) =>Ok (( value, buffers)), 730Ok ( Err ( error)) =>Err ( error), 731Err ( _) =>Err ( "Native call was dropped" . to_string ()), 732}, 733None =>Err ( "Unknown call" . to_string ()), 734}; 735let ( outcome, buffers) =match outcome{ 736Ok (( value, buffers)) =>( Ok ( value), buffers), 737Err ( error) =>( Err ( error), Vec :: new ()), 738}; 739 completed. borrow_mut (). insert ( 740 call_id, 741Completed { 742 outcome, 743 buffers, 744 originals, 745 targets, 746}, 747); 748Ok ::< _ , std:: io:: Error >( call_id) 749} 750}), 751Op :: raw ( "op_ffi_call_async_finish" , move |scope, args|{ 752let result =( || ->CallResult < v8:: Local < v8:: Value >> { 753let call_id =id ::< u64 >( scope, args. get ( 0 )) ?; 754let result_kind =Kind :: parse ( & args. get ( 1 ). to_rust_string_lossy ( scope)) 755. map_err ( CallError :: type_) ?; 756let completed = finish_registry 757. completed 758. borrow_mut () 759. remove ( & call_id) 760. ok_or_else ( ||CallError :: error ( "Call is not complete" )) ?; 761for (( target, bytes), original) in completed 762. targets 763. iter () 764. zip ( & completed. buffers ) 765. zip ( & completed. originals ) 766{ 767let target = v8:: Local :: new ( scope, target); 768let ( pointer, length) =view_bytes ( scope, target) ?; 769if length != bytes. len () { 770return Err ( CallError :: error ( 771"Buffer changed size during a nonblocking call" , 772)); 773} 774// Only bytes the native call changed are written 775// back, so JS writes to other bytes during the call 776// are not clobbered. SAFETY: pointer/length describe 777// the target view, kept alive via this Global. 778for ( index, ( byte, before)) in bytes. iter (). zip ( original). enumerate () { 779if byte != before{ 780unsafe { * pointer. add ( index) =* byte}; 781} 782} 783} 784let value = completed. outcome . map_err ( CallError :: error) ?; 785value_to_v8 ( scope, & value, result_kind) 786. ok_or_else ( ||CallError :: error ( "Could not convert the result" )) 787})(); 788match result{ 789Ok ( value) =>Some ( value), 790Err ( error) =>throw ( scope, & error), 791} 792}), 793Op :: raw ( "op_ffi_address_of" , move |scope, args|{ 794if let Err ( error) =authorize_any ( & address_registry) { 795return throw ( scope, & error); 796} 797match view_bytes ( scope, args. get ( 0 )) { 798Ok (( pointer, _)) =>{ 799Some ( v8:: BigInt :: new_from_u64 ( scope, pointeras usize as u64 ). into ()) 800} 801Err ( error) =>throw ( scope, & error), 802} 803}), 804Op :: raw ( "op_ffi_read" , move |scope, args|{ 805let result =( || ->CallResult < v8:: Local < v8:: Value >> { 806authorize_any ( & read_registry) ?; 807let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 808let kind =Kind :: parse ( & args. get ( 1 ). to_rust_string_lossy ( scope)) 809. map_err ( CallError :: type_) ?; 810let pointer = addressas * const u8 ; 811// SAFETY: the caller asserts address is readable for kind; 812// that is the documented unsafety of pointer reads. 813let value =unsafe { 814match kind{ 815Kind :: Bool =>Value :: Bool ( * pointer !=0 ), 816Kind :: I8 =>Value :: I64 ( pointer. cast ::< i8 >(). read_unaligned () as i64 ), 817Kind :: U8 =>Value :: U64 ( * pointeras u64 ), 818Kind :: I16 =>Value :: I64 ( pointer. cast ::< i16 >(). read_unaligned () as i64 ), 819Kind :: U16 =>Value :: U64 ( pointer. cast ::< u16 >(). read_unaligned () as u64 ), 820Kind :: I32 =>Value :: I64 ( pointer. cast ::< i32 >(). read_unaligned () as i64 ), 821Kind :: U32 =>Value :: U64 ( pointer. cast ::< u32 >(). read_unaligned () as u64 ), 822Kind :: I64 |Kind :: Isize =>{ 823Value :: I64 ( pointer. cast ::< i64 >(). read_unaligned ()) 824} 825Kind :: U64 |Kind :: Usize =>{ 826Value :: U64 ( pointer. cast ::< u64 >(). read_unaligned ()) 827} 828Kind :: F32 =>Value :: F32 ( pointer. cast ::< f32 >(). read_unaligned ()), 829Kind :: F64 =>Value :: F64 ( pointer. cast ::< f64 >(). read_unaligned ()), 830Kind :: Pointer |Kind :: Function =>{ 831Value :: Ptr ( pointer. cast ::< usize >(). read_unaligned () as u64 ) 832} 833 _ =>{ 834return Err ( CallError :: type_ ( 835"Type cannot be read through a pointer" , 836)); 837} 838} 839}; 840value_to_v8 ( scope, & value, kind) 841. ok_or_else ( ||CallError :: error ( "Could not convert the result" )) 842})(); 843match result{ 844Ok ( value) =>Some ( value), 845Err ( error) =>throw ( scope, & error), 846} 847}), 848Op :: raw ( "op_ffi_write" , move |scope, args|{ 849let result =( || ->CallResult < v8:: Local < v8:: Value >> { 850authorize_any ( & write_registry) ?; 851let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 852let kind =Kind :: parse ( & args. get ( 1 ). to_rust_string_lossy ( scope)) 853. map_err ( CallError :: type_) ?; 854let mut buffers =Vec :: new (); 855let slot =convert_arg ( scope, args. get ( 2 ), kind, false , & mut buffers) ?; 856let size =match kind{ 857Kind :: Bool |Kind :: I8 |Kind :: U8 =>1 , 858Kind :: I16 |Kind :: U16 =>2 , 859Kind :: I32 |Kind :: U32 |Kind :: F32 =>4 , 860Kind :: I64 861 |Kind :: U64 862 |Kind :: Isize 863 |Kind :: Usize 864 |Kind :: F64 865 |Kind :: Pointer 866 |Kind :: Function =>8 , 867 _ =>{ 868return Err ( CallError :: type_ ( 869"Type cannot be written through a pointer" , 870)); 871} 872}; 873// SAFETY: as op_ffi_read, with the caller's writable assert. 874unsafe { 875 std:: ptr:: copy_nonoverlapping ( 876( & slot. 0 as * const u64 ). cast ::< u8 >(), 877 addressas * mut u8 , 878 size, 879); 880} 881Ok ( v8:: undefined ( scope). into ()) 882})(); 883match result{ 884Ok ( value) =>Some ( value), 885Err ( error) =>throw ( scope, & error), 886} 887}), 888Op :: raw ( "op_ffi_read_bytes" , move |scope, args|{ 889let result =( || ->CallResult < v8:: Local < v8:: Value >> { 890authorize_any ( & bytes_registry) ?; 891let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 892let length =id ::< usize >( scope, args. get ( 1 )) ?; 893let bytes =if length ==0 { 894Vec :: new () 895} else { 896// SAFETY: the caller asserts address..address+length is readable. 897unsafe { std:: slice:: from_raw_parts ( addressas * const u8 , length) }. to_vec () 898}; 899 avril_core:: serde_v8:: to_v8 ( scope, ToJsBuffer :: from ( bytes)) 900. map_err ( |error|CallError :: error ( error. to_string ())) 901})(); 902match result{ 903Ok ( value) =>Some ( value), 904Err ( error) =>throw ( scope, & error), 905} 906}), 907Op :: raw ( "op_ffi_write_bytes" , move |scope, args|{ 908let result =( || ->CallResult < v8:: Local < v8:: Value >> { 909authorize_any ( & write_bytes_registry) ?; 910let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 911let ( pointer, length) =view_bytes ( scope, args. get ( 1 )) ?; 912// SAFETY: source is a live view; the caller asserts the 913// destination is writable for length bytes. Ranges may 914// overlap, hence ptr::copy. 915if length >0 { 916unsafe { std:: ptr:: copy ( pointer, addressas * mut u8 , length) }; 917} 918Ok ( v8:: undefined ( scope). into ()) 919})(); 920match result{ 921Ok ( value) =>Some ( value), 922Err ( error) =>throw ( scope, & error), 923} 924}), 925Op :: raw ( "op_ffi_read_cstring" , move |scope, args|{ 926let result =( || ->CallResult < v8:: Local < v8:: Value >> { 927authorize_any ( & cstring_registry) ?; 928let address =bigint_u64 ( scope, args. get ( 0 )) ?as usize ; 929let max =id ::< usize >( scope, args. get ( 1 )) ?; 930let mut bytes =Vec :: new (); 931for offsetin 0 ..max{ 932// SAFETY: the caller asserts a NUL-terminated string 933// at address; we stop at max bytes regardless. 934let byte =unsafe { * ( addressas * const u8 ). add ( offset) }; 935if byte ==0 { 936break ; 937} 938 bytes. push ( byte); 939} 940 avril_core:: serde_v8:: to_v8 ( scope, ToJsBuffer :: from ( bytes)) 941. map_err ( |error|CallError :: error ( error. to_string ())) 942})(); 943match result{ 944Ok ( value) =>Some ( value), 945Err ( error) =>throw ( scope, & error), 946} 947}), 948], 949modules : const { & [ PUBLIC_MODULES [ 0 ]. module ] }, 950entry_point : Some ( "ext:ffi/main.js" ), 951} 952} 953 954pub const PUBLIC_MODULES : & [ PublicModule ] = 955& [ avril_core:: public_module!( "avril:ffi" , "ext:ffi" , "main" )];