deno.land / x / deno@v1.28.2 / ext / ffi / lib.rs
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use core::ptr::NonNull;use deno_core::anyhow::anyhow;use deno_core::error::generic_error;use deno_core::error::range_error;use deno_core::error::type_error;use deno_core::error::AnyError;use deno_core::futures::channel::mpsc;use deno_core::futures::Future;use deno_core::include_js_files;use deno_core::op;use deno_core::serde_json::Value;use deno_core::serde_v8;use deno_core::v8;use deno_core::CancelFuture;use deno_core::CancelHandle;use deno_core::Extension;use deno_core::OpState;use deno_core::Resource;use deno_core::ResourceId;use dlopen::raw::Library;use libffi::middle::Arg;use libffi::middle::Cif;use serde::Deserialize;use std::borrow::Cow;use std::cell::RefCell;use std::collections::HashMap;use std::ffi::c_void;use std::ffi::CStr;use std::future::IntoFuture;use std::mem::size_of;use std::os::raw::c_char;use std::os::raw::c_short;use std::path::Path;use std::path::PathBuf;use std::pin::Pin;use std::ptr;use std::rc::Rc;use std::sync::mpsc::sync_channel;use std::task::Poll;use std::task::Waker;
mod fast_call;
#[cfg(not(target_pointer_width = "64"))]compile_error!("platform not supported");
const _: () = { assert!(size_of::<c_char>() == 1); assert!(size_of::<c_short>() == 2); assert!(size_of::<*const ()>() == 8);};
thread_local! { static LOCAL_ISOLATE_POINTER: RefCell<*const v8::Isolate> = RefCell::new(ptr::null());}
const MAX_SAFE_INTEGER: isize = 9007199254740991;const MIN_SAFE_INTEGER: isize = -9007199254740991;
pub struct Unstable(pub bool);
fn check_unstable(state: &OpState, api_name: &str) { let unstable = state.borrow::<Unstable>();
if !unstable.0 { eprintln!( "Unstable API '{}'. The --unstable flag must be provided.", api_name ); std::process::exit(70); }}
pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) { let state = state.borrow(); check_unstable(&state, api_name)}
pub trait FfiPermissions { fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError>;}
#[derive(Clone)]struct Symbol { cif: libffi::middle::Cif, ptr: libffi::middle::CodePtr, parameter_types: Vec<NativeType>, result_type: NativeType, can_callback: bool,}
#[allow(clippy::non_send_fields_in_send_ty)]// SAFETY: unsafe trait must have unsafe implementationunsafe impl Send for Symbol {}// SAFETY: unsafe trait must have unsafe implementationunsafe impl Sync for Symbol {}
#[derive(Clone)]struct PtrSymbol { cif: libffi::middle::Cif, ptr: libffi::middle::CodePtr,}
impl PtrSymbol { fn new(fn_ptr: usize, def: &ForeignFunction) -> Self { let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( def .parameters .clone() .into_iter() .map(libffi::middle::Type::from), def.result.into(), );
Self { cif, ptr } }}
#[allow(clippy::non_send_fields_in_send_ty)]// SAFETY: unsafe trait must have unsafe implementationunsafe impl Send for PtrSymbol {}// SAFETY: unsafe trait must have unsafe implementationunsafe impl Sync for PtrSymbol {}
struct DynamicLibraryResource { lib: Library, symbols: HashMap<String, Box<Symbol>>,}
impl Resource for DynamicLibraryResource { fn name(&self) -> Cow<str> { "dynamicLibrary".into() }
fn close(self: Rc<Self>) { drop(self) }}
impl DynamicLibraryResource { fn get_static(&self, symbol: String) -> Result<*const c_void, AnyError> { // By default, Err returned by this function does not tell // which symbol wasn't exported. So we'll modify the error // message to include the name of symbol. // // SAFETY: The obtained T symbol is the size of a pointer. match unsafe { self.lib.symbol::<*const c_void>(&symbol) } { Ok(value) => Ok(Ok(value)), Err(err) => Err(generic_error(format!( "Failed to register symbol {}: {}", symbol, err ))), }? }}
type PendingFfiAsyncWork = Box<dyn FnOnce()>;
struct FfiState { async_work_sender: mpsc::UnboundedSender<PendingFfiAsyncWork>, async_work_receiver: mpsc::UnboundedReceiver<PendingFfiAsyncWork>,}
pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension { Extension::builder() .js(include_js_files!( prefix "deno:ext/ffi", "00_ffi.js", )) .ops(vec![ op_ffi_load::decl::<P>(), op_ffi_get_static::decl(), op_ffi_call_nonblocking::decl(), op_ffi_call_ptr::decl::<P>(), op_ffi_call_ptr_nonblocking::decl::<P>(), op_ffi_ptr_of::decl::<P>(), op_ffi_get_buf::decl::<P>(), op_ffi_buf_copy_into::decl::<P>(), op_ffi_cstr_read::decl::<P>(), op_ffi_read_bool::decl::<P>(), op_ffi_read_u8::decl::<P>(), op_ffi_read_i8::decl::<P>(), op_ffi_read_u16::decl::<P>(), op_ffi_read_i16::decl::<P>(), op_ffi_read_u32::decl::<P>(), op_ffi_read_i32::decl::<P>(), op_ffi_read_u64::decl::<P>(), op_ffi_read_i64::decl::<P>(), op_ffi_read_f32::decl::<P>(), op_ffi_read_f64::decl::<P>(), op_ffi_unsafe_callback_create::decl::<P>(), op_ffi_unsafe_callback_ref::decl(), op_ffi_unsafe_callback_unref::decl(), ]) .event_loop_middleware(|op_state_rc, _cx| { // FFI callbacks coming in from other threads will call in and get queued. let mut maybe_scheduling = false;
let mut work_items: Vec<PendingFfiAsyncWork> = vec![];
{ let mut op_state = op_state_rc.borrow_mut(); let ffi_state = op_state.borrow_mut::<FfiState>();
while let Ok(Some(async_work_fut)) = ffi_state.async_work_receiver.try_next() { // Move received items to a temporary vector so that we can drop the `op_state` borrow before we do the work. work_items.push(async_work_fut); maybe_scheduling = true; }
drop(op_state); } while let Some(async_work_fut) = work_items.pop() { async_work_fut(); }
maybe_scheduling }) .state(move |state| { // Stolen from deno_webgpu, is there a better option? state.put(Unstable(unstable));
let (async_work_sender, async_work_receiver) = mpsc::unbounded::<PendingFfiAsyncWork>();
state.put(FfiState { async_work_receiver, async_work_sender, });
Ok(()) }) .build()}
/// Defines the accepted types that can be used as/// parameters and return values in FFI.#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]#[serde(rename_all = "lowercase")]enum NativeType { Void, Bool, U8, I8, U16, I16, U32, I32, U64, I64, USize, ISize, F32, F64, Pointer, Buffer, Function,}
impl From<NativeType> for libffi::middle::Type { fn from(native_type: NativeType) -> Self { match native_type { NativeType::Void => libffi::middle::Type::void(), NativeType::U8 | NativeType::Bool => libffi::middle::Type::u8(), NativeType::I8 => libffi::middle::Type::i8(), NativeType::U16 => libffi::middle::Type::u16(), NativeType::I16 => libffi::middle::Type::i16(), NativeType::U32 => libffi::middle::Type::u32(), NativeType::I32 => libffi::middle::Type::i32(), NativeType::U64 => libffi::middle::Type::u64(), NativeType::I64 => libffi::middle::Type::i64(), NativeType::USize => libffi::middle::Type::usize(), NativeType::ISize => libffi::middle::Type::isize(), NativeType::F32 => libffi::middle::Type::f32(), NativeType::F64 => libffi::middle::Type::f64(), NativeType::Pointer | NativeType::Buffer | NativeType::Function => { libffi::middle::Type::pointer() } } }}
/// Intermediate format for easy translation from NativeType + V8 value/// to libffi argument types.#[repr(C)]union NativeValue { void_value: (), bool_value: bool, u8_value: u8, i8_value: i8, u16_value: u16, i16_value: i16, u32_value: u32, i32_value: i32, u64_value: u64, i64_value: i64, usize_value: usize, isize_value: isize, f32_value: f32, f64_value: f64, pointer: *const u8,}
impl NativeValue { unsafe fn as_arg(&self, native_type: NativeType) -> Arg { match native_type { NativeType::Void => unreachable!(), NativeType::Bool => Arg::new(&self.bool_value), NativeType::U8 => Arg::new(&self.u8_value), NativeType::I8 => Arg::new(&self.i8_value), NativeType::U16 => Arg::new(&self.u16_value), NativeType::I16 => Arg::new(&self.i16_value), NativeType::U32 => Arg::new(&self.u32_value), NativeType::I32 => Arg::new(&self.i32_value), NativeType::U64 => Arg::new(&self.u64_value), NativeType::I64 => Arg::new(&self.i64_value), NativeType::USize => Arg::new(&self.usize_value), NativeType::ISize => Arg::new(&self.isize_value), NativeType::F32 => Arg::new(&self.f32_value), NativeType::F64 => Arg::new(&self.f64_value), NativeType::Pointer | NativeType::Buffer | NativeType::Function => { Arg::new(&self.pointer) } } }
// SAFETY: native_type must correspond to the type of value represented by the union field unsafe fn to_value(&self, native_type: NativeType) -> Value { match native_type { NativeType::Void => Value::Null, NativeType::Bool => Value::from(self.bool_value), NativeType::U8 => Value::from(self.u8_value), NativeType::I8 => Value::from(self.i8_value), NativeType::U16 => Value::from(self.u16_value), NativeType::I16 => Value::from(self.i16_value), NativeType::U32 => Value::from(self.u32_value), NativeType::I32 => Value::from(self.i32_value), NativeType::U64 => Value::from(self.u64_value), NativeType::I64 => Value::from(self.i64_value), NativeType::USize => Value::from(self.usize_value), NativeType::ISize => Value::from(self.isize_value), NativeType::F32 => Value::from(self.f32_value), NativeType::F64 => Value::from(self.f64_value), NativeType::Pointer | NativeType::Function | NativeType::Buffer => { Value::from(self.pointer as usize) } } }
// SAFETY: native_type must correspond to the type of value represented by the union field #[inline] unsafe fn to_v8<'scope>( &self, scope: &mut v8::HandleScope<'scope>, native_type: NativeType, ) -> serde_v8::Value<'scope> { match native_type { NativeType::Void => { let local_value: v8::Local<v8::Value> = v8::undefined(scope).into(); local_value.into() } NativeType::Bool => { let local_value: v8::Local<v8::Value> = v8::Boolean::new(scope, self.bool_value).into(); local_value.into() } NativeType::U8 => { let local_value: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, self.u8_value as u32).into(); local_value.into() } NativeType::I8 => { let local_value: v8::Local<v8::Value> = v8::Integer::new(scope, self.i8_value as i32).into(); local_value.into() } NativeType::U16 => { let local_value: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, self.u16_value as u32).into(); local_value.into() } NativeType::I16 => { let local_value: v8::Local<v8::Value> = v8::Integer::new(scope, self.i16_value as i32).into(); local_value.into() } NativeType::U32 => { let local_value: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, self.u32_value).into(); local_value.into() } NativeType::I32 => { let local_value: v8::Local<v8::Value> = v8::Integer::new(scope, self.i32_value).into(); local_value.into() } NativeType::U64 => { let value = self.u64_value; let local_value: v8::Local<v8::Value> = if value > MAX_SAFE_INTEGER as u64 { v8::BigInt::new_from_u64(scope, value).into() } else { v8::Number::new(scope, value as f64).into() }; local_value.into() } NativeType::I64 => { let value = self.i64_value; let local_value: v8::Local<v8::Value> = if value > MAX_SAFE_INTEGER as i64 || value < MIN_SAFE_INTEGER as i64 { v8::BigInt::new_from_i64(scope, self.i64_value).into() } else { v8::Number::new(scope, value as f64).into() }; local_value.into() } NativeType::USize => { let value = self.usize_value; let local_value: v8::Local<v8::Value> = if value > MAX_SAFE_INTEGER as usize { v8::BigInt::new_from_u64(scope, value as u64).into() } else { v8::Number::new(scope, value as f64).into() }; local_value.into() } NativeType::ISize => { let value = self.isize_value; let local_value: v8::Local<v8::Value> = if !(MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&value) { v8::BigInt::new_from_i64(scope, self.isize_value as i64).into() } else { v8::Number::new(scope, value as f64).into() }; local_value.into() } NativeType::F32 => { let local_value: v8::Local<v8::Value> = v8::Number::new(scope, self.f32_value as f64).into(); local_value.into() } NativeType::F64 => { let local_value: v8::Local<v8::Value> = v8::Number::new(scope, self.f64_value).into(); local_value.into() } NativeType::Pointer | NativeType::Buffer | NativeType::Function => { let value = self.pointer as u64; let local_value: v8::Local<v8::Value> = if value > MAX_SAFE_INTEGER as u64 { v8::BigInt::new_from_u64(scope, value).into() } else { v8::Number::new(scope, value as f64).into() }; local_value.into() } } }}
// SAFETY: unsafe trait must have unsafe implementationunsafe impl Send for NativeValue {}
#[derive(Deserialize, Debug)]#[serde(rename_all = "camelCase")]struct ForeignFunction { name: Option<String>, parameters: Vec<NativeType>, result: NativeType, #[serde(rename = "nonblocking")] non_blocking: Option<bool>, #[serde(rename = "callback")] #[serde(default = "default_callback")] callback: bool,}
fn default_callback() -> bool { false}
// ForeignStatic's name and type fields are read and used by// serde_v8 to determine which variant a ForeignSymbol is.// They are not used beyond that and are thus marked with underscores.#[derive(Deserialize, Debug)]struct ForeignStatic { #[serde(rename(deserialize = "name"))] _name: Option<String>, #[serde(rename(deserialize = "type"))] _type: String,}
#[derive(Deserialize, Debug)]#[serde(untagged)]enum ForeignSymbol { ForeignFunction(ForeignFunction), ForeignStatic(ForeignStatic),}
#[derive(Deserialize, Debug)]struct FfiLoadArgs { path: String, symbols: HashMap<String, ForeignSymbol>,}
// `path` is only used on Windows.#[allow(unused_variables)]pub(crate) fn format_error(e: dlopen::Error, path: String) -> String { match e { #[cfg(target_os = "windows")] // This calls FormatMessageW with library path // as replacement for the insert sequences. // Unlike libstd which passes the FORMAT_MESSAGE_IGNORE_INSERTS // flag without any arguments. // // https://github.com/denoland/deno/issues/11632 dlopen::Error::OpeningLibraryError(e) => { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use winapi::shared::minwindef::DWORD; use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; use winapi::um::errhandlingapi::GetLastError; use winapi::um::winbase::FormatMessageW; use winapi::um::winbase::FORMAT_MESSAGE_ARGUMENT_ARRAY; use winapi::um::winbase::FORMAT_MESSAGE_FROM_SYSTEM; use winapi::um::winnt::LANG_SYSTEM_DEFAULT; use winapi::um::winnt::MAKELANGID; use winapi::um::winnt::SUBLANG_SYS_DEFAULT;
let err_num = match e.raw_os_error() { Some(err_num) => err_num, // This should never hit unless dlopen changes its error type. None => return e.to_string(), };
// Language ID (0x0800) let lang_id = MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) as DWORD;
let mut buf = vec![0; 500];
let path = OsStr::new(&path) .encode_wide() .chain(Some(0).into_iter()) .collect::<Vec<_>>();
let arguments = [path.as_ptr()];
loop { // SAFETY: // winapi call to format the error message let length = unsafe { FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, std::ptr::null_mut(), err_num as DWORD, lang_id as DWORD, buf.as_mut_ptr(), buf.len() as DWORD, arguments.as_ptr() as _, ) };
if length == 0 { // SAFETY: // winapi call to get the last error message let err_num = unsafe { GetLastError() }; if err_num == ERROR_INSUFFICIENT_BUFFER { buf.resize(buf.len() * 2, 0); continue; }
// Something went wrong, just return the original error. return e.to_string(); }
let msg = String::from_utf16_lossy(&buf[..length as usize]); return msg; } } _ => e.to_string(), }}
#[op(v8)]fn op_ffi_load<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, args: FfiLoadArgs,) -> Result<(ResourceId, serde_v8::Value<'scope>), AnyError>where FP: FfiPermissions + 'static,{ let path = args.path;
check_unstable(state, "Deno.dlopen"); let permissions = state.borrow_mut::<FP>(); permissions.check(Some(&PathBuf::from(&path)))?;
let lib = Library::open(&path).map_err(|e| { dlopen::Error::OpeningLibraryError(std::io::Error::new( std::io::ErrorKind::Other, format_error(e, path), )) })?; let mut resource = DynamicLibraryResource { lib, symbols: HashMap::new(), }; let obj = v8::Object::new(scope);
for (symbol_key, foreign_symbol) in args.symbols { match foreign_symbol { ForeignSymbol::ForeignStatic(_) => { // No-op: Statics will be handled separately and are not part of the Rust-side resource. } ForeignSymbol::ForeignFunction(foreign_fn) => { let symbol = match &foreign_fn.name { Some(symbol) => symbol, None => &symbol_key, }; // By default, Err returned by this function does not tell // which symbol wasn't exported. So we'll modify the error // message to include the name of symbol. let fn_ptr = // SAFETY: The obtained T symbol is the size of a pointer. match unsafe { resource.lib.symbol::<*const c_void>(symbol) } { Ok(value) => Ok(value), Err(err) => Err(generic_error(format!( "Failed to register symbol {}: {}", symbol, err ))), }?; let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _); let cif = libffi::middle::Cif::new( foreign_fn .parameters .clone() .into_iter() .map(libffi::middle::Type::from), foreign_fn.result.into(), );
let func_key = v8::String::new(scope, &symbol_key).unwrap(); let sym = Box::new(Symbol { cif, ptr, parameter_types: foreign_fn.parameters, result_type: foreign_fn.result, can_callback: foreign_fn.callback, });
resource.symbols.insert(symbol_key, sym.clone()); match foreign_fn.non_blocking { // Generate functions for synchronous calls. Some(false) | None => { let function = make_sync_fn(scope, sym); obj.set(scope, func_key.into(), function.into()); } // This optimization is not yet supported for non-blocking calls. _ => {} }; } } }
let rid = state.resource_table.add(resource); Ok(( rid, serde_v8::Value { v8_value: obj.into(), }, ))}
fn needs_unwrap(rv: NativeType) -> bool { matches!( rv, NativeType::Function | NativeType::Pointer | NativeType::Buffer | NativeType::I64 | NativeType::ISize | NativeType::U64 | NativeType::USize )}
fn is_i64(rv: NativeType) -> bool { matches!(rv, NativeType::I64 | NativeType::ISize)}
// Create a JavaScript function for synchronous FFI call to// the given symbol.fn make_sync_fn<'s>( scope: &mut v8::HandleScope<'s>, sym: Box<Symbol>,) -> v8::Local<'s, v8::Function> { let sym = Box::leak(sym); let builder = v8::FunctionTemplate::builder( |scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue| { let external: v8::Local<v8::External> = args.data().try_into().unwrap(); // SAFETY: The pointer will not be deallocated until the function is // garbage collected. let symbol = unsafe { &*(external.value() as *const Symbol) }; let needs_unwrap = match needs_unwrap(symbol.result_type) { true => Some(args.get(symbol.parameter_types.len() as i32)), false => None, }; match ffi_call_sync(scope, args, symbol) { Ok(result) => { match needs_unwrap { Some(v) => { let view: v8::Local<v8::ArrayBufferView> = v.try_into().unwrap(); let backing_store = view.buffer(scope).unwrap().get_backing_store();
if is_i64(symbol.result_type) { // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, // it points to a fixed continuous slice of bytes on the heap. let bs = unsafe { &mut *(&backing_store[..] as *const _ as *mut [u8] as *mut i64) }; // SAFETY: We already checked that type == I64 let value = unsafe { result.i64_value }; *bs = value; } else { // SAFETY: v8::SharedRef<v8::BackingStore> is similar to Arc<[u8]>, // it points to a fixed continuous slice of bytes on the heap. let bs = unsafe { &mut *(&backing_store[..] as *const _ as *mut [u8] as *mut u64) }; // SAFETY: We checked that type == U64 let value = unsafe { result.u64_value }; *bs = value; } } None => { // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. let result = unsafe { result.to_v8(scope, symbol.result_type) }; rv.set(result.v8_value); } } } Err(err) => { deno_core::_ops::throw_type_error(scope, err.to_string()); } }; }, ) .data(v8::External::new(scope, sym as *mut Symbol as *mut _).into());
let mut fast_call_alloc = None;
let func = if fast_call::is_compatible(sym) { let trampoline = fast_call::compile_trampoline(sym); let func = builder.build_fast( scope, &fast_call::make_template(sym, &trampoline), None, ); fast_call_alloc = Some(Box::into_raw(Box::new(trampoline))); func } else { builder.build(scope) }; let func = func.get_function(scope).unwrap();
let weak = v8::Weak::with_finalizer( scope, func, Box::new(move |_| { // SAFETY: This is never called twice. pointer obtained // from Box::into_raw, hence, satisfies memory layout requirements. let _ = unsafe { Box::from_raw(sym) }; if let Some(fast_call_ptr) = fast_call_alloc { // fast-call compiled trampoline is unmapped when the MMAP handle is dropped // SAFETY: This is never called twice. pointer obtained // from Box::into_raw, hence, satisfies memory layout requirements. let _ = unsafe { Box::from_raw(fast_call_ptr) }; } }), );
weak.to_local(scope).unwrap()}
#[inline]fn ffi_parse_bool_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let bool_value = v8::Local::<v8::Boolean>::try_from(arg) .map_err(|_| type_error("Invalid FFI u8 type, expected boolean"))? .is_true(); Ok(NativeValue { bool_value })}
#[inline]fn ffi_parse_u8_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let u8_value = v8::Local::<v8::Uint32>::try_from(arg) .map_err(|_| type_error("Invalid FFI u8 type, expected unsigned integer"))? .value() as u8; Ok(NativeValue { u8_value })}
#[inline]fn ffi_parse_i8_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let i8_value = v8::Local::<v8::Int32>::try_from(arg) .map_err(|_| type_error("Invalid FFI i8 type, expected integer"))? .value() as i8; Ok(NativeValue { i8_value })}
#[inline]fn ffi_parse_u16_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let u16_value = v8::Local::<v8::Uint32>::try_from(arg) .map_err(|_| type_error("Invalid FFI u16 type, expected unsigned integer"))? .value() as u16; Ok(NativeValue { u16_value })}
#[inline]fn ffi_parse_i16_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let i16_value = v8::Local::<v8::Int32>::try_from(arg) .map_err(|_| type_error("Invalid FFI i16 type, expected integer"))? .value() as i16; Ok(NativeValue { i16_value })}
#[inline]fn ffi_parse_u32_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let u32_value = v8::Local::<v8::Uint32>::try_from(arg) .map_err(|_| type_error("Invalid FFI u32 type, expected unsigned integer"))? .value() as u32; Ok(NativeValue { u32_value })}
#[inline]fn ffi_parse_i32_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let i32_value = v8::Local::<v8::Int32>::try_from(arg) .map_err(|_| type_error("Invalid FFI i32 type, expected integer"))? .value() as i32; Ok(NativeValue { i32_value })}
#[inline]fn ffi_parse_u64_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case. // 2. Number: Common, supported by Fast API, so let that be the optimal case. let u64_value: u64 = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.u64_value().0 } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as u64 } else { return Err(type_error( "Invalid FFI u64 type, expected unsigned integer", )); }; Ok(NativeValue { u64_value })}
#[inline]fn ffi_parse_i64_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case. // 2. Number: Common, supported by Fast API, so let that be the optimal case. let i64_value: i64 = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.i64_value().0 } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as i64 } else { return Err(type_error("Invalid FFI i64 type, expected integer")); }; Ok(NativeValue { i64_value })}
#[inline]fn ffi_parse_usize_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case. // 2. Number: Common, supported by Fast API, so let that be the optimal case. let usize_value: usize = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.u64_value().0 as usize } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as usize } else { return Err(type_error("Invalid FFI usize type, expected integer")); }; Ok(NativeValue { usize_value })}
#[inline]fn ffi_parse_isize_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, so optimise slow call for this case. // 2. Number: Common, supported by Fast API, so let that be the optimal case. let isize_value: isize = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.i64_value().0 as isize } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as isize } else { return Err(type_error("Invalid FFI isize type, expected integer")); }; Ok(NativeValue { isize_value })}
#[inline]fn ffi_parse_f32_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let f32_value = v8::Local::<v8::Number>::try_from(arg) .map_err(|_| type_error("Invalid FFI f32 type, expected number"))? .value() as f32; Ok(NativeValue { f32_value })}
#[inline]fn ffi_parse_f64_arg( arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { let f64_value = v8::Local::<v8::Number>::try_from(arg) .map_err(|_| type_error("Invalid FFI f64 type, expected number"))? .value() as f64; Ok(NativeValue { f64_value })}
#[inline]fn ffi_parse_pointer_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, optimise this case. // 2. Number: Common and supported by Fast API. // 3. Null: Very uncommon / can be represented by a 0. let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.u64_value().0 as usize as *const u8 } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as usize as *const u8 } else if arg.is_null() { ptr::null() } else { return Err(type_error( "Invalid FFI pointer type, expected null, integer or BigInt", )); }; Ok(NativeValue { pointer })}
#[inline]fn ffi_parse_buffer_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. ArrayBuffer: Fairly common and not supported by Fast API, optimise this case. // 2. ArrayBufferView: Common and supported by Fast API // 5. Null: Very uncommon / can be represented by a 0.
let pointer = if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(arg) { let backing_store = value.get_backing_store(); &backing_store[..] as *const _ as *const u8 } else if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(arg) { let byte_offset = value.byte_offset(); let backing_store = value .buffer(scope) .ok_or_else(|| { type_error("Invalid FFI ArrayBufferView, expected data in the buffer") })? .get_backing_store(); &backing_store[byte_offset..] as *const _ as *const u8 } else if arg.is_null() { ptr::null() } else { return Err(type_error( "Invalid FFI buffer type, expected null, ArrayBuffer, or ArrayBufferView", )); }; Ok(NativeValue { pointer })}
#[inline]fn ffi_parse_function_arg( scope: &mut v8::HandleScope, arg: v8::Local<v8::Value>,) -> Result<NativeValue, AnyError> { // Order of checking: // 1. BigInt: Uncommon and not supported by Fast API, optimise this case. // 2. Number: Common and supported by Fast API, optimise this case as second. // 3. Null: Very uncommon / can be represented by a 0. let pointer = if let Ok(value) = v8::Local::<v8::BigInt>::try_from(arg) { value.u64_value().0 as usize as *const u8 } else if let Ok(value) = v8::Local::<v8::Number>::try_from(arg) { value.integer_value(scope).unwrap() as usize as *const u8 } else if arg.is_null() { ptr::null() } else { return Err(type_error( "Invalid FFI function type, expected null, integer, or BigInt", )); }; Ok(NativeValue { pointer })}
fn ffi_parse_args<'scope>( scope: &mut v8::HandleScope<'scope>, args: serde_v8::Value<'scope>, parameter_types: &[NativeType],) -> Result<Vec<NativeValue>, AnyError>where 'scope: 'scope,{ if parameter_types.is_empty() { return Ok(vec![]); }
let args = v8::Local::<v8::Array>::try_from(args.v8_value) .map_err(|_| type_error("Invalid FFI parameters, expected Array"))?; let mut ffi_args: Vec<NativeValue> = Vec::with_capacity(parameter_types.len());
for (index, native_type) in parameter_types.iter().enumerate() { let value = args.get_index(scope, index as u32).unwrap(); match native_type { NativeType::Bool => { ffi_args.push(ffi_parse_bool_arg(value)?); } NativeType::U8 => { ffi_args.push(ffi_parse_u8_arg(value)?); } NativeType::I8 => { ffi_args.push(ffi_parse_i8_arg(value)?); } NativeType::U16 => { ffi_args.push(ffi_parse_u16_arg(value)?); } NativeType::I16 => { ffi_args.push(ffi_parse_i16_arg(value)?); } NativeType::U32 => { ffi_args.push(ffi_parse_u32_arg(value)?); } NativeType::I32 => { ffi_args.push(ffi_parse_i32_arg(value)?); } NativeType::U64 => { ffi_args.push(ffi_parse_u64_arg(scope, value)?); } NativeType::I64 => { ffi_args.push(ffi_parse_i64_arg(scope, value)?); } NativeType::USize => { ffi_args.push(ffi_parse_usize_arg(scope, value)?); } NativeType::ISize => { ffi_args.push(ffi_parse_isize_arg(scope, value)?); } NativeType::F32 => { ffi_args.push(ffi_parse_f32_arg(value)?); } NativeType::F64 => { ffi_args.push(ffi_parse_f64_arg(value)?); } NativeType::Buffer => { ffi_args.push(ffi_parse_buffer_arg(scope, value)?); } NativeType::Pointer => { ffi_args.push(ffi_parse_pointer_arg(scope, value)?); } NativeType::Function => { ffi_args.push(ffi_parse_function_arg(scope, value)?); } NativeType::Void => { unreachable!(); } } }
Ok(ffi_args)}
// A one-off synchronous FFI call.fn ffi_call_sync<'scope>( scope: &mut v8::HandleScope<'scope>, args: v8::FunctionCallbackArguments, symbol: &Symbol,) -> Result<NativeValue, AnyError>where 'scope: 'scope,{ let Symbol { parameter_types, result_type, cif, ptr: fun_ptr, .. } = symbol; let mut ffi_args: Vec<NativeValue> = Vec::with_capacity(parameter_types.len());
for (index, native_type) in parameter_types.iter().enumerate() { let value = args.get(index as i32); match native_type { NativeType::Bool => { ffi_args.push(ffi_parse_bool_arg(value)?); } NativeType::U8 => { ffi_args.push(ffi_parse_u8_arg(value)?); } NativeType::I8 => { ffi_args.push(ffi_parse_i8_arg(value)?); } NativeType::U16 => { ffi_args.push(ffi_parse_u16_arg(value)?); } NativeType::I16 => { ffi_args.push(ffi_parse_i16_arg(value)?); } NativeType::U32 => { ffi_args.push(ffi_parse_u32_arg(value)?); } NativeType::I32 => { ffi_args.push(ffi_parse_i32_arg(value)?); } NativeType::U64 => { ffi_args.push(ffi_parse_u64_arg(scope, value)?); } NativeType::I64 => { ffi_args.push(ffi_parse_i64_arg(scope, value)?); } NativeType::USize => { ffi_args.push(ffi_parse_usize_arg(scope, value)?); } NativeType::ISize => { ffi_args.push(ffi_parse_isize_arg(scope, value)?); } NativeType::F32 => { ffi_args.push(ffi_parse_f32_arg(value)?); } NativeType::F64 => { ffi_args.push(ffi_parse_f64_arg(value)?); } NativeType::Buffer => { ffi_args.push(ffi_parse_buffer_arg(scope, value)?); } NativeType::Pointer => { ffi_args.push(ffi_parse_pointer_arg(scope, value)?); } NativeType::Function => { ffi_args.push(ffi_parse_function_arg(scope, value)?); } NativeType::Void => { unreachable!(); } } } let call_args: Vec<Arg> = ffi_args.iter().map(Arg::new).collect(); // SAFETY: types in the `Cif` match the actual calling convention and // types of symbol. unsafe { Ok(match result_type { NativeType::Void => NativeValue { void_value: cif.call::<()>(*fun_ptr, &call_args), }, NativeType::Bool => NativeValue { bool_value: cif.call::<bool>(*fun_ptr, &call_args), }, NativeType::U8 => NativeValue { u8_value: cif.call::<u8>(*fun_ptr, &call_args), }, NativeType::I8 => NativeValue { i8_value: cif.call::<i8>(*fun_ptr, &call_args), }, NativeType::U16 => NativeValue { u16_value: cif.call::<u16>(*fun_ptr, &call_args), }, NativeType::I16 => NativeValue { i16_value: cif.call::<i16>(*fun_ptr, &call_args), }, NativeType::U32 => NativeValue { u32_value: cif.call::<u32>(*fun_ptr, &call_args), }, NativeType::I32 => NativeValue { i32_value: cif.call::<i32>(*fun_ptr, &call_args), }, NativeType::U64 => NativeValue { u64_value: cif.call::<u64>(*fun_ptr, &call_args), }, NativeType::I64 => NativeValue { i64_value: cif.call::<i64>(*fun_ptr, &call_args), }, NativeType::USize => NativeValue { usize_value: cif.call::<usize>(*fun_ptr, &call_args), }, NativeType::ISize => NativeValue { isize_value: cif.call::<isize>(*fun_ptr, &call_args), }, NativeType::F32 => NativeValue { f32_value: cif.call::<f32>(*fun_ptr, &call_args), }, NativeType::F64 => NativeValue { f64_value: cif.call::<f64>(*fun_ptr, &call_args), }, NativeType::Pointer | NativeType::Function | NativeType::Buffer => { NativeValue { pointer: cif.call::<*const u8>(*fun_ptr, &call_args), } } }) }}
fn ffi_call( call_args: Vec<NativeValue>, cif: &libffi::middle::Cif, fun_ptr: libffi::middle::CodePtr, parameter_types: &[NativeType], result_type: NativeType,) -> Result<NativeValue, AnyError> { let call_args: Vec<Arg> = call_args .iter() .enumerate() .map(|(index, ffi_arg)| { // SAFETY: the union field is initialized unsafe { ffi_arg.as_arg(*parameter_types.get(index).unwrap()) } }) .collect();
// SAFETY: types in the `Cif` match the actual calling convention and // types of symbol. unsafe { Ok(match result_type { NativeType::Void => NativeValue { void_value: cif.call::<()>(fun_ptr, &call_args), }, NativeType::Bool => NativeValue { bool_value: cif.call::<bool>(fun_ptr, &call_args), }, NativeType::U8 => NativeValue { u8_value: cif.call::<u8>(fun_ptr, &call_args), }, NativeType::I8 => NativeValue { i8_value: cif.call::<i8>(fun_ptr, &call_args), }, NativeType::U16 => NativeValue { u16_value: cif.call::<u16>(fun_ptr, &call_args), }, NativeType::I16 => NativeValue { i16_value: cif.call::<i16>(fun_ptr, &call_args), }, NativeType::U32 => NativeValue { u32_value: cif.call::<u32>(fun_ptr, &call_args), }, NativeType::I32 => NativeValue { i32_value: cif.call::<i32>(fun_ptr, &call_args), }, NativeType::U64 => NativeValue { u64_value: cif.call::<u64>(fun_ptr, &call_args), }, NativeType::I64 => NativeValue { i64_value: cif.call::<i64>(fun_ptr, &call_args), }, NativeType::USize => NativeValue { usize_value: cif.call::<usize>(fun_ptr, &call_args), }, NativeType::ISize => NativeValue { isize_value: cif.call::<isize>(fun_ptr, &call_args), }, NativeType::F32 => NativeValue { f32_value: cif.call::<f32>(fun_ptr, &call_args), }, NativeType::F64 => NativeValue { f64_value: cif.call::<f64>(fun_ptr, &call_args), }, NativeType::Pointer | NativeType::Function | NativeType::Buffer => { NativeValue { pointer: cif.call::<*const u8>(fun_ptr, &call_args), } } }) }}
struct UnsafeCallbackResource { cancel: Rc<CancelHandle>, // Closure is never directly touched, but it keeps the C callback alive // until `close()` method is called. #[allow(dead_code)] closure: libffi::middle::Closure<'static>, info: *mut CallbackInfo,}
impl Resource for UnsafeCallbackResource { fn name(&self) -> Cow<str> { "unsafecallback".into() }
fn close(self: Rc<Self>) { self.cancel.cancel(); // SAFETY: This drops the closure and the callback info associated with it. // Any retained function pointers to the closure become dangling pointers. // It is up to the user to know that it is safe to call the `close()` on the // UnsafeCallback instance. unsafe { let info = Box::from_raw(self.info); let isolate = info.isolate.as_mut().unwrap(); let _ = v8::Global::from_raw(isolate, info.callback); let _ = v8::Global::from_raw(isolate, info.context); } }}
struct CallbackInfo { pub parameters: Vec<NativeType>, pub result: NativeType, pub async_work_sender: mpsc::UnboundedSender<PendingFfiAsyncWork>, pub callback: NonNull<v8::Function>, pub context: NonNull<v8::Context>, pub isolate: *mut v8::Isolate, pub waker: Option<Waker>,}
unsafe extern "C" fn deno_ffi_callback( _cif: &libffi::low::ffi_cif, result: &mut c_void, args: *const *const c_void, info: &CallbackInfo,) { LOCAL_ISOLATE_POINTER.with(|s| { if ptr::eq(*s.borrow(), info.isolate) { // Own isolate thread, okay to call directly do_ffi_callback(info, result, args); } else { let async_work_sender = &info.async_work_sender; // SAFETY: Safe as this function blocks until `do_ffi_callback` completes and a response message is received. let result: &'static mut c_void = std::mem::transmute(result); let info: &'static CallbackInfo = std::mem::transmute(info); let (response_sender, response_receiver) = sync_channel::<()>(0); let fut = Box::new(move || { do_ffi_callback(info, result, args); response_sender.send(()).unwrap(); }); async_work_sender.unbounded_send(fut).unwrap(); if let Some(waker) = info.waker.as_ref() { // Make sure event loop wakes up to receive our message before we start waiting for a response. waker.wake_by_ref(); } response_receiver.recv().unwrap(); } });}
unsafe fn do_ffi_callback( info: &CallbackInfo, result: &mut c_void, args: *const *const c_void,) { let callback: NonNull<v8::Function> = info.callback; let context: NonNull<v8::Context> = info.context; let isolate: *mut v8::Isolate = info.isolate; let isolate = &mut *isolate; let callback = v8::Global::from_raw(isolate, callback); let context = std::mem::transmute::< NonNull<v8::Context>, v8::Local<v8::Context>, >(context); // Call from main thread. If this callback is being triggered due to a // function call coming from Deno itself, then this callback will build // ontop of that stack. // If this callback is being triggered outside of Deno (for example from a // signal handler) then this will either create an empty new stack if // Deno currently has nothing running and is waiting for promises to resolve, // or will (very incorrectly) build ontop of whatever stack exists. // The callback will even be called through from a `while (true)` liveloop, but // it somehow cannot change the values that the loop sees, even if they both // refer the same `let bool_value`. let mut cb_scope = v8::CallbackScope::new(context); let scope = &mut v8::HandleScope::new(&mut cb_scope); let func = callback.open(scope); let result = result as *mut c_void; let vals: &[*const c_void] = std::slice::from_raw_parts(args, info.parameters.len() as usize);
let mut params: Vec<v8::Local<v8::Value>> = vec![]; for (native_type, val) in info.parameters.iter().zip(vals) { let value: v8::Local<v8::Value> = match native_type { NativeType::Bool => { let value = *((*val) as *const bool); v8::Boolean::new(scope, value).into() } NativeType::F32 => { let value = *((*val) as *const f32); v8::Number::new(scope, value as f64).into() } NativeType::F64 => { let value = *((*val) as *const f64); v8::Number::new(scope, value).into() } NativeType::I8 => { let value = *((*val) as *const i8); v8::Integer::new(scope, value as i32).into() } NativeType::U8 => { let value = *((*val) as *const u8); v8::Integer::new_from_unsigned(scope, value as u32).into() } NativeType::I16 => { let value = *((*val) as *const i16); v8::Integer::new(scope, value as i32).into() } NativeType::U16 => { let value = *((*val) as *const u16); v8::Integer::new_from_unsigned(scope, value as u32).into() } NativeType::I32 => { let value = *((*val) as *const i32); v8::Integer::new(scope, value).into() } NativeType::U32 => { let value = *((*val) as *const u32); v8::Integer::new_from_unsigned(scope, value).into() } NativeType::I64 | NativeType::ISize => { let result = *((*val) as *const i64); if result > MAX_SAFE_INTEGER as i64 || result < MIN_SAFE_INTEGER as i64 { v8::BigInt::new_from_i64(scope, result).into() } else { v8::Number::new(scope, result as f64).into() } } NativeType::U64 | NativeType::USize => { let result = *((*val) as *const u64); if result > MAX_SAFE_INTEGER as u64 { v8::BigInt::new_from_u64(scope, result).into() } else { v8::Number::new(scope, result as f64).into() } } NativeType::Pointer | NativeType::Buffer | NativeType::Function => { let result = *((*val) as *const usize); if result > MAX_SAFE_INTEGER as usize { v8::BigInt::new_from_u64(scope, result as u64).into() } else { v8::Number::new(scope, result as f64).into() } } NativeType::Void => unreachable!(), }; params.push(value); }
let recv = v8::undefined(scope); let call_result = func.call(scope, recv.into(), ¶ms); std::mem::forget(callback);
if call_result.is_none() { // JS function threw an exception. Set the return value to zero and return. // The exception continue propagating up the call chain when the event loop // resumes. match info.result { NativeType::Bool => { *(result as *mut bool) = false; } NativeType::U32 | NativeType::I32 => { // zero is equal for signed and unsigned alike *(result as *mut u32) = 0; } NativeType::F32 => { *(result as *mut f32) = 0.0; } NativeType::F64 => { *(result as *mut f64) = 0.0; } NativeType::U8 | NativeType::I8 => { // zero is equal for signed and unsigned alike *(result as *mut u8) = 0; } NativeType::U16 | NativeType::I16 => { // zero is equal for signed and unsigned alike *(result as *mut u16) = 0; } NativeType::Pointer | NativeType::Buffer | NativeType::Function | NativeType::U64 | NativeType::I64 => { *(result as *mut usize) = 0; } NativeType::Void => { // nop } _ => { unreachable!(); } };
return; } let value = call_result.unwrap();
match info.result { NativeType::Bool => { let value = if let Ok(value) = v8::Local::<v8::Boolean>::try_from(value) { value.is_true() } else { value.boolean_value(scope) }; *(result as *mut bool) = value; } NativeType::I32 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as i32 } else { // Fallthrough, probably UB. value .int32_value(scope) .expect("Unable to deserialize result parameter.") as i32 }; *(result as *mut i32) = value; } NativeType::F32 => { let value = if let Ok(value) = v8::Local::<v8::Number>::try_from(value) { value.value() as f32 } else { // Fallthrough, probably UB. value .number_value(scope) .expect("Unable to deserialize result parameter.") as f32 }; *(result as *mut f32) = value; } NativeType::F64 => { let value = if let Ok(value) = v8::Local::<v8::Number>::try_from(value) { value.value() } else { // Fallthrough, probably UB. value .number_value(scope) .expect("Unable to deserialize result parameter.") }; *(result as *mut f64) = value; } NativeType::Pointer | NativeType::Buffer | NativeType::Function => { let pointer = if let Ok(value) = v8::Local::<v8::ArrayBufferView>::try_from(value) { let byte_offset = value.byte_offset(); let backing_store = value .buffer(scope) .expect("Unable to deserialize result parameter.") .get_backing_store(); &backing_store[byte_offset..] as *const _ as *const u8 } else if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) { value.u64_value().0 as usize as *const u8 } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value) { let backing_store = value.get_backing_store(); &backing_store[..] as *const _ as *const u8 } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as usize as *const u8 } else if value.is_null() { ptr::null() } else { // Fallthrough: Probably someone returned a number but this could // also be eg. a string. This is essentially UB. value .integer_value(scope) .expect("Unable to deserialize result parameter.") as usize as *const u8 }; *(result as *mut *const u8) = pointer; } NativeType::I8 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as i8 } else { // Fallthrough, essentially UB. value .int32_value(scope) .expect("Unable to deserialize result parameter.") as i8 }; *(result as *mut i8) = value; } NativeType::U8 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as u8 } else { // Fallthrough, essentially UB. value .uint32_value(scope) .expect("Unable to deserialize result parameter.") as u8 }; *(result as *mut u8) = value; } NativeType::I16 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as i16 } else { // Fallthrough, essentially UB. value .int32_value(scope) .expect("Unable to deserialize result parameter.") as i16 }; *(result as *mut i16) = value; } NativeType::U16 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as u16 } else { // Fallthrough, essentially UB. value .uint32_value(scope) .expect("Unable to deserialize result parameter.") as u16 }; *(result as *mut u16) = value; } NativeType::U32 => { let value = if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { value.value() as u32 } else { // Fallthrough, essentially UB. value .uint32_value(scope) .expect("Unable to deserialize result parameter.") }; *(result as *mut u32) = value; } NativeType::I64 => { if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) { *(result as *mut i64) = value.i64_value().0; } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { *(result as *mut i64) = value.value(); } else { *(result as *mut i64) = value .integer_value(scope) .expect("Unable to deserialize result parameter.") as i64; } } NativeType::U64 => { if let Ok(value) = v8::Local::<v8::BigInt>::try_from(value) { *(result as *mut u64) = value.u64_value().0; } else if let Ok(value) = v8::Local::<v8::Integer>::try_from(value) { *(result as *mut u64) = value.value() as u64; } else { *(result as *mut u64) = value .integer_value(scope) .expect("Unable to deserialize result parameter.") as u64; } } NativeType::Void => { // nop } _ => { unreachable!(); } };}
#[derive(Deserialize)]struct RegisterCallbackArgs { parameters: Vec<NativeType>, result: NativeType,}
#[op(v8)]fn op_ffi_unsafe_callback_create<FP, 'scope>( state: &mut deno_core::OpState, scope: &mut v8::HandleScope<'scope>, args: RegisterCallbackArgs, cb: serde_v8::Value<'scope>,) -> Result<serde_v8::Value<'scope>, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafeCallback"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let v8_value = cb.v8_value; let cb = v8::Local::<v8::Function>::try_from(v8_value)?;
let isolate: *mut v8::Isolate = &mut *scope as &mut v8::Isolate; LOCAL_ISOLATE_POINTER.with(|s| { if s.borrow().is_null() { s.replace(isolate); } });
let async_work_sender = state.borrow_mut::<FfiState>().async_work_sender.clone(); let callback = v8::Global::new(scope, cb).into_raw(); let current_context = scope.get_current_context(); let context = v8::Global::new(scope, current_context).into_raw();
let info: *mut CallbackInfo = Box::leak(Box::new(CallbackInfo { parameters: args.parameters.clone(), result: args.result, async_work_sender, callback, context, isolate, waker: None, })); let cif = Cif::new( args.parameters.into_iter().map(libffi::middle::Type::from), libffi::middle::Type::from(args.result), );
// SAFETY: CallbackInfo is leaked, is not null and stays valid as long as the callback exists. let closure = libffi::middle::Closure::new(cif, deno_ffi_callback, unsafe { info.as_ref().unwrap() }); let ptr = *closure.code_ptr() as usize; let resource = UnsafeCallbackResource { cancel: CancelHandle::new_rc(), closure, info, }; let rid = state.resource_table.add(resource);
let rid_local = v8::Integer::new_from_unsigned(scope, rid); let ptr_local: v8::Local<v8::Value> = if ptr > MAX_SAFE_INTEGER as usize { v8::BigInt::new_from_u64(scope, ptr as u64).into() } else { v8::Number::new(scope, ptr as f64).into() }; let array = v8::Array::new(scope, 2); array.set_index(scope, 0, rid_local.into()); array.set_index(scope, 1, ptr_local); let array_value: v8::Local<v8::Value> = array.into();
Ok(array_value.into())}
#[op(v8)]fn op_ffi_call_ptr<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, pointer: usize, def: ForeignFunction, parameters: serde_v8::Value<'scope>,) -> Result<serde_v8::Value<'scope>, AnyError>where FP: FfiPermissions + 'static,{ check_unstable2(&state, "Deno.UnsafeFnPointer#call"); { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; };
let symbol = PtrSymbol::new(pointer, &def); let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
let result = ffi_call( call_args, &symbol.cif, symbol.ptr, &def.parameters, def.result, )?; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. let result = unsafe { result.to_v8(scope, def.result) }; Ok(result)}
impl Future for CallbackInfo { type Output = (); fn poll( mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll<Self::Output> { // Always replace the waker to make sure it's bound to the proper Future. self.waker.replace(cx.waker().clone()); // The future for the CallbackInfo never resolves: It can only be canceled. Poll::Pending }}
#[op]fn op_ffi_unsafe_callback_ref( state: &mut deno_core::OpState, rid: ResourceId,) -> Result<impl Future<Output = Result<(), AnyError>>, AnyError> { let callback_resource = state.resource_table.get::<UnsafeCallbackResource>(rid)?;
Ok(async move { let info: &mut CallbackInfo = // SAFETY: CallbackInfo pointer stays valid as long as the resource is still alive. unsafe { callback_resource.info.as_mut().unwrap() }; // Ignore cancellation rejection let _ = info .into_future() .or_cancel(callback_resource.cancel.clone()) .await; Ok(()) })}
#[op(fast)]fn op_ffi_unsafe_callback_unref( state: &mut deno_core::OpState, rid: u32,) -> Result<(), AnyError> { state .resource_table .get::<UnsafeCallbackResource>(rid)? .cancel .cancel(); Ok(())}
#[op(v8)]fn op_ffi_call_ptr_nonblocking<'scope, FP>( scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, pointer: usize, def: ForeignFunction, parameters: serde_v8::Value<'scope>,) -> Result<impl Future<Output = Result<Value, AnyError>>, AnyError>where FP: FfiPermissions + 'static,{ check_unstable2(&state, "Deno.UnsafeFnPointer#call"); { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?; };
let symbol = PtrSymbol::new(pointer, &def); let call_args = ffi_parse_args(scope, parameters, &def.parameters)?;
let join_handle = tokio::task::spawn_blocking(move || { let PtrSymbol { cif, ptr } = symbol.clone(); ffi_call(call_args, &cif, ptr, &def.parameters, def.result) });
Ok(async move { let result = join_handle .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. Ok(unsafe { result.to_value(def.result) }) })}
#[op(v8)]fn op_ffi_get_static<'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, rid: ResourceId, name: String, static_type: NativeType,) -> Result<serde_v8::Value<'scope>, AnyError> { let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?;
let data_ptr = resource.get_static(name)?;
Ok(match static_type { NativeType::Void => { return Err(type_error("Invalid FFI static type 'void'")); } NativeType::Bool => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const bool) }; let boolean: v8::Local<v8::Value> = v8::Boolean::new(scope, result).into(); boolean.into() } NativeType::U8 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const u8) }; let number: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, result as u32).into(); number.into() } NativeType::I8 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const i8) }; let number: v8::Local<v8::Value> = v8::Integer::new(scope, result as i32).into(); number.into() } NativeType::U16 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const u16) }; let number: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, result as u32).into(); number.into() } NativeType::I16 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const i16) }; let number: v8::Local<v8::Value> = v8::Integer::new(scope, result as i32).into(); number.into() } NativeType::U32 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const u32) }; let number: v8::Local<v8::Value> = v8::Integer::new_from_unsigned(scope, result).into(); number.into() } NativeType::I32 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const i32) }; let number: v8::Local<v8::Value> = v8::Integer::new(scope, result).into(); number.into() } NativeType::U64 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const u64) }; let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 { v8::BigInt::new_from_u64(scope, result).into() } else { v8::Number::new(scope, result as f64).into() }; integer.into() } NativeType::I64 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const i64) }; let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as i64 || result < MIN_SAFE_INTEGER as i64 { v8::BigInt::new_from_i64(scope, result).into() } else { v8::Number::new(scope, result as f64).into() }; integer.into() } NativeType::USize => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const usize) }; let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as usize { v8::BigInt::new_from_u64(scope, result as u64).into() } else { v8::Number::new(scope, result as f64).into() }; integer.into() } NativeType::ISize => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const isize) }; let integer: v8::Local<v8::Value> = if !(MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&result) { v8::BigInt::new_from_i64(scope, result as i64).into() } else { v8::Number::new(scope, result as f64).into() }; integer.into() } NativeType::F32 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const f32) }; let number: v8::Local<v8::Value> = v8::Number::new(scope, result as f64).into(); number.into() } NativeType::F64 => { // SAFETY: ptr is user provided let result = unsafe { ptr::read_unaligned(data_ptr as *const f64) }; let number: v8::Local<v8::Value> = v8::Number::new(scope, result).into(); number.into() } NativeType::Pointer | NativeType::Function | NativeType::Buffer => { let result = data_ptr as u64; let integer: v8::Local<v8::Value> = if result > MAX_SAFE_INTEGER as u64 { v8::BigInt::new_from_u64(scope, result).into() } else { v8::Number::new(scope, result as f64).into() }; integer.into() } })}
/// A non-blocking FFI call.#[op(v8)]fn op_ffi_call_nonblocking<'scope>( scope: &mut v8::HandleScope<'scope>, state: Rc<RefCell<deno_core::OpState>>, rid: ResourceId, symbol: String, parameters: serde_v8::Value<'scope>,) -> Result<impl Future<Output = Result<Value, AnyError>> + 'static, AnyError> { let symbol = { let state = state.borrow(); let resource = state.resource_table.get::<DynamicLibraryResource>(rid)?; let symbols = &resource.symbols; *symbols .get(&symbol) .ok_or_else(|| type_error("Invalid FFI symbol name"))? .clone() };
let call_args = ffi_parse_args(scope, parameters, &symbol.parameter_types)?;
let result_type = symbol.result_type; let join_handle = tokio::task::spawn_blocking(move || { let Symbol { cif, ptr, parameter_types, result_type, .. } = symbol.clone(); ffi_call(call_args, &cif, ptr, ¶meter_types, result_type) });
Ok(async move { let result = join_handle .await .map_err(|err| anyhow!("Nonblocking FFI call failed: {}", err))??; // SAFETY: Same return type declared to libffi; trust user to have it right beyond that. Ok(unsafe { result.to_value(result_type) }) })}
#[op(fast)]fn op_ffi_ptr_of<FP>( state: &mut deno_core::OpState, buf: &[u8], out: &mut [u32],) -> Result<(), AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointer#of"); let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let outptr = out.as_ptr() as *mut usize; let length = out.len(); assert!( length >= (std::mem::size_of::<usize>() / std::mem::size_of::<u32>()) ); assert_eq!(outptr as usize % std::mem::size_of::<usize>(), 0);
// SAFETY: Out buffer was asserted to be at least large enough to hold a usize, and properly aligned. let out = unsafe { &mut *outptr }; *out = buf.as_ptr() as usize;
Ok(())}
unsafe extern "C" fn noop_deleter_callback( _data: *mut c_void, _byte_length: usize, _deleter_data: *mut c_void,) {}
#[op(v8)]fn op_ffi_get_buf<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, ptr: usize, offset: usize, len: usize,) -> Result<serde_v8::Value<'scope>, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#arrayBuffer");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *mut c_void;
if ptr.is_null() { return Err(type_error("Invalid FFI pointer value, got nullptr")); }
// SAFETY: Offset is user defined. let ptr = unsafe { ptr.add(offset) };
// SAFETY: Trust the user to have provided a real pointer, and a valid matching size to it. Since this is a foreign pointer, we should not do any deletion. let backing_store = unsafe { v8::ArrayBuffer::new_backing_store_from_ptr( ptr, len, noop_deleter_callback, std::ptr::null_mut(), ) } .make_shared(); let array_buffer: v8::Local<v8::Value> = v8::ArrayBuffer::with_backing_store(scope, &backing_store).into(); Ok(array_buffer.into())}
#[op(fast)]fn op_ffi_buf_copy_into<FP>( state: &mut deno_core::OpState, src: usize, offset: usize, dst: &mut [u8], len: usize,) -> Result<(), AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#copyInto");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
if dst.len() < len { Err(range_error( "Destination length is smaller than source length", )) } else { let src = src as *const c_void;
// SAFETY: Offset is user defined. let src = unsafe { src.add(offset) as *const u8 };
// SAFETY: src is user defined. // dest is properly aligned and is valid for writes of len * size_of::<T>() bytes. unsafe { ptr::copy::<u8>(src, dst.as_mut_ptr(), len) }; Ok(()) }}
#[op(v8)]fn op_ffi_cstr_read<FP, 'scope>( scope: &mut v8::HandleScope<'scope>, state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<serde_v8::Value<'scope>, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getCString");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid CString pointer, pointer is null")); }
// SAFETY: Offset is user defined. let ptr = unsafe { ptr.add(offset) };
// SAFETY: Pointer is user provided. let cstr = unsafe { CStr::from_ptr(ptr as *const c_char) } .to_str() .map_err(|_| type_error("Invalid CString pointer, not valid UTF-8"))?; let value: v8::Local<v8::Value> = v8::String::new(scope, cstr) .ok_or_else(|| { type_error("Invalid CString pointer, string exceeds max length") })? .into(); Ok(value.into())}
#[op(fast)]fn op_ffi_read_bool<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<bool, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getBool");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid bool pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<bool>(ptr.add(offset) as *const bool) })}
#[op(fast)]fn op_ffi_read_u8<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<u32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getUint8");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid u8 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<u8>(ptr.add(offset) as *const u8) as u32 })}
#[op(fast)]fn op_ffi_read_i8<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<i32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getInt8");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid i8 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<i8>(ptr.add(offset) as *const i8) as i32 })}
#[op(fast)]fn op_ffi_read_u16<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<u32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getUint16");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid u16 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<u16>(ptr.add(offset) as *const u16) as u32 })}
#[op(fast)]fn op_ffi_read_i16<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<i32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getInt16");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid i16 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<i16>(ptr.add(offset) as *const i16) as i32 })}
#[op(fast)]fn op_ffi_read_u32<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<u32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getUint32");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid u32 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<u32>(ptr.add(offset) as *const u32) as u32 })}
#[op(fast)]fn op_ffi_read_i32<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<i32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getInt32");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid i32 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<i32>(ptr.add(offset) as *const i32) as i32 })}
#[op]fn op_ffi_read_u64<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize, out: &mut [u32],) -> Result<(), AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let outptr = out.as_mut_ptr() as *mut u64;
assert!( out.len() >= (std::mem::size_of::<u64>() / std::mem::size_of::<u32>()) ); assert_eq!((outptr as usize % std::mem::size_of::<u64>()), 0);
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid u64 pointer, pointer is null")); }
let value = // SAFETY: ptr and offset are user provided. unsafe { ptr::read_unaligned::<u64>(ptr.add(offset) as *const u64) };
// SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; Ok(())}
#[op(fast)]fn op_ffi_read_i64<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize, out: &mut [u32],) -> Result<(), AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getBigUint64");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let outptr = out.as_mut_ptr() as *mut i64;
assert!( out.len() >= (std::mem::size_of::<i64>() / std::mem::size_of::<u32>()) ); assert_eq!((outptr as usize % std::mem::size_of::<i64>()), 0);
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid i64 pointer, pointer is null")); }
let value = // SAFETY: ptr and offset are user provided. unsafe { ptr::read_unaligned::<i64>(ptr.add(offset) as *const i64) }; // SAFETY: Length and alignment of out slice were asserted to be correct. unsafe { *outptr = value }; Ok(())}
#[op(fast)]fn op_ffi_read_f32<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<f32, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getFloat32");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid f32 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<f32>(ptr.add(offset) as *const f32) })}
#[op(fast)]fn op_ffi_read_f64<FP>( state: &mut deno_core::OpState, ptr: usize, offset: usize,) -> Result<f64, AnyError>where FP: FfiPermissions + 'static,{ check_unstable(state, "Deno.UnsafePointerView#getFloat64");
let permissions = state.borrow_mut::<FP>(); permissions.check(None)?;
let ptr = ptr as *const c_void;
if ptr.is_null() { return Err(type_error("Invalid f64 pointer, pointer is null")); }
// SAFETY: ptr and offset are user provided. Ok(unsafe { ptr::read_unaligned::<f64>(ptr.add(offset) as *const f64) })}
#[cfg(test)]mod tests { #[cfg(target_os = "windows")] #[test] fn test_format_error() { use super::format_error;
// BAD_EXE_FORMAT let err = dlopen::Error::OpeningLibraryError( std::io::Error::from_raw_os_error(0x000000C1), ); assert_eq!( format_error(err, "foo.dll".to_string()), "foo.dll is not a valid Win32 application.\r\n".to_string(), ); }}
Version Info