diff --git a/src/binding.cc b/src/binding.cc index b8934d02..984b8458 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1760,6 +1760,37 @@ const v8::Signature* v8__Signature__New(v8::Isolate* isolate, return local_to_ptr(v8::Signature::New(isolate, ptr_to_local(templ))); } +v8::CTypeInfo* v8__CTypeInfo__New(v8::CTypeInfo::Type ty) { + std::unique_ptr u = std::make_unique(v8::CTypeInfo(ty)); + return u.release(); +} + +struct CTypeSequenceType { + v8::CTypeInfo::Type c_type; + v8::CTypeInfo::SequenceType sequence_type; +}; + +v8::CTypeInfo* v8__CTypeInfo__New__From__Slice(unsigned int len, + CTypeSequenceType* ty) { + v8::CTypeInfo* v = (v8::CTypeInfo*)malloc(sizeof(v8::CTypeInfo) * len); + for (size_t i = 0; i < len; i += 1) { + v[i] = v8::CTypeInfo(ty[i].c_type, ty[i].sequence_type); + } + return v; +} + +v8::CFunction* v8__CFunction__New(void* func_ptr, const v8::CFunctionInfo* info) { + std::unique_ptr c_function = std::make_unique(v8::CFunction(func_ptr, info)); + return c_function.release(); +} + +v8::CFunctionInfo* v8__CFunctionInfo__New(const v8::CTypeInfo& return_info, + unsigned int args_len, + v8::CTypeInfo* args_info) { + std::unique_ptr info = std::make_unique(v8::CFunctionInfo(return_info, args_len, args_info)); + return info.release(); +} + const v8::FunctionTemplate* v8__FunctionTemplate__New( v8::Isolate* isolate, v8::FunctionCallback callback, const v8::Value* data_or_null, const v8::Signature* signature_or_null, diff --git a/src/fast_api.rs b/src/fast_api.rs new file mode 100644 index 00000000..852fb521 --- /dev/null +++ b/src/fast_api.rs @@ -0,0 +1,150 @@ +use crate::support::Opaque; +use libc::c_void; +use std::mem::transmute_copy; +use std::ptr::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__CFunctionInfo__New( + return_info: *const CTypeInfo, + args_len: usize, + args_info: *const CTypeInfo, + ) -> *mut CFunctionInfo; + fn v8__CFunction__New( + func_ptr: *const c_void, + info: *const CFunctionInfo, + ) -> *mut CFunction; +} + +#[repr(C)] +#[derive(Default)] +pub struct CFunctionInfo(Opaque); + +#[repr(C)] +#[derive(Default)] +pub struct CFunction(Opaque); + +impl CFunction { + pub(crate) unsafe fn new( + func_ptr: *const c_void, + args: *const CTypeInfo, + args_len: usize, + return_type: *const CTypeInfo, + ) -> NonNull { + let info = v8__CFunctionInfo__New(return_type, args_len, args); + NonNull::new_unchecked(v8__CFunction__New(func_ptr, info)) + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct CTypeInfo(Opaque); + +impl CTypeInfo { + pub(crate) fn new(ty: CType) -> NonNull { + unsafe { NonNull::new_unchecked(v8__CTypeInfo__New(ty)) } + } + + pub(crate) fn new_from_slice(types: &[Type]) -> NonNull { + 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(), + )) + } + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub enum SequenceType { + Scalar, + /// sequence + IsSequence, + /// TypedArray of T or any ArrayBufferView if T is void + IsTypedArray, + /// ArrayBuffer + IsArrayBuffer, +} + +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum CType { + Void = 0, + Bool, + Int32, + Uint32, + Int64, + Uint64, + Float32, + Float64, + V8Value, +} + +#[derive(Clone, Copy)] +pub enum Type { + Void, + Bool, + Int32, + Uint32, + Int64, + Uint64, + Float32, + Float64, + V8Value, +} + +impl From<&Type> for CType { + fn from(ty: &Type) -> CType { + match ty { + Type::Void => CType::Void, + Type::Bool => CType::Bool, + 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::V8Value => CType::V8Value, + } + } +} + +impl From<&Type> for CTypeSequenceInfo { + fn from(ty: &Type) -> CTypeSequenceInfo { + CTypeSequenceInfo { + c_type: ty.into(), + sequence_type: SequenceType::Scalar, + } + } +} + +#[repr(C)] +struct CTypeSequenceInfo { + c_type: CType, + sequence_type: SequenceType, +} + +pub trait FastFunction { + type Signature; + fn args(&self) -> &'static [Type] { + &[] + } + fn return_type(&self) -> CType { + CType::Void + } + fn function(&self) -> Self::Signature; + fn raw(&self) -> *const c_void { + unsafe { transmute_copy(&self.function()) } + } +} diff --git a/src/function.rs b/src/function.rs index e642cd17..2d204380 100644 --- a/src/function.rs +++ b/src/function.rs @@ -117,10 +117,6 @@ pub enum SideEffectType { HasSideEffectToReceiver, } -#[repr(C)] -#[derive(Default)] -pub(crate) struct CFunction([usize; 2]); - // Note: the 'cb lifetime is required because the ReturnValue object must not // outlive the FunctionCallbackInfo/PropertyCallbackInfo object from which it // is derived. diff --git a/src/lib.rs b/src/lib.rs index 53404a30..92034d57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,7 @@ mod date; mod exception; mod external; mod external_references; +pub mod fast_api; mod fixed_array; mod function; mod handle; diff --git a/src/template.rs b/src/template.rs index 7e231e70..47d9250c 100644 --- a/src/template.rs +++ b/src/template.rs @@ -3,12 +3,14 @@ use crate::data::FunctionTemplate; use crate::data::Name; use crate::data::ObjectTemplate; use crate::data::Template; +use crate::fast_api::CFunction; +use crate::fast_api::CTypeInfo; +use crate::fast_api::FastFunction; use crate::isolate::Isolate; use crate::support::int; use crate::support::MapFnTo; use crate::AccessorNameGetterCallback; use crate::AccessorNameSetterCallback; -use crate::CFunction; use crate::ConstructorBehavior; use crate::Context; use crate::Function; @@ -149,6 +151,36 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { } .unwrap() } + + pub fn build_fast( + self, + scope: &mut HandleScope<'s, ()>, + fast_function: impl FastFunction, + ) -> Local<'s, FunctionTemplate> { + unsafe { + let args = CTypeInfo::new_from_slice(fast_function.args()); + let ret = CTypeInfo::new(fast_function.return_type()); + let c_fn = CFunction::new( + fast_function.raw(), + args.as_ptr(), + fast_function.args().len(), + ret.as_ptr(), + ); + scope.cast_local(|sd| { + v8__FunctionTemplate__New( + sd.get_isolate_ptr(), + self.callback, + self.data.map_or_else(null, |p| &*p), + self.signature.map_or_else(null, |p| &*p), + self.length, + ConstructorBehavior::Throw, + self.side_effect_type, + c_fn.as_ptr() as _, + ) + }) + } + .unwrap() + } } /// A Signature specifies which receiver is valid for a function. diff --git a/tests/test_api.rs b/tests/test_api.rs index 1e739d78..4cd39409 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -14,6 +14,7 @@ use std::ptr::NonNull; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::sync::Mutex; +use v8::fast_api; // TODO(piscisaureus): Ideally there would be no need to import this trait. use v8::MapFnTo; @@ -36,7 +37,7 @@ fn setup() -> SetupGuard { )) .is_ok()); v8::V8::set_flags_from_string( - "--expose_gc --harmony-import-assertions --harmony-shadow-realm", + "--expose_gc --harmony-import-assertions --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls", ); v8::V8::initialize_platform( v8::new_default_platform(0, false).make_shared(), @@ -7014,3 +7015,66 @@ fn host_create_shadow_realm_context_callback() { assert_eq!(value.uint32_value(scope), Some(42)); assert!(scope.get_slot::().unwrap().callback_called); } + +#[test] +fn test_fast_calls() { + static mut WHO: &str = "none"; + fn fast_fn(a: u32, b: u32) -> u32 { + unsafe { WHO = "fast" }; + a + b + } + + pub struct FastTest; + impl fast_api::FastFunction for FastTest { + fn args(&self) -> &'static [fast_api::Type] { + &[fast_api::Type::Uint32, fast_api::Type::Uint32] + } + + fn return_type(&self) -> fast_api::CType { + fast_api::CType::Uint32 + } + + type Signature = fn(a: u32, b: u32) -> u32; + fn function(&self) -> Self::Signature { + fast_fn + } + } + + fn slow_fn( + scope: &mut v8::HandleScope, + _: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue, + ) { + unsafe { WHO = "slow" }; + rv.set(v8::Boolean::new(scope, false).into()); + } + + let _setup_guard = setup(); + let isolate = &mut v8::Isolate::new(Default::default()); + let scope = &mut v8::HandleScope::new(isolate); + let context = v8::Context::new(scope); + let scope = &mut v8::ContextScope::new(scope, context); + + let global = context.global(scope); + + let template = + v8::FunctionTemplate::builder(slow_fn).build_fast(scope, FastTest); + + let name = v8::String::new(scope, "func").unwrap(); + let value = template.get_function(scope).unwrap(); + global.set(scope, name.into(), value.into()).unwrap(); + let source = r#" + function f(x, y) { return func(x, y); } + %PrepareFunctionForOptimization(f); + f(1, 2); +"#; + eval(scope, source).unwrap(); + assert_eq!("slow", unsafe { WHO }); + + let source = r#" + %OptimizeFunctionOnNextCall(f); + f(1, 2); + "#; + eval(scope, source).unwrap(); + assert_eq!("fast", unsafe { WHO }); +}