Support Fast API Overloads (#1031)

This commit is contained in:
Divy Srivastava 2022-07-16 19:57:58 +05:30 committed by GitHub
parent 5c42d601cf
commit 3be46ecc52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 216 additions and 82 deletions

View File

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

View File

@ -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;
}

View File

@ -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,
)
})
}

View File

@ -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 });
}