mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
refactor(ops): ops2 supports result in fast path (#19603)
Implements `Result` in fast-calls. Note that the approach here is slightly different. Rather than store the last result in the `OpState`, we put it into the `OpCtx` which saves us a lookup and lock in the error case. We do not have to lock this field as it's guaranteed only one runtime and thread can ever access it. The fastcall path for many ops can avoid doing a great deal of work, even for `Result` return values. In the previous iteration of `ops`, all `Result`-returning functions would fetch and lock the `OpState`, regardless of whether it was used or not.
This commit is contained in:
parent
3fe44a50c3
commit
8fe9b8a4cc
13 changed files with 551 additions and 221 deletions
35
core/ops.rs
35
core/ops.rs
|
@ -14,6 +14,7 @@ use futures::Future;
|
|||
use pin_project::pin_project;
|
||||
use serde::Serialize;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::ptr::NonNull;
|
||||
|
@ -106,7 +107,10 @@ pub fn to_op_result<R: Serialize + 'static>(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(@AaronO): optimize OpCtx(s) mem usage ?
|
||||
/// Per-op context.
|
||||
///
|
||||
// Note: We don't worry too much about the size of this struct because it's allocated once per realm, and is
|
||||
// stored in a contiguous array.
|
||||
pub struct OpCtx {
|
||||
pub id: OpId,
|
||||
pub state: Rc<RefCell<OpState>>,
|
||||
|
@ -114,6 +118,8 @@ pub struct OpCtx {
|
|||
pub fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
|
||||
pub runtime_state: Weak<RefCell<JsRuntimeState>>,
|
||||
pub(crate) context_state: Rc<RefCell<ContextState>>,
|
||||
/// If the last fast op failed, stores the error to be picked up by the slow op.
|
||||
pub(crate) last_fast_error: UnsafeCell<Option<AnyError>>,
|
||||
}
|
||||
|
||||
impl OpCtx {
|
||||
|
@ -145,8 +151,35 @@ impl OpCtx {
|
|||
decl,
|
||||
context_state,
|
||||
fast_fn_c_info,
|
||||
last_fast_error: UnsafeCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// This takes the last error from an [`OpCtx`], assuming that no other code anywhere
|
||||
/// can hold a `&mut` to the last_fast_error field.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must only be called from op implementations.
|
||||
#[inline(always)]
|
||||
pub unsafe fn unsafely_take_last_error_for_ops_only(
|
||||
&self,
|
||||
) -> Option<AnyError> {
|
||||
let opt_mut = &mut *self.last_fast_error.get();
|
||||
opt_mut.take()
|
||||
}
|
||||
|
||||
/// This set the last error for an [`OpCtx`], assuming that no other code anywhere
|
||||
/// can hold a `&mut` to the last_fast_error field.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Must only be called from op implementations.
|
||||
#[inline(always)]
|
||||
pub unsafe fn unsafely_set_last_error_for_ops_only(&self, error: AnyError) {
|
||||
let opt_mut = &mut *self.last_fast_error.get();
|
||||
*opt_mut = Some(error);
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintains the resources and ops inside a JS runtime.
|
||||
|
|
|
@ -215,12 +215,15 @@ mod tests {
|
|||
use crate::JsRuntime;
|
||||
use crate::RuntimeOptions;
|
||||
use deno_ops::op2;
|
||||
use std::cell::Cell;
|
||||
|
||||
crate::extension!(
|
||||
testing,
|
||||
ops = [
|
||||
op_test_fail,
|
||||
op_test_add,
|
||||
op_test_add_option,
|
||||
op_test_result_void_switch,
|
||||
op_test_result_void_ok,
|
||||
op_test_result_void_err,
|
||||
op_test_result_primitive_ok,
|
||||
|
@ -228,47 +231,84 @@ mod tests {
|
|||
]
|
||||
);
|
||||
|
||||
thread_local! {
|
||||
static FAIL: Cell<bool> = Cell::new(false)
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_fail() {
|
||||
FAIL.with(|b| {
|
||||
println!("fail");
|
||||
b.set(true)
|
||||
})
|
||||
}
|
||||
|
||||
/// Run a test for a single op.
|
||||
fn run_test(
|
||||
fn run_test2(
|
||||
repeat: usize,
|
||||
op: &'static str,
|
||||
test: &'static str,
|
||||
f: impl FnOnce(Result<&v8::Value, anyhow::Error>, &mut v8::HandleScope),
|
||||
) {
|
||||
) -> Result<(), AnyError> {
|
||||
let mut runtime = JsRuntime::new(RuntimeOptions {
|
||||
extensions: vec![testing::init_ext()],
|
||||
..Default::default()
|
||||
});
|
||||
let value: Result<v8::Global<v8::Value>, anyhow::Error> = runtime
|
||||
runtime
|
||||
.execute_script(
|
||||
"",
|
||||
FastString::Owned(
|
||||
format!("const {{ {op} }} = Deno.core.ensureFastOps(); {test}")
|
||||
.into(),
|
||||
format!(
|
||||
r"
|
||||
const {{ op_test_fail, {op} }} = Deno.core.ensureFastOps();
|
||||
function assert(b) {{
|
||||
if (!b) {{
|
||||
op_test_fail();
|
||||
}}
|
||||
}}
|
||||
"
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
);
|
||||
let mut scope: v8::HandleScope =
|
||||
// SAFETY: transmute for test (this lifetime should be safe for this purpose)
|
||||
unsafe { std::mem::transmute(runtime.handle_scope()) };
|
||||
match value {
|
||||
Ok(value) => {
|
||||
let value = value.open(&mut scope);
|
||||
f(Ok(value), &mut scope)
|
||||
}
|
||||
Err(err) => f(Err(err), &mut scope),
|
||||
)
|
||||
.unwrap();
|
||||
FAIL.with(|b| b.set(false));
|
||||
runtime.execute_script(
|
||||
"",
|
||||
FastString::Owned(
|
||||
format!(
|
||||
r"
|
||||
for (let __index__ = 0; __index__ < {repeat}; __index__++) {{
|
||||
{test}
|
||||
}}
|
||||
"
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
)?;
|
||||
if FAIL.with(|b| b.get()) {
|
||||
Err(generic_error("test failed"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
pub async fn test_op_fail() {
|
||||
assert!(run_test2(1, "", "assert(false)").is_err());
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_add(a: u32, b: u32) -> u32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
pub async fn test_op_add() -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_test("op_test_add", "op_test_add(1, 11)", |value, scope| {
|
||||
assert_eq!(value.unwrap().int32_value(scope), Some(12));
|
||||
});
|
||||
Ok(())
|
||||
Ok(run_test2(
|
||||
10000,
|
||||
"op_test_add",
|
||||
"assert(op_test_add(1, 11) == 12)",
|
||||
)?)
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
|
@ -276,59 +316,84 @@ mod tests {
|
|||
a + b.unwrap_or(100)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
pub async fn test_op_add_option() -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_test(
|
||||
// This isn't fast, so we don't repeat it
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_add_option",
|
||||
"op_test_add_option(1, 11)",
|
||||
|value, scope| {
|
||||
assert_eq!(value.unwrap().int32_value(scope), Some(12));
|
||||
},
|
||||
);
|
||||
run_test(
|
||||
"assert(op_test_add_option(1, 11) == 12)",
|
||||
)?;
|
||||
run_test2(
|
||||
1,
|
||||
"op_test_add_option",
|
||||
"op_test_add_option(1, null)",
|
||||
|value, scope| {
|
||||
assert_eq!(value.unwrap().int32_value(scope), Some(101));
|
||||
},
|
||||
);
|
||||
"assert(op_test_add_option(1, null) == 101)",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
thread_local! {
|
||||
static RETURN_COUNT: Cell<usize> = Cell::new(0);
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_result_void_switch() -> Result<(), AnyError> {
|
||||
let count = RETURN_COUNT.with(|count| {
|
||||
let new = count.get() + 1;
|
||||
count.set(new);
|
||||
new
|
||||
});
|
||||
if count > 5000 {
|
||||
Err(generic_error("failed!!!"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_result_void_err() -> Result<(), AnyError> {
|
||||
Err(generic_error("failed!!!"))
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_result_void_ok() -> Result<(), AnyError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
pub async fn test_op_result_void() -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_test(
|
||||
// Test the non-switching kinds
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_result_void_err",
|
||||
"op_test_result_void_err()",
|
||||
|value, _scope| {
|
||||
let js_error = value.err().unwrap().downcast::<JsError>().unwrap();
|
||||
assert_eq!(js_error.message, Some("failed!!!".to_owned()));
|
||||
},
|
||||
);
|
||||
run_test(
|
||||
"op_test_result_void_ok",
|
||||
"op_test_result_void_ok()",
|
||||
|value, _scope| assert!(value.unwrap().is_null_or_undefined()),
|
||||
);
|
||||
"try { op_test_result_void_err(); assert(false) } catch (e) {}",
|
||||
)?;
|
||||
run_test2(10000, "op_test_result_void_ok", "op_test_result_void_ok()")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
pub async fn test_op_result_void_switch(
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
RETURN_COUNT.with(|count| count.set(0));
|
||||
let err = run_test2(
|
||||
10000,
|
||||
"op_test_result_void_switch",
|
||||
"op_test_result_void_switch();",
|
||||
)
|
||||
.expect_err("Expected this to fail");
|
||||
let js_err = err.downcast::<JsError>().unwrap();
|
||||
assert_eq!(js_err.message, Some("failed!!!".into()));
|
||||
assert_eq!(RETURN_COUNT.with(|count| count.get()), 5001);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_result_primitive_err() -> Result<u32, AnyError> {
|
||||
Err(generic_error("failed!!!"))
|
||||
}
|
||||
|
||||
#[op2(core)]
|
||||
#[op2(core, fast)]
|
||||
pub fn op_test_result_primitive_ok() -> Result<u32, AnyError> {
|
||||
Ok(123)
|
||||
}
|
||||
|
@ -336,19 +401,16 @@ mod tests {
|
|||
#[tokio::test]
|
||||
pub async fn test_op_result_primitive(
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_test(
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_result_primitive_err",
|
||||
"op_test_result_primitive_err()",
|
||||
|value, _scope| {
|
||||
let js_error = value.err().unwrap().downcast::<JsError>().unwrap();
|
||||
assert_eq!(js_error.message, Some("failed!!!".to_owned()));
|
||||
},
|
||||
);
|
||||
run_test(
|
||||
"try { op_test_result_primitive_err(); assert(false) } catch (e) {}",
|
||||
)?;
|
||||
run_test2(
|
||||
10000,
|
||||
"op_test_result_primitive_ok",
|
||||
"op_test_result_primitive_ok()",
|
||||
|value, scope| assert_eq!(value.unwrap().int32_value(scope), Some(123)),
|
||||
);
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use quote::quote;
|
|||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub(crate) enum FastValue {
|
||||
pub(crate) enum V8FastCallType {
|
||||
#[default]
|
||||
Void,
|
||||
Bool,
|
||||
|
@ -27,66 +27,74 @@ pub(crate) enum FastValue {
|
|||
Uint32Array,
|
||||
Float64Array,
|
||||
SeqOneByteString,
|
||||
CallbackOptions,
|
||||
}
|
||||
|
||||
impl FastValue {
|
||||
impl V8FastCallType {
|
||||
/// Quote fast value type.
|
||||
fn quote_rust_type(&self) -> TokenStream {
|
||||
fn quote_rust_type(&self, deno_core: &TokenStream) -> TokenStream {
|
||||
match self {
|
||||
FastValue::Void => quote!(()),
|
||||
FastValue::Bool => quote!(bool),
|
||||
FastValue::U32 => quote!(u32),
|
||||
FastValue::I32 => quote!(i32),
|
||||
FastValue::U64 => quote!(u64),
|
||||
FastValue::I64 => quote!(i64),
|
||||
FastValue::F32 => quote!(f32),
|
||||
FastValue::F64 => quote!(f64),
|
||||
FastValue::Pointer => quote!(*mut ::std::ffi::c_void),
|
||||
FastValue::V8Value => unimplemented!("v8::Local<v8::Value>"),
|
||||
FastValue::Uint8Array
|
||||
| FastValue::Uint32Array
|
||||
| FastValue::Float64Array
|
||||
| FastValue::SeqOneByteString => unreachable!(),
|
||||
V8FastCallType::Void => quote!(()),
|
||||
V8FastCallType::Bool => quote!(bool),
|
||||
V8FastCallType::U32 => quote!(u32),
|
||||
V8FastCallType::I32 => quote!(i32),
|
||||
V8FastCallType::U64 => quote!(u64),
|
||||
V8FastCallType::I64 => quote!(i64),
|
||||
V8FastCallType::F32 => quote!(f32),
|
||||
V8FastCallType::F64 => quote!(f64),
|
||||
V8FastCallType::Pointer => quote!(*mut ::std::ffi::c_void),
|
||||
V8FastCallType::V8Value => {
|
||||
quote!(#deno_core::v8::Local<#deno_core::v8::Value>)
|
||||
}
|
||||
V8FastCallType::CallbackOptions => {
|
||||
quote!(*mut #deno_core::v8::fast_api::FastApiCallbackOptions)
|
||||
}
|
||||
V8FastCallType::Uint8Array
|
||||
| V8FastCallType::Uint32Array
|
||||
| V8FastCallType::Float64Array
|
||||
| V8FastCallType::SeqOneByteString => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Quote fast value type's variant.
|
||||
fn quote_ctype(&self) -> TokenStream {
|
||||
match &self {
|
||||
FastValue::Void => quote!(CType::Void),
|
||||
FastValue::Bool => quote!(CType::Bool),
|
||||
FastValue::U32 => quote!(CType::Uint32),
|
||||
FastValue::I32 => quote!(CType::Int32),
|
||||
FastValue::U64 => quote!(CType::Uint64),
|
||||
FastValue::I64 => quote!(CType::Int64),
|
||||
FastValue::F32 => quote!(CType::Float32),
|
||||
FastValue::F64 => quote!(CType::Float64),
|
||||
FastValue::Pointer => quote!(CType::Pointer),
|
||||
FastValue::V8Value => quote!(CType::V8Value),
|
||||
FastValue::Uint8Array => unreachable!(),
|
||||
FastValue::Uint32Array => unreachable!(),
|
||||
FastValue::Float64Array => unreachable!(),
|
||||
FastValue::SeqOneByteString => quote!(CType::SeqOneByteString),
|
||||
V8FastCallType::Void => quote!(CType::Void),
|
||||
V8FastCallType::Bool => quote!(CType::Bool),
|
||||
V8FastCallType::U32 => quote!(CType::Uint32),
|
||||
V8FastCallType::I32 => quote!(CType::Int32),
|
||||
V8FastCallType::U64 => quote!(CType::Uint64),
|
||||
V8FastCallType::I64 => quote!(CType::Int64),
|
||||
V8FastCallType::F32 => quote!(CType::Float32),
|
||||
V8FastCallType::F64 => quote!(CType::Float64),
|
||||
V8FastCallType::Pointer => quote!(CType::Pointer),
|
||||
V8FastCallType::V8Value => quote!(CType::V8Value),
|
||||
V8FastCallType::CallbackOptions => quote!(CType::CallbackOptions),
|
||||
V8FastCallType::Uint8Array => unreachable!(),
|
||||
V8FastCallType::Uint32Array => unreachable!(),
|
||||
V8FastCallType::Float64Array => unreachable!(),
|
||||
V8FastCallType::SeqOneByteString => quote!(CType::SeqOneByteString),
|
||||
}
|
||||
}
|
||||
|
||||
/// Quote fast value type's variant.
|
||||
fn quote_type(&self) -> TokenStream {
|
||||
match &self {
|
||||
FastValue::Void => quote!(Type::Void),
|
||||
FastValue::Bool => quote!(Type::Bool),
|
||||
FastValue::U32 => quote!(Type::Uint32),
|
||||
FastValue::I32 => quote!(Type::Int32),
|
||||
FastValue::U64 => quote!(Type::Uint64),
|
||||
FastValue::I64 => quote!(Type::Int64),
|
||||
FastValue::F32 => quote!(Type::Float32),
|
||||
FastValue::F64 => quote!(Type::Float64),
|
||||
FastValue::Pointer => quote!(Type::Pointer),
|
||||
FastValue::V8Value => quote!(Type::V8Value),
|
||||
FastValue::Uint8Array => quote!(Type::TypedArray(CType::Uint8)),
|
||||
FastValue::Uint32Array => quote!(Type::TypedArray(CType::Uint32)),
|
||||
FastValue::Float64Array => quote!(Type::TypedArray(CType::Float64)),
|
||||
FastValue::SeqOneByteString => quote!(Type::SeqOneByteString),
|
||||
V8FastCallType::Void => quote!(Type::Void),
|
||||
V8FastCallType::Bool => quote!(Type::Bool),
|
||||
V8FastCallType::U32 => quote!(Type::Uint32),
|
||||
V8FastCallType::I32 => quote!(Type::Int32),
|
||||
V8FastCallType::U64 => quote!(Type::Uint64),
|
||||
V8FastCallType::I64 => quote!(Type::Int64),
|
||||
V8FastCallType::F32 => quote!(Type::Float32),
|
||||
V8FastCallType::F64 => quote!(Type::Float64),
|
||||
V8FastCallType::Pointer => quote!(Type::Pointer),
|
||||
V8FastCallType::V8Value => quote!(Type::V8Value),
|
||||
V8FastCallType::CallbackOptions => quote!(Type::CallbackOptions),
|
||||
V8FastCallType::Uint8Array => quote!(Type::TypedArray(CType::Uint8)),
|
||||
V8FastCallType::Uint32Array => quote!(Type::TypedArray(CType::Uint32)),
|
||||
V8FastCallType::Float64Array => quote!(Type::TypedArray(CType::Float64)),
|
||||
V8FastCallType::SeqOneByteString => quote!(Type::SeqOneByteString),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,104 +103,181 @@ pub fn generate_dispatch_fast(
|
|||
generator_state: &mut GeneratorState,
|
||||
signature: &ParsedSignature,
|
||||
) -> Result<Option<(TokenStream, TokenStream)>, V8MappingError> {
|
||||
// Result not fast-call compatible (yet)
|
||||
if matches!(signature.ret_val, RetVal::Result(..)) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut inputs = vec![];
|
||||
for arg in &signature.args {
|
||||
let fv = match arg {
|
||||
Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
|
||||
Arg::Numeric(NumericArg::bool) => FastValue::Bool,
|
||||
Arg::Numeric(NumericArg::u32)
|
||||
| Arg::Numeric(NumericArg::u16)
|
||||
| Arg::Numeric(NumericArg::u8) => FastValue::U32,
|
||||
Arg::Numeric(NumericArg::i32)
|
||||
| Arg::Numeric(NumericArg::i16)
|
||||
| Arg::Numeric(NumericArg::i8)
|
||||
| Arg::Numeric(NumericArg::__SMI__) => FastValue::I32,
|
||||
Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
|
||||
FastValue::U64
|
||||
}
|
||||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
FastValue::I64
|
||||
}
|
||||
_ => {
|
||||
return Err(V8MappingError::NoMapping("a fast argument", arg.clone()))
|
||||
}
|
||||
let Some(fv) = map_arg_to_v8_fastcall_type(arg)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
inputs.push(fv);
|
||||
}
|
||||
let mut names = inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| format_ident!("arg{i}"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ret_val = match &signature.ret_val {
|
||||
RetVal::Infallible(arg) => arg,
|
||||
RetVal::Result(arg) => arg,
|
||||
};
|
||||
|
||||
let output = match ret_val {
|
||||
Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
|
||||
Arg::Void => FastValue::Void,
|
||||
Arg::Numeric(NumericArg::bool) => FastValue::Bool,
|
||||
Arg::Numeric(NumericArg::u32)
|
||||
| Arg::Numeric(NumericArg::u16)
|
||||
| Arg::Numeric(NumericArg::u8) => FastValue::U32,
|
||||
Arg::Numeric(NumericArg::i32)
|
||||
| Arg::Numeric(NumericArg::i16)
|
||||
| Arg::Numeric(NumericArg::i8) => FastValue::I32,
|
||||
Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
|
||||
FastValue::U64
|
||||
}
|
||||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
FastValue::I64
|
||||
}
|
||||
Arg::Special(_) => return Ok(None),
|
||||
_ => {
|
||||
return Err(V8MappingError::NoMapping(
|
||||
"a fast return value",
|
||||
ret_val.clone(),
|
||||
))
|
||||
}
|
||||
let output = match map_retval_to_v8_fastcall_type(ret_val)? {
|
||||
None => return Ok(None),
|
||||
Some(rv) => rv,
|
||||
};
|
||||
|
||||
let GeneratorState {
|
||||
fast_function,
|
||||
deno_core,
|
||||
result,
|
||||
opctx,
|
||||
fast_api_callback_options,
|
||||
needs_fast_api_callback_options,
|
||||
needs_fast_opctx,
|
||||
..
|
||||
} = &generator_state;
|
||||
} = generator_state;
|
||||
|
||||
let input_types = inputs.iter().map(|fv| fv.quote_type());
|
||||
let handle_error = match signature.ret_val {
|
||||
RetVal::Infallible(_) => quote!(),
|
||||
RetVal::Result(_) => {
|
||||
*needs_fast_api_callback_options = true;
|
||||
*needs_fast_opctx = true;
|
||||
inputs.push(V8FastCallType::CallbackOptions);
|
||||
quote! {
|
||||
let #result = match #result {
|
||||
Ok(#result) => #result,
|
||||
Err(err) => {
|
||||
// FASTCALL FALLBACK: This is where we set the errors for the slow-call error pickup path. There
|
||||
// is no code running between this and the other FASTCALL FALLBACK comment, except some V8 code
|
||||
// required to perform the fallback process. This is why the below call is safe.
|
||||
|
||||
// The reason we need to do this is because V8 does not allow exceptions to be thrown from the
|
||||
// fast call. Instead, you are required to set the fallback flag, which indicates to V8 that it
|
||||
// should re-call the slow version of the function. Technically the slow call should perform the
|
||||
// same operation and then throw the same error (because it should be idempotent), but in our
|
||||
// case we stash the error and pick it up on the slow path before doing any work.
|
||||
|
||||
// TODO(mmastrac): We should allow an #[op] flag to re-perform slow calls without the error path when
|
||||
// the method is performance sensitive.
|
||||
|
||||
// SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called,
|
||||
// allowing us to perform this one little bit of mutable magic.
|
||||
unsafe { #opctx.unsafely_set_last_error_for_ops_only(err); }
|
||||
#fast_api_callback_options.fallback = true;
|
||||
return ::std::default::Default::default();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let input_types = inputs.iter().map(|fv| fv.quote_type()).collect::<Vec<_>>();
|
||||
let output_type = output.quote_ctype();
|
||||
|
||||
let fast_definition = quote! {
|
||||
use #deno_core::v8::fast_api::Type;
|
||||
use #deno_core::v8::fast_api::CType;
|
||||
#deno_core::v8::fast_api::FastFunction::new(
|
||||
&[ #( #input_types ),* ],
|
||||
&[ Type::V8Value, #( #input_types ),* ],
|
||||
#output_type,
|
||||
Self::#fast_function as *const ::std::ffi::c_void
|
||||
)
|
||||
};
|
||||
|
||||
let output_type = output.quote_rust_type();
|
||||
let names = &inputs
|
||||
let output_type = output.quote_rust_type(deno_core);
|
||||
let mut types = inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, _)| format_ident!("arg{i}"))
|
||||
.map(|rv| rv.quote_rust_type(deno_core))
|
||||
.collect::<Vec<_>>();
|
||||
let types = inputs.iter().map(|rv| rv.quote_rust_type());
|
||||
|
||||
let call_args = names.clone();
|
||||
|
||||
let with_fast_api_callback_options = if *needs_fast_api_callback_options {
|
||||
types.push(V8FastCallType::CallbackOptions.quote_rust_type(deno_core));
|
||||
names.push(fast_api_callback_options.clone());
|
||||
quote! {
|
||||
let #fast_api_callback_options = unsafe { &mut *#fast_api_callback_options };
|
||||
}
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let with_opctx = if *needs_fast_opctx {
|
||||
quote!(
|
||||
let #opctx = unsafe {
|
||||
&*(#deno_core::v8::Local::<v8::External>::cast(unsafe { #fast_api_callback_options.data.data }).value()
|
||||
as *const #deno_core::_ops::OpCtx)
|
||||
};
|
||||
)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let fast_fn = quote!(
|
||||
fn #fast_function(
|
||||
_: #deno_core::v8::Local<#deno_core::v8::Object>,
|
||||
#( #names: #types, )*
|
||||
) -> #output_type {
|
||||
#(
|
||||
let #names = #names as _;
|
||||
)*
|
||||
Self::call(#(#names),*)
|
||||
#with_fast_api_callback_options
|
||||
#with_opctx
|
||||
let #result = Self::call(#(#call_args as _),*);
|
||||
#handle_error
|
||||
#result
|
||||
}
|
||||
);
|
||||
|
||||
Ok(Some((fast_definition, fast_fn)))
|
||||
}
|
||||
|
||||
fn map_arg_to_v8_fastcall_type(
|
||||
arg: &Arg,
|
||||
) -> Result<Option<V8FastCallType>, V8MappingError> {
|
||||
let rv = match arg {
|
||||
Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
|
||||
Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool,
|
||||
Arg::Numeric(NumericArg::u32)
|
||||
| Arg::Numeric(NumericArg::u16)
|
||||
| Arg::Numeric(NumericArg::u8) => V8FastCallType::U32,
|
||||
Arg::Numeric(NumericArg::i32)
|
||||
| Arg::Numeric(NumericArg::i16)
|
||||
| Arg::Numeric(NumericArg::i8)
|
||||
| Arg::Numeric(NumericArg::__SMI__) => V8FastCallType::I32,
|
||||
Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
|
||||
V8FastCallType::U64
|
||||
}
|
||||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
V8FastCallType::I64
|
||||
}
|
||||
_ => return Err(V8MappingError::NoMapping("a fast argument", arg.clone())),
|
||||
};
|
||||
Ok(Some(rv))
|
||||
}
|
||||
|
||||
fn map_retval_to_v8_fastcall_type(
|
||||
arg: &Arg,
|
||||
) -> Result<Option<V8FastCallType>, V8MappingError> {
|
||||
let rv = match arg {
|
||||
Arg::OptionNumeric(_) | Arg::SerdeV8(_) => return Ok(None),
|
||||
Arg::Void => V8FastCallType::Void,
|
||||
Arg::Numeric(NumericArg::bool) => V8FastCallType::Bool,
|
||||
Arg::Numeric(NumericArg::u32)
|
||||
| Arg::Numeric(NumericArg::u16)
|
||||
| Arg::Numeric(NumericArg::u8) => V8FastCallType::U32,
|
||||
Arg::Numeric(NumericArg::i32)
|
||||
| Arg::Numeric(NumericArg::i16)
|
||||
| Arg::Numeric(NumericArg::i8) => V8FastCallType::I32,
|
||||
Arg::Numeric(NumericArg::u64) | Arg::Numeric(NumericArg::usize) => {
|
||||
V8FastCallType::U64
|
||||
}
|
||||
Arg::Numeric(NumericArg::i64) | Arg::Numeric(NumericArg::isize) => {
|
||||
V8FastCallType::I64
|
||||
}
|
||||
Arg::Special(_) => return Ok(None),
|
||||
_ => {
|
||||
return Err(V8MappingError::NoMapping(
|
||||
"a fast return value",
|
||||
arg.clone(),
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(Some(rv))
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use super::MacroConfig;
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
use super::generator_state::GeneratorState;
|
||||
use super::signature::Arg;
|
||||
|
@ -9,11 +10,31 @@ use super::V8MappingError;
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn generate_dispatch_slow(
|
||||
pub(crate) fn generate_dispatch_slow(
|
||||
config: &MacroConfig,
|
||||
generator_state: &mut GeneratorState,
|
||||
signature: &ParsedSignature,
|
||||
) -> Result<TokenStream, V8MappingError> {
|
||||
let mut output = TokenStream::new();
|
||||
|
||||
// Fast ops require the slow op to check op_ctx for the last error
|
||||
if config.fast && matches!(signature.ret_val, RetVal::Result(_)) {
|
||||
generator_state.needs_opctx = true;
|
||||
let throw_exception = throw_exception(generator_state)?;
|
||||
// If the fast op returned an error, we must throw it rather than doing work.
|
||||
output.extend(quote!{
|
||||
// FASTCALL FALLBACK: This is where we pick up the errors for the slow-call error pickup
|
||||
// path. There is no code running between this and the other FASTCALL FALLBACK comment,
|
||||
// except some V8 code required to perform the fallback process. This is why the below call is safe.
|
||||
|
||||
// SAFETY: We guarantee that OpCtx has no mutable references once ops are live and being called,
|
||||
// allowing us to perform this one little bit of mutable magic.
|
||||
if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
|
||||
#throw_exception
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (index, arg) in signature.args.iter().enumerate() {
|
||||
output.extend(extract_arg(generator_state, index)?);
|
||||
output.extend(from_arg(generator_state, index, arg)?);
|
||||
|
@ -27,6 +48,12 @@ pub fn generate_dispatch_slow(
|
|||
quote!()
|
||||
};
|
||||
|
||||
let with_opctx = if generator_state.needs_opctx {
|
||||
with_opctx(generator_state)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let with_retval = if generator_state.needs_retval {
|
||||
with_retval(generator_state)
|
||||
} else {
|
||||
|
@ -51,6 +78,7 @@ pub fn generate_dispatch_slow(
|
|||
#with_scope
|
||||
#with_retval
|
||||
#with_args
|
||||
#with_opctx
|
||||
|
||||
#output
|
||||
}})
|
||||
|
@ -94,9 +122,11 @@ fn with_opctx(generator_state: &mut GeneratorState) -> TokenStream {
|
|||
deno_core,
|
||||
opctx,
|
||||
fn_args,
|
||||
needs_args,
|
||||
..
|
||||
} = &generator_state;
|
||||
} = generator_state;
|
||||
|
||||
*needs_args = true;
|
||||
quote!(let #opctx = unsafe {
|
||||
&*(#deno_core::v8::Local::<#deno_core::v8::External>::cast(#fn_args.data()).value()
|
||||
as *const #deno_core::_ops::OpCtx)
|
||||
|
@ -246,56 +276,68 @@ pub fn return_value_infallible(
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn return_value_result(
|
||||
fn return_value_result(
|
||||
generator_state: &mut GeneratorState,
|
||||
ret_type: &Arg,
|
||||
) -> Result<TokenStream, V8MappingError> {
|
||||
let infallible = return_value_infallible(generator_state, ret_type)?;
|
||||
let exception = throw_exception(generator_state)?;
|
||||
|
||||
let GeneratorState { result, .. } = &generator_state;
|
||||
|
||||
let tokens = quote!(
|
||||
match #result {
|
||||
Ok(#result) => {
|
||||
#infallible
|
||||
}
|
||||
Err(err) => {
|
||||
#exception
|
||||
}
|
||||
};
|
||||
);
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
/// Generates code to throw an exception, adding required additional dependencies as needed.
|
||||
fn throw_exception(
|
||||
generator_state: &mut GeneratorState,
|
||||
) -> Result<TokenStream, V8MappingError> {
|
||||
let maybe_scope = if generator_state.needs_scope {
|
||||
quote!()
|
||||
} else {
|
||||
with_scope(generator_state)
|
||||
};
|
||||
|
||||
let maybe_args = if generator_state.needs_args {
|
||||
quote!()
|
||||
} else {
|
||||
with_fn_args(generator_state)
|
||||
};
|
||||
|
||||
let maybe_opctx = if generator_state.needs_opctx {
|
||||
quote!()
|
||||
} else {
|
||||
with_opctx(generator_state)
|
||||
};
|
||||
|
||||
let maybe_args = if generator_state.needs_args {
|
||||
quote!()
|
||||
} else {
|
||||
with_fn_args(generator_state)
|
||||
};
|
||||
|
||||
let GeneratorState {
|
||||
deno_core,
|
||||
result,
|
||||
scope,
|
||||
opctx,
|
||||
..
|
||||
} = &generator_state;
|
||||
|
||||
let tokens = quote!(
|
||||
match #result {
|
||||
Ok(#result) => {
|
||||
#infallible
|
||||
}
|
||||
Err(err) => {
|
||||
#maybe_scope
|
||||
#maybe_args
|
||||
#maybe_opctx
|
||||
let opstate = ::std::cell::RefCell::borrow(&*#opctx.state);
|
||||
let exception = #deno_core::error::to_v8_error(
|
||||
#scope,
|
||||
opstate.get_error_class_fn,
|
||||
&err,
|
||||
);
|
||||
scope.throw_exception(exception);
|
||||
return;
|
||||
}
|
||||
};
|
||||
);
|
||||
Ok(tokens)
|
||||
Ok(quote! {
|
||||
#maybe_scope
|
||||
#maybe_args
|
||||
#maybe_opctx
|
||||
let opstate = ::std::cell::RefCell::borrow(&*#opctx.state);
|
||||
let exception = #deno_core::error::to_v8_error(
|
||||
#scope,
|
||||
opstate.get_error_class_fn,
|
||||
&err,
|
||||
);
|
||||
scope.throw_exception(exception);
|
||||
return;
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ pub struct GeneratorState {
|
|||
pub fn_args: Ident,
|
||||
/// The `OpCtx` used for various information required for some ops.
|
||||
pub opctx: Ident,
|
||||
/// The `FastApiCallbackOptions` used in fast calls for fallback returns.
|
||||
pub fast_api_callback_options: Ident,
|
||||
/// The `v8::ReturnValue` used in the slow function
|
||||
pub retval: Ident,
|
||||
/// The "slow" function (ie: the one that isn't a fastcall)
|
||||
|
@ -33,4 +35,6 @@ pub struct GeneratorState {
|
|||
pub needs_scope: bool,
|
||||
pub needs_opstate: bool,
|
||||
pub needs_opctx: bool,
|
||||
pub needs_fast_opctx: bool,
|
||||
pub needs_fast_api_callback_options: bool,
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub enum V8MappingError {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MacroConfig {
|
||||
pub(crate) struct MacroConfig {
|
||||
pub core: bool,
|
||||
pub fast: bool,
|
||||
}
|
||||
|
@ -135,6 +135,8 @@ fn generate_op2(
|
|||
let opctx = Ident::new("opctx", Span::call_site());
|
||||
let slow_function = Ident::new("slow_function", Span::call_site());
|
||||
let fast_function = Ident::new("fast_function", Span::call_site());
|
||||
let fast_api_callback_options =
|
||||
Ident::new("fast_api_callback_options", Span::call_site());
|
||||
|
||||
let deno_core = if config.core {
|
||||
syn2::parse_str::<Path>("crate")
|
||||
|
@ -151,6 +153,7 @@ fn generate_op2(
|
|||
scope,
|
||||
info,
|
||||
opctx,
|
||||
fast_api_callback_options,
|
||||
deno_core,
|
||||
result,
|
||||
retval,
|
||||
|
@ -161,10 +164,14 @@ fn generate_op2(
|
|||
needs_scope: false,
|
||||
needs_opctx: false,
|
||||
needs_opstate: false,
|
||||
needs_fast_opctx: false,
|
||||
needs_fast_api_callback_options: false,
|
||||
};
|
||||
|
||||
let name = func.sig.ident;
|
||||
let slow_fn = generate_dispatch_slow(&mut generator_state, &signature)?;
|
||||
|
||||
let slow_fn =
|
||||
generate_dispatch_slow(&config, &mut generator_state, &signature)?;
|
||||
let (fast_definition, fast_fn) =
|
||||
match generate_dispatch_fast(&mut generator_state, &signature)? {
|
||||
Some((fast_definition, fast_fn)) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ impl op_add {
|
|||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::Uint32, Type::Uint32],
|
||||
&[Type::V8Value, Type::Uint32, Type::Uint32],
|
||||
CType::Uint32,
|
||||
Self::fast_function as *const ::std::ffi::c_void,
|
||||
)
|
||||
|
@ -43,9 +43,8 @@ impl op_add {
|
|||
arg0: u32,
|
||||
arg1: u32,
|
||||
) -> u32 {
|
||||
let arg0 = arg0 as _;
|
||||
let arg1 = arg1 as _;
|
||||
Self::call(arg0, arg1)
|
||||
let result = Self::call(arg0 as _, arg1 as _);
|
||||
result
|
||||
}
|
||||
#[inline(always)]
|
||||
fn call(a: u32, b: u32) -> u32 {
|
||||
|
|
|
@ -13,7 +13,7 @@ impl op_has_doc_comment {
|
|||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[],
|
||||
&[Type::V8Value],
|
||||
CType::Void,
|
||||
Self::fast_function as *const ::std::ffi::c_void,
|
||||
)
|
||||
|
@ -28,7 +28,8 @@ impl op_has_doc_comment {
|
|||
let result = Self::call();
|
||||
}
|
||||
fn fast_function(_: deno_core::v8::Local<deno_core::v8::Object>) -> () {
|
||||
Self::call()
|
||||
let result = Self::call();
|
||||
result
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn call() -> () {}
|
||||
|
|
|
@ -9,7 +9,15 @@ impl op_u32_with_result {
|
|||
name: stringify!(op_u32_with_result),
|
||||
v8_fn_ptr: Self::slow_function as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::CallbackOptions],
|
||||
CType::Uint32,
|
||||
Self::fast_function as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
|
@ -20,6 +28,27 @@ impl op_u32_with_result {
|
|||
let mut rv = deno_core::v8::ReturnValue::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||
as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opstate = ::std::cell::RefCell::borrow(&*opctx.state);
|
||||
let exception = deno_core::error::to_v8_error(
|
||||
scope,
|
||||
opstate.get_error_class_fn,
|
||||
&err,
|
||||
);
|
||||
scope.throw_exception(exception);
|
||||
return;
|
||||
}
|
||||
let result = Self::call();
|
||||
match result {
|
||||
Ok(result) => {
|
||||
|
@ -30,10 +59,6 @@ impl op_u32_with_result {
|
|||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data())
|
||||
.value() as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
let opstate = ::std::cell::RefCell::borrow(&*opctx.state);
|
||||
let exception = deno_core::error::to_v8_error(
|
||||
scope,
|
||||
|
@ -45,6 +70,30 @@ impl op_u32_with_result {
|
|||
}
|
||||
};
|
||||
}
|
||||
fn fast_function(
|
||||
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
|
||||
) -> u32 {
|
||||
let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<
|
||||
v8::External,
|
||||
>::cast(unsafe { fast_api_callback_options.data.data })
|
||||
.value() as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
let result = Self::call();
|
||||
let result = match result {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
unsafe {
|
||||
opctx.unsafely_set_last_error_for_ops_only(err);
|
||||
}
|
||||
fast_api_callback_options.fallback = true;
|
||||
return ::std::default::Default::default();
|
||||
}
|
||||
};
|
||||
result
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn call() -> Result<u32, AnyError> {}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2]
|
||||
#[op2(fast)]
|
||||
pub fn op_u32_with_result() -> Result<u32, AnyError> {}
|
||||
|
|
|
@ -9,7 +9,15 @@ impl op_void_with_result {
|
|||
name: stringify!(op_void_with_result),
|
||||
v8_fn_ptr: Self::slow_function as _,
|
||||
enabled: true,
|
||||
fast_fn: None,
|
||||
fast_fn: Some({
|
||||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::V8Value, Type::CallbackOptions],
|
||||
CType::Void,
|
||||
Self::fast_function as *const ::std::ffi::c_void,
|
||||
)
|
||||
}),
|
||||
is_async: false,
|
||||
is_unstable: false,
|
||||
is_v8: false,
|
||||
|
@ -17,6 +25,27 @@ impl op_void_with_result {
|
|||
}
|
||||
}
|
||||
pub extern "C" fn slow_function(info: *const deno_core::v8::FunctionCallbackInfo) {
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||
as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
if let Some(err) = unsafe { opctx.unsafely_take_last_error_for_ops_only() } {
|
||||
let scope = &mut unsafe { deno_core::v8::CallbackScope::new(&*info) };
|
||||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opstate = ::std::cell::RefCell::borrow(&*opctx.state);
|
||||
let exception = deno_core::error::to_v8_error(
|
||||
scope,
|
||||
opstate.get_error_class_fn,
|
||||
&err,
|
||||
);
|
||||
scope.throw_exception(exception);
|
||||
return;
|
||||
}
|
||||
let result = Self::call();
|
||||
match result {
|
||||
Ok(result) => {}
|
||||
|
@ -25,10 +54,6 @@ impl op_void_with_result {
|
|||
let args = deno_core::v8::FunctionCallbackArguments::from_function_callback_info(unsafe {
|
||||
&*info
|
||||
});
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data())
|
||||
.value() as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
let opstate = ::std::cell::RefCell::borrow(&*opctx.state);
|
||||
let exception = deno_core::error::to_v8_error(
|
||||
scope,
|
||||
|
@ -40,6 +65,30 @@ impl op_void_with_result {
|
|||
}
|
||||
};
|
||||
}
|
||||
fn fast_function(
|
||||
_: deno_core::v8::Local<deno_core::v8::Object>,
|
||||
fast_api_callback_options: *mut deno_core::v8::fast_api::FastApiCallbackOptions,
|
||||
) -> () {
|
||||
let fast_api_callback_options = unsafe { &mut *fast_api_callback_options };
|
||||
let opctx = unsafe {
|
||||
&*(deno_core::v8::Local::<
|
||||
v8::External,
|
||||
>::cast(unsafe { fast_api_callback_options.data.data })
|
||||
.value() as *const deno_core::_ops::OpCtx)
|
||||
};
|
||||
let result = Self::call();
|
||||
let result = match result {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
unsafe {
|
||||
opctx.unsafely_set_last_error_for_ops_only(err);
|
||||
}
|
||||
fast_api_callback_options.fallback = true;
|
||||
return ::std::default::Default::default();
|
||||
}
|
||||
};
|
||||
result
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn call() -> Result<(), AnyError> {}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#[op2]
|
||||
#[op2(fast)]
|
||||
pub fn op_void_with_result() -> Result<(), AnyError> {}
|
||||
|
|
|
@ -13,7 +13,7 @@ impl op_add {
|
|||
use deno_core::v8::fast_api::Type;
|
||||
use deno_core::v8::fast_api::CType;
|
||||
deno_core::v8::fast_api::FastFunction::new(
|
||||
&[Type::Int32, Type::Uint32],
|
||||
&[Type::V8Value, Type::Int32, Type::Uint32],
|
||||
CType::Uint32,
|
||||
Self::fast_function as *const ::std::ffi::c_void,
|
||||
)
|
||||
|
@ -43,9 +43,8 @@ impl op_add {
|
|||
arg0: i32,
|
||||
arg1: u32,
|
||||
) -> u32 {
|
||||
let arg0 = arg0 as _;
|
||||
let arg1 = arg1 as _;
|
||||
Self::call(arg0, arg1)
|
||||
let result = Self::call(arg0 as _, arg1 as _);
|
||||
result
|
||||
}
|
||||
#[inline(always)]
|
||||
fn call(id: ResourceId, extra: u16) -> u32 {}
|
||||
|
|
Loading…
Reference in a new issue