mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-11-24 15:19:31 -05:00
Support Fast API Overloads (#1031)
This commit is contained in:
parent
5c42d601cf
commit
3be46ecc52
4 changed files with 216 additions and 82 deletions
|
@ -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<v8::CTypeInfo> u = std::make_unique<v8::CTypeInfo>(v8::CTypeInfo(ty));
|
||||
std::unique_ptr<v8::CTypeInfo> u =
|
||||
std::make_unique<v8::CTypeInfo>(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<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));
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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<const v8::CFunction>{};
|
||||
// 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<const v8::CFunction>{o, 1};
|
||||
} else {
|
||||
const v8::CFunction o[] = {v8::CFunction(func_ptr1, c_function_info1),
|
||||
v8::CFunction(func_ptr2, c_function_info2)};
|
||||
overload = v8::MemorySpan<const v8::CFunction>{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(
|
||||
|
|
|
@ -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<CFunction> {
|
||||
let info = v8__CFunctionInfo__New(return_type, args_len, args);
|
||||
NonNull::new_unchecked(v8__CFunction__New(func_ptr, info))
|
||||
) -> NonNull<CFunctionInfo> {
|
||||
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<T: Default> {
|
||||
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<T: Default> FastApiTypedArray<T> {
|
||||
#[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::<T>() != 0 {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
self.data,
|
||||
self.byte_length / align_of::<T>(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<v8::Object>,
|
||||
a: u32,
|
||||
b: u32,
|
||||
array: v8::Local<v8::Array>,
|
||||
) -> 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<v8::Object>,
|
||||
a: u32,
|
||||
b: u32,
|
||||
data: *const FastApiArrayBuffer,
|
||||
data: *const fast_api::FastApiTypedArray<u32>,
|
||||
) -> 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<v8::Object>,
|
||||
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<v8::Object>) -> 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<v8::Object>,
|
||||
data: *const fast_api::FastApiTypedArray<u32>,
|
||||
) {
|
||||
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<v8::Object>, data: v8::Local<v8::Array>) {
|
||||
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 });
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue