mirror of
https://github.com/denoland/rusty_v8.git
synced 2024-11-24 15:19:31 -05:00
support V8 FastApiCallbackOptions (#1044)
This commit is contained in:
parent
3fbf87aa4b
commit
606fee5ec7
3 changed files with 109 additions and 1 deletions
|
@ -670,7 +670,7 @@ impl_partial_eq! { UnboundScript for UnboundScript use identity }
|
|||
|
||||
/// The superclass of all JavaScript values and objects.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Value(Opaque);
|
||||
|
||||
impl_deref! { Data for Value }
|
||||
|
|
|
@ -75,6 +75,7 @@ pub enum SequenceType {
|
|||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
pub enum CType {
|
||||
Void = 0,
|
||||
Bool,
|
||||
|
@ -85,9 +86,15 @@ pub enum CType {
|
|||
Float32,
|
||||
Float64,
|
||||
V8Value,
|
||||
// https://github.com/v8/v8/blob/492a32943bc34a527f42df2ae15a77154b16cc84/include/v8-fast-api-calls.h#L264-L267
|
||||
// kCallbackOptionsType is not part of the Type enum
|
||||
// because it is only used internally. Use value 255 that is larger
|
||||
// than any valid Type enum.
|
||||
CallbackOptions = 255,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum Type {
|
||||
Void,
|
||||
Bool,
|
||||
|
@ -98,6 +105,7 @@ pub enum Type {
|
|||
Float32,
|
||||
Float64,
|
||||
V8Value,
|
||||
CallbackOptions,
|
||||
Sequence(CType),
|
||||
TypedArray(CType),
|
||||
ArrayBuffer(CType),
|
||||
|
@ -115,6 +123,7 @@ impl From<&Type> for CType {
|
|||
Type::Float32 => CType::Float32,
|
||||
Type::Float64 => CType::Float64,
|
||||
Type::V8Value => CType::V8Value,
|
||||
Type::CallbackOptions => CType::CallbackOptions,
|
||||
Type::Sequence(ty) => *ty,
|
||||
Type::TypedArray(ty) => *ty,
|
||||
Type::ArrayBuffer(ty) => *ty,
|
||||
|
@ -148,6 +157,35 @@ struct CTypeSequenceInfo {
|
|||
sequence_type: SequenceType,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub union FastApiCallbackData {
|
||||
/// `data_ptr` allows for default constructing FastApiCallbackOptions.
|
||||
pub data_ptr: *mut c_void,
|
||||
/// The `data` passed to the FunctionTemplate constructor, or `undefined`.
|
||||
pub data: crate::Value,
|
||||
}
|
||||
|
||||
/// A struct which may be passed to a fast call callback, like so
|
||||
/// ```c
|
||||
/// void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
pub struct FastApiCallbackOptions {
|
||||
/// If the callback wants to signal an error condition or to perform an
|
||||
/// allocation, it must set options.fallback to true and do an early return
|
||||
/// from the fast method. Then V8 checks the value of options.fallback and if
|
||||
/// it's true, falls back to executing the SlowCallback, which is capable of
|
||||
/// reporting the error (either by throwing a JS exception or logging to the
|
||||
/// console) or doing the allocation. It's the embedder's responsibility to
|
||||
/// ensure that the fast callback is idempotent up to the point where error and
|
||||
/// fallback conditions are checked, because otherwise executing the slow
|
||||
/// callback might produce visible side-effects twice.
|
||||
pub fallback: bool,
|
||||
pub data: FastApiCallbackData,
|
||||
/// When called from WebAssembly, a view of the calling module's memory.
|
||||
pub wasm_memory: *const FastApiTypedArray<u8>,
|
||||
}
|
||||
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:v8/include/v8-fast-api-calls.h;l=336
|
||||
#[repr(C)]
|
||||
pub struct FastApiTypedArray<T: Default> {
|
||||
|
|
|
@ -7427,3 +7427,73 @@ fn test_fast_calls_overload() {
|
|||
eval(scope, source).unwrap();
|
||||
assert_eq!("fast_array", unsafe { WHO });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fast_calls_callback_options() {
|
||||
static mut WHO: &str = "none";
|
||||
fn fast_fn(
|
||||
_recv: v8::Local<v8::Object>,
|
||||
options: *mut fast_api::FastApiCallbackOptions,
|
||||
) {
|
||||
if unsafe { WHO == "fast" } {
|
||||
let options = unsafe { &mut *options };
|
||||
options.fallback = true; // Go back to slow path.
|
||||
} else {
|
||||
unsafe { WHO = "fast" };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FastTest;
|
||||
impl fast_api::FastFunction for FastTest {
|
||||
fn args(&self) -> &'static [fast_api::Type] {
|
||||
&[fast_api::Type::V8Value, fast_api::Type::CallbackOptions]
|
||||
}
|
||||
|
||||
fn function(&self) -> *const c_void {
|
||||
fast_fn 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, None);
|
||||
|
||||
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() { return func(); }
|
||||
%PrepareFunctionForOptimization(f);
|
||||
f();
|
||||
"#;
|
||||
eval(scope, source).unwrap();
|
||||
assert_eq!("slow", unsafe { WHO });
|
||||
|
||||
let source = r#"
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f();
|
||||
"#;
|
||||
eval(scope, source).unwrap();
|
||||
assert_eq!("fast", unsafe { WHO });
|
||||
let source = r#"
|
||||
f(); // Second call fallbacks back to slow path.
|
||||
"#;
|
||||
eval(scope, source).unwrap();
|
||||
assert_eq!("slow", unsafe { WHO });
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue