0
0
Fork 0
mirror of https://github.com/denoland/rusty_v8.git synced 2024-12-25 08:39:15 -05:00

V8 Fast API Calls (#1021)

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Divy Srivastava 2022-07-07 16:30:35 +05:30 committed by GitHub
parent e6b443a6e8
commit 1d0a4c1792
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 280 additions and 6 deletions

View file

@ -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<v8::CTypeInfo> u = std::make_unique<v8::CTypeInfo>(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<v8::CFunction> c_function = std::make_unique<v8::CFunction>(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<v8::CFunctionInfo> info = std::make_unique<v8::CFunctionInfo>(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,

150
src/fast_api.rs Normal file
View file

@ -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<CFunction> {
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<CTypeInfo> {
unsafe { NonNull::new_unchecked(v8__CTypeInfo__New(ty)) }
}
pub(crate) 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(),
))
}
}
}
#[derive(Clone, Copy, PartialEq, 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)]
#[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()) }
}
}

View file

@ -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.

View file

@ -38,6 +38,7 @@ mod date;
mod exception;
mod external;
mod external_references;
pub mod fast_api;
mod fixed_array;
mod function;
mod handle;

View file

@ -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.

View file

@ -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::<CheckData>().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 });
}