0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-11-28 16:21:04 -05:00
denoland-rusty-v8/src/fast_api.rs
2024-07-30 06:03:27 +00:00

343 lines
8.7 KiB
Rust

use crate::support::Opaque;
use crate::Isolate;
use crate::Local;
use crate::Value;
use std::{
ffi::c_void,
mem::align_of,
ptr::{self, NonNull},
};
extern "C" {
fn v8__CTypeInfo__New(ty: CType) -> *mut CTypeInfo;
fn v8__CTypeInfo__New__From__Slice(
len: usize,
tys: *const CTypeSequenceInfo,
) -> *mut CTypeInfo;
fn v8__CTypeInfo__DELETE(this: *mut CTypeInfo);
fn v8__CFunctionInfo__New(
return_info: *const CTypeInfo,
args_len: usize,
args_info: *const CTypeInfo,
repr: Int64Representation,
) -> *mut CFunctionInfo;
fn v8__CFunctionInfo__DELETE(this: *mut CFunctionInfo);
}
#[repr(C)]
#[derive(Default)]
pub struct CFunctionInfo(Opaque);
#[repr(C)]
#[derive(Default)]
pub struct CFunction(Opaque);
impl CFunctionInfo {
#[inline(always)]
pub unsafe fn new(
args: *const CTypeInfo,
args_len: usize,
return_type: *const CTypeInfo,
repr: Int64Representation,
) -> NonNull<CFunctionInfo> {
NonNull::new_unchecked(v8__CFunctionInfo__New(
return_type,
args_len,
args,
repr,
))
}
}
impl Drop for CFunctionInfo {
fn drop(&mut self) {
unsafe { v8__CFunctionInfo__DELETE(self) };
}
}
#[repr(C)]
#[derive(Debug)]
pub struct CTypeInfo(Opaque);
impl CTypeInfo {
#[inline(always)]
pub fn new(ty: CType) -> NonNull<CTypeInfo> {
unsafe { NonNull::new_unchecked(v8__CTypeInfo__New(ty)) }
}
pub fn new_from_slice(types: &[Type]) -> NonNull<CTypeInfo> {
let mut structs = vec![];
for type_ in types.iter() {
structs.push(type_.into())
}
unsafe {
NonNull::new_unchecked(v8__CTypeInfo__New__From__Slice(
structs.len(),
structs.as_ptr(),
))
}
}
}
impl Drop for CTypeInfo {
fn drop(&mut self) {
unsafe { v8__CTypeInfo__DELETE(self) };
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum SequenceType {
Scalar,
/// sequence<T>
IsSequence,
/// TypedArray of T or any ArrayBufferView if T is void
IsTypedArray,
/// ArrayBuffer
IsArrayBuffer,
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
#[non_exhaustive]
pub enum CType {
Void = 0,
Bool,
Uint8,
Int32,
Uint32,
Int64,
Uint64,
Float32,
Float64,
Pointer,
V8Value,
SeqOneByteString,
// https://github.com/v8/v8/blob/492a32943bc34a527f42df2ae15a77154b16cc84/include/v8-fast-api-calls.h#L264-L267
// kCallbackOptionsType is not part of the Type enum
// because it is only used internally. Use value 255 that is larger
// than any valid Type enum.
CallbackOptions = 255,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Type {
Void,
Bool,
Uint8,
Int32,
Uint32,
Int64,
Uint64,
Float32,
Float64,
Pointer,
V8Value,
SeqOneByteString,
CallbackOptions,
Sequence(CType),
TypedArray(CType),
ArrayBuffer(CType),
}
impl From<&Type> for CType {
fn from(ty: &Type) -> CType {
match ty {
Type::Void => CType::Void,
Type::Bool => CType::Bool,
Type::Uint8 => CType::Uint8,
Type::Int32 => CType::Int32,
Type::Uint32 => CType::Uint32,
Type::Int64 => CType::Int64,
Type::Uint64 => CType::Uint64,
Type::Float32 => CType::Float32,
Type::Float64 => CType::Float64,
Type::Pointer => CType::Pointer,
Type::V8Value => CType::V8Value,
Type::SeqOneByteString => CType::SeqOneByteString,
Type::CallbackOptions => CType::CallbackOptions,
Type::Sequence(ty) => *ty,
Type::TypedArray(ty) => *ty,
Type::ArrayBuffer(ty) => *ty,
}
}
}
impl From<&Type> for SequenceType {
fn from(ty: &Type) -> SequenceType {
match ty {
Type::Sequence(_) => SequenceType::IsSequence,
Type::TypedArray(_) => SequenceType::IsTypedArray,
Type::ArrayBuffer(_) => SequenceType::IsArrayBuffer,
_ => SequenceType::Scalar,
}
}
}
impl From<&Type> for CTypeSequenceInfo {
fn from(ty: &Type) -> CTypeSequenceInfo {
CTypeSequenceInfo {
c_type: ty.into(),
sequence_type: ty.into(),
}
}
}
#[repr(C)]
struct CTypeSequenceInfo {
c_type: CType,
sequence_type: SequenceType,
}
#[repr(C)]
pub union FastApiCallbackData<'a> {
/// `data_ptr` allows for default constructing FastApiCallbackOptions.
pub data_ptr: *mut c_void,
/// The `data` passed to the FunctionTemplate constructor, or `undefined`.
pub data: Local<'a, Value>,
}
/// A struct which may be passed to a fast call callback, like so
/// ```c
/// void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
/// ```
#[repr(C)]
pub struct FastApiCallbackOptions<'a> {
pub isolate: *mut Isolate,
/// If the callback wants to signal an error condition or to perform an
/// allocation, it must set options.fallback to true and do an early return
/// from the fast method. Then V8 checks the value of options.fallback and if
/// it's true, falls back to executing the SlowCallback, which is capable of
/// reporting the error (either by throwing a JS exception or logging to the
/// console) or doing the allocation. It's the embedder's responsibility to
/// ensure that the fast callback is idempotent up to the point where error and
/// fallback conditions are checked, because otherwise executing the slow
/// callback might produce visible side-effects twice.
pub fallback: bool,
pub data: FastApiCallbackData<'a>,
/// When called from WebAssembly, a view of the calling module's memory.
pub wasm_memory: *const FastApiTypedArray<u8>,
}
// https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-fast-api-calls.h;l=336
#[repr(C)]
pub struct FastApiTypedArray<T: Default> {
/// Returns the length in number of elements.
pub length: usize,
// This pointer should include the typed array offset applied.
// It's not guaranteed that it's aligned to sizeof(T), it's only
// guaranteed that it's 4-byte aligned, so for 8-byte types we need to
// provide a special implementation for reading from it, which hides
// the possibly unaligned read in the `get` method.
data: *mut T,
}
// FastApiOneByteString is an alias for SeqOneByteString and the type is widely used in deno_core.
#[allow(dead_code)]
#[repr(C)]
pub struct FastApiOneByteString {
data: *const u8,
pub length: u32,
}
impl FastApiOneByteString {
#[inline(always)]
pub fn as_bytes(&self) -> &[u8] {
// Ensure that we never create a null-ptr slice (even a zero-length null-ptr slice
// is invalid because of Rust's niche packing).
if self.data.is_null() {
return &mut [];
}
// SAFETY: The data is guaranteed to be valid for the length of the string.
unsafe { std::slice::from_raw_parts(self.data, self.length as usize) }
}
}
impl<T: Default> FastApiTypedArray<T> {
/// Performs an unaligned-safe read of T from the underlying data.
#[inline(always)]
pub const fn get(&self, index: usize) -> T {
debug_assert!(index < self.length);
// SAFETY: src is valid for reads, and is a valid value for T
unsafe { ptr::read_unaligned(self.data.add(index)) }
}
/// Given a pointer to a `FastApiTypedArray`, returns a slice pointing to the
/// data if safe to do so.
///
/// # Safety
///
/// The pointer must not be null and the caller must choose a lifetime that is
/// safe.
#[inline(always)]
pub unsafe fn get_storage_from_pointer_if_aligned<'a>(
ptr: *mut Self,
) -> Option<&'a mut [T]> {
debug_assert!(!ptr.is_null());
let self_ref = ptr.as_mut().unwrap_unchecked();
self_ref.get_storage_if_aligned()
}
/// Returns a slice pointing to the underlying data if safe to do so.
#[inline(always)]
pub fn get_storage_if_aligned(&self) -> Option<&mut [T]> {
// V8 may provide an invalid or null pointer when length is zero, so we just
// ignore that value completely and create an empty slice in this case.
if self.length == 0 {
return Some(&mut []);
}
// Ensure that we never return an unaligned or null buffer
if self.data.is_null() || (self.data as usize) % align_of::<T>() != 0 {
return None;
}
Some(unsafe { std::slice::from_raw_parts_mut(self.data, self.length) })
}
}
#[derive(Copy, Clone)]
pub struct FastFunction {
pub args: &'static [Type],
pub function: *const c_void,
pub repr: Int64Representation,
pub return_type: CType,
}
impl FastFunction {
#[inline(always)]
pub const fn new(
args: &'static [Type],
return_type: CType,
function: *const c_void,
) -> Self {
Self {
args,
function,
repr: Int64Representation::Number,
return_type,
}
}
pub const fn new_with_bigint(
args: &'static [Type],
return_type: CType,
function: *const c_void,
) -> Self {
Self {
args,
function,
repr: Int64Representation::BigInt,
return_type,
}
}
}
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum Int64Representation {
Number = 0,
BigInt = 1,
}