From 3be46ecc5271aa68475e1e02b163a0bc5f6992ed Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 16 Jul 2022 19:57:58 +0530 Subject: [PATCH] Support Fast API Overloads (#1031) --- src/binding.cc | 47 +++++++++------ src/fast_api.rs | 61 ++++++++++++++----- src/template.rs | 42 +++++++++---- tests/test_api.rs | 148 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 216 insertions(+), 82 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index 934ee6cb..baa8f40c 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -247,7 +247,7 @@ void v8__Isolate__SetPromiseRejectCallback(v8::Isolate* isolate, } void v8__Isolate__SetWasmAsyncResolvePromiseCallback( - v8::Isolate* isolate, v8::WasmAsyncResolvePromiseCallback callback) { + v8::Isolate* isolate, v8::WasmAsyncResolvePromiseCallback callback) { isolate->SetWasmAsyncResolvePromiseCallback(callback); } @@ -1166,12 +1166,12 @@ const v8::Value* v8__Object__GetIndex(const v8::Object& self, } void* v8__Object__GetAlignedPointerFromInternalField(const v8::Object& self, - int index) { + int index) { return ptr_to_local(&self)->GetAlignedPointerFromInternalField(index); } void v8__Object__SetAlignedPointerInInternalField(const v8::Object& self, - int index, void* value) { + int index, void* value) { ptr_to_local(&self)->SetAlignedPointerInInternalField(index, value); } @@ -1780,7 +1780,8 @@ const v8::Signature* v8__Signature__New(v8::Isolate* isolate, } v8::CTypeInfo* v8__CTypeInfo__New(v8::CTypeInfo::Type ty) { - std::unique_ptr u = std::make_unique(v8::CTypeInfo(ty)); + std::unique_ptr u = + std::make_unique(v8::CTypeInfo(ty)); return u.release(); } @@ -1789,8 +1790,8 @@ struct CTypeSequenceType { v8::CTypeInfo::SequenceType sequence_type; }; -v8::CTypeInfo* v8__CTypeInfo__New__From__Slice(unsigned int len, - CTypeSequenceType* ty) { +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); @@ -1798,15 +1799,11 @@ v8::CTypeInfo* v8__CTypeInfo__New__From__Slice(unsigned int len, 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)); + 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(); } @@ -1814,12 +1811,26 @@ const v8::FunctionTemplate* v8__FunctionTemplate__New( v8::Isolate* isolate, v8::FunctionCallback callback, const v8::Value* data_or_null, const v8::Signature* signature_or_null, int length, v8::ConstructorBehavior constructor_behavior, - v8::SideEffectType side_effect_type, - const v8::CFunction* c_function_or_null) { - return local_to_ptr(v8::FunctionTemplate::New( + v8::SideEffectType side_effect_type, void* func_ptr1, + const v8::CFunctionInfo* c_function_info1, void* func_ptr2, + const v8::CFunctionInfo* c_function_info2) { + auto overload = v8::MemorySpan{}; + // Support upto 2 overloads. V8 requires TypedArray to have a + // v8::Array overload. + if (func_ptr1) { + if (func_ptr2 == nullptr) { + const v8::CFunction o[] = {v8::CFunction(func_ptr1, c_function_info1)}; + overload = v8::MemorySpan{o, 1}; + } else { + const v8::CFunction o[] = {v8::CFunction(func_ptr1, c_function_info1), + v8::CFunction(func_ptr2, c_function_info2)}; + overload = v8::MemorySpan{o, 2}; + } + } + return local_to_ptr(v8::FunctionTemplate::NewWithCFunctionOverloads( isolate, callback, ptr_to_local(data_or_null), ptr_to_local(signature_or_null), length, constructor_behavior, - side_effect_type, c_function_or_null)); + side_effect_type, overload)); } const v8::Function* v8__FunctionTemplate__GetFunction( diff --git a/src/fast_api.rs b/src/fast_api.rs index 5e5ee857..1057d4e7 100644 --- a/src/fast_api.rs +++ b/src/fast_api.rs @@ -1,7 +1,9 @@ use crate::support::Opaque; use libc::c_void; -use std::mem::transmute_copy; -use std::ptr::NonNull; +use std::{ + mem::align_of, + ptr::{self, NonNull}, +}; extern "C" { fn v8__CTypeInfo__New(ty: CType) -> *mut CTypeInfo; @@ -14,10 +16,6 @@ extern "C" { 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)] @@ -28,15 +26,13 @@ pub struct CFunctionInfo(Opaque); #[derive(Default)] pub struct CFunction(Opaque); -impl CFunction { +impl CFunctionInfo { 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)) + ) -> NonNull { + NonNull::new_unchecked(v8__CFunctionInfo__New(return_type, args_len, args)) } } @@ -152,16 +148,49 @@ struct CTypeSequenceInfo { sequence_type: SequenceType, } +// https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-fast-api-calls.h;l=336 +#[repr(C)] +pub struct FastApiTypedArray { + pub byte_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, +} + +impl FastApiTypedArray { + #[inline] + pub fn get(&self, index: usize) -> T { + debug_assert!(index < self.byte_length); + let mut t: T = Default::default(); + unsafe { + ptr::copy_nonoverlapping(self.data.add(index), &mut t, 1); + } + t + } + + #[inline] + pub fn get_storage_if_aligned(&self) -> Option<&mut [T]> { + if (self.data as usize) % align_of::() != 0 { + return None; + } + Some(unsafe { + std::slice::from_raw_parts_mut( + self.data, + self.byte_length / align_of::(), + ) + }) + } +} + 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()) } - } + fn function(&self) -> *const c_void; } diff --git a/src/template.rs b/src/template.rs index 47d9250c..efda4bf7 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,9 +1,11 @@ +use libc::c_void; + use crate::data::Data; 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::CFunctionInfo; use crate::fast_api::CTypeInfo; use crate::fast_api::FastFunction; use crate::isolate::Isolate; @@ -47,7 +49,10 @@ extern "C" { length: i32, constructor_behavior: ConstructorBehavior, side_effect_type: SideEffectType, - c_function_or_null: *const CFunction, + func_ptr1: *const c_void, + c_function1: *const CFunctionInfo, + func_ptr2: *const c_void, + c_function2: *const CFunctionInfo, ) -> *const FunctionTemplate; fn v8__FunctionTemplate__GetFunction( this: *const FunctionTemplate, @@ -146,6 +151,9 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { self.constructor_behavior, self.side_effect_type, null(), + null(), + null(), + null(), ) }) } @@ -155,17 +163,24 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { pub fn build_fast( self, scope: &mut HandleScope<'s, ()>, - fast_function: impl FastFunction, + overload1: &dyn FastFunction, + overload2: Option<&dyn 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(), - ); + let args = CTypeInfo::new_from_slice(overload1.args()); + let ret = CTypeInfo::new(overload1.return_type()); + let c_fn1 = + CFunctionInfo::new(args.as_ptr(), overload1.args().len(), ret.as_ptr()); + + let c_fn2 = match overload2 { + Some(overload) => { + let args = CTypeInfo::new_from_slice(overload.args()); + let ret = CTypeInfo::new(overload.return_type()); + CFunctionInfo::new(args.as_ptr(), overload.args().len(), ret.as_ptr()) + .as_ptr() + } + None => null(), + }; scope.cast_local(|sd| { v8__FunctionTemplate__New( sd.get_isolate_ptr(), @@ -175,7 +190,10 @@ impl<'s> FunctionBuilder<'s, FunctionTemplate> { self.length, ConstructorBehavior::Throw, self.side_effect_type, - c_fn.as_ptr() as _, + overload1.function(), + c_fn1.as_ptr(), + overload2.map_or(null(), |f| f.function()), + c_fn2, ) }) } diff --git a/tests/test_api.rs b/tests/test_api.rs index eb4b5d95..dad69e6f 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -7036,9 +7036,8 @@ fn test_fast_calls() { fast_api::CType::Uint32 } - type Signature = fn(a: u32, b: u32) -> u32; - fn function(&self) -> Self::Signature { - fast_fn + fn function(&self) -> *const c_void { + fast_fn as _ } } @@ -7060,7 +7059,7 @@ fn test_fast_calls() { let global = context.global(scope); let template = - v8::FunctionTemplate::builder(slow_fn).build_fast(scope, FastTest); + v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &FastTest, None); let name = v8::String::new(scope, "func").unwrap(); let value = template.get_function(scope).unwrap(); @@ -7110,14 +7109,8 @@ fn test_fast_calls_sequence() { fast_api::CType::Uint32 } - type Signature = fn( - receiver: v8::Local, - a: u32, - b: u32, - array: v8::Local, - ) -> u32; - fn function(&self) -> Self::Signature { - fast_fn + fn function(&self) -> *const c_void { + fast_fn as _ } } @@ -7139,7 +7132,7 @@ fn test_fast_calls_sequence() { let global = context.global(scope); let template = - v8::FunctionTemplate::builder(slow_fn).build_fast(scope, FastTest); + v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &FastTest, None); let name = v8::String::new(scope, "func").unwrap(); let value = template.get_function(scope).unwrap(); @@ -7161,12 +7154,6 @@ fn test_fast_calls_sequence() { assert_eq!("fast", unsafe { WHO }); } -#[repr(C)] -pub struct FastApiArrayBuffer { - byte_length: usize, - data: *mut u32, -} - #[test] fn test_fast_calls_arraybuffer() { static mut WHO: &str = "none"; @@ -7174,12 +7161,10 @@ fn test_fast_calls_arraybuffer() { _recv: v8::Local, a: u32, b: u32, - data: *const FastApiArrayBuffer, + data: *const fast_api::FastApiTypedArray, ) -> u32 { unsafe { WHO = "fast" }; - let buf = - unsafe { std::slice::from_raw_parts((*data).data, (*data).byte_length) }; - a + b + buf[0] + a + b + unsafe { &*data }.get(0) } pub struct FastTest; @@ -7197,14 +7182,8 @@ fn test_fast_calls_arraybuffer() { fast_api::CType::Uint32 } - type Signature = fn( - receiver: v8::Local, - a: u32, - b: u32, - data: *const FastApiArrayBuffer, - ) -> u32; - fn function(&self) -> Self::Signature { - fast_fn + fn function(&self) -> *const c_void { + fast_fn as _ } } @@ -7226,7 +7205,7 @@ fn test_fast_calls_arraybuffer() { let global = context.global(scope); let template = - v8::FunctionTemplate::builder(slow_fn).build_fast(scope, FastTest); + v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &FastTest, None); let name = v8::String::new(scope, "func").unwrap(); let value = template.get_function(scope).unwrap(); @@ -7276,9 +7255,8 @@ fn test_fast_calls_reciever() { fast_api::CType::Uint32 } - type Signature = fn(receiver: v8::Local) -> u32; - fn function(&self) -> Self::Signature { - fast_fn + fn function(&self) -> *const c_void { + fast_fn as _ } } @@ -7314,7 +7292,7 @@ fn test_fast_calls_reciever() { ); let template = - v8::FunctionTemplate::builder(slow_fn).build_fast(scope, FastTest); + v8::FunctionTemplate::builder(slow_fn).build_fast(scope, &FastTest, None); let name = v8::String::new(scope, "method").unwrap(); let value = template.get_function(scope).unwrap(); @@ -7339,3 +7317,101 @@ fn test_fast_calls_reciever() { eval(scope, source).unwrap(); assert_eq!("fast", unsafe { WHO }); } + +#[test] +fn test_fast_calls_overload() { + static mut WHO: &str = "none"; + fn fast_fn( + _recv: v8::Local, + data: *const fast_api::FastApiTypedArray, + ) { + unsafe { WHO = "fast_buf" }; + let buf = unsafe { &*data }; + assert_eq!(buf.byte_length, 2); + assert_eq!(buf.get(0), 6); + assert_eq!(buf.get(1), 9); + } + + fn fast_fn2(_recv: v8::Local, data: v8::Local) { + unsafe { WHO = "fast_array" }; + assert_eq!(data.length(), 2); + } + + pub struct FastTest; + impl fast_api::FastFunction for FastTest { + fn args(&self) -> &'static [fast_api::Type] { + &[ + fast_api::Type::V8Value, + fast_api::Type::TypedArray(fast_api::CType::Uint32), + ] + } + + fn function(&self) -> *const c_void { + fast_fn as _ + } + } + + pub struct FastTest2; + impl fast_api::FastFunction for FastTest2 { + fn args(&self) -> &'static [fast_api::Type] { + &[ + fast_api::Type::V8Value, + fast_api::Type::Sequence(fast_api::CType::Void), + ] + } + + fn function(&self) -> *const c_void { + fast_fn2 as _ + } + } + + 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, + Some(&FastTest2), + ); + + 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(data) { return func(data); } + %PrepareFunctionForOptimization(f); + const arr = [6, 9]; + const buf = new Uint32Array(arr); + f(buf); + f(arr); +"#; + eval(scope, source).unwrap(); + assert_eq!("slow", unsafe { WHO }); + + let source = r#" + %OptimizeFunctionOnNextCall(f); + f(buf); + "#; + eval(scope, source).unwrap(); + assert_eq!("fast_buf", unsafe { WHO }); + let source = r#" + %OptimizeFunctionOnNextCall(f); + f(arr); + "#; + eval(scope, source).unwrap(); + assert_eq!("fast_array", unsafe { WHO }); +}