diff --git a/core/benches/op_baseline.rs b/core/benches/op_baseline.rs index 5287a7a8b1..132d92f000 100644 --- a/core/benches/op_baseline.rs +++ b/core/benches/op_baseline.rs @@ -3,10 +3,10 @@ use bencher::{benchmark_group, benchmark_main, Bencher}; use deno_core::error::AnyError; use deno_core::op_async; use deno_core::op_sync; +use deno_core::serialize_op_result; use deno_core::v8; use deno_core::JsRuntime; use deno_core::Op; -use deno_core::OpResponse; use deno_core::OpState; use deno_core::ZeroCopyBuf; @@ -17,8 +17,9 @@ fn create_js_runtime() -> JsRuntime { let mut runtime = JsRuntime::new(Default::default()); runtime.register_op("pi_json", op_sync(|_, _: (), _| Ok(314159))); runtime.register_op("pi_async", op_async(op_pi_async)); - runtime - .register_op("nop", |_, _, _| Op::Sync(OpResponse::Value(Box::new(9)))); + runtime.register_op("nop", |state, _, _| { + Op::Sync(serialize_op_result(Ok(9), state)) + }); // Init ops runtime diff --git a/core/lib.rs b/core/lib.rs index b49de3b7b1..bc9d50e3f7 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -5,6 +5,7 @@ mod bindings; pub mod error; mod flags; mod gotham_state; +mod minvalue; mod module_specifier; mod modules; mod normalize_path; diff --git a/core/minvalue.rs b/core/minvalue.rs new file mode 100644 index 0000000000..1c9059c125 --- /dev/null +++ b/core/minvalue.rs @@ -0,0 +1,95 @@ +use rusty_v8 as v8; +use std::any::TypeId; + +/// SerializablePkg exists to provide a fast path for op returns, +/// allowing them to avoid boxing primtives (ints/floats/bool/unit/...) +pub enum SerializablePkg { + MinValue(MinValue), + Serializable(Box), +} + +impl SerializablePkg { + pub fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, serde_v8::Error> { + match &*self { + Self::MinValue(x) => serde_v8::to_v8(scope, x), + Self::Serializable(x) => x.to_v8(scope), + } + } +} + +/// MinValue serves as a lightweight serializable wrapper around primitives +/// so that we can use them for async values +#[derive(Clone, Copy)] +pub enum MinValue { + Unit(()), + Bool(bool), + Int8(i8), + Int16(i16), + Int32(i32), + Int64(i64), + UInt8(u8), + UInt16(u16), + UInt32(u32), + UInt64(u64), + Float32(f32), + Float64(f64), +} + +impl serde::Serialize for MinValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match *self { + Self::Unit(_) => serializer.serialize_unit(), + Self::Bool(x) => serializer.serialize_bool(x), + Self::Int8(x) => serializer.serialize_i8(x), + Self::Int16(x) => serializer.serialize_i16(x), + Self::Int32(x) => serializer.serialize_i32(x), + Self::Int64(x) => serializer.serialize_i64(x), + Self::UInt8(x) => serializer.serialize_u8(x), + Self::UInt16(x) => serializer.serialize_u16(x), + Self::UInt32(x) => serializer.serialize_u32(x), + Self::UInt64(x) => serializer.serialize_u64(x), + Self::Float32(x) => serializer.serialize_f32(x), + Self::Float64(x) => serializer.serialize_f64(x), + } + } +} + +impl From for SerializablePkg { + fn from(x: T) -> Self { + let tid = TypeId::of::(); + + if tid == TypeId::of::<()>() { + Self::MinValue(MinValue::Unit(())) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Bool(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Int8(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Int16(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Int32(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Int64(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::UInt8(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::UInt16(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::UInt32(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::UInt64(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Float32(unsafe { std::mem::transmute_copy(&x) })) + } else if tid == TypeId::of::() { + Self::MinValue(MinValue::Float64(unsafe { std::mem::transmute_copy(&x) })) + } else { + Self::Serializable(Box::new(x)) + } + } +} diff --git a/core/ops.rs b/core/ops.rs index 3cc24c8ccb..0c9e192925 100644 --- a/core/ops.rs +++ b/core/ops.rs @@ -4,6 +4,7 @@ use crate::error::bad_resource_id; use crate::error::type_error; use crate::error::AnyError; use crate::gotham_state::GothamState; +use crate::minvalue::SerializablePkg; use crate::resources::ResourceId; use crate::resources::ResourceTable; use crate::runtime::GetErrorClassFn; @@ -61,7 +62,7 @@ impl<'a, 'b, 'c> OpPayload<'a, 'b, 'c> { } pub enum OpResponse { - Value(Box), + Value(OpResult), Buffer(Box<[u8]>), } @@ -74,13 +75,23 @@ pub enum Op { NotFound, } -#[derive(Serialize)] -#[serde(untagged)] -pub enum OpResult { - Ok(R), +pub enum OpResult { + Ok(SerializablePkg), Err(OpError), } +impl OpResult { + pub fn to_v8<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> Result, serde_v8::Error> { + match self { + Self::Ok(x) => x.to_v8(scope), + Self::Err(err) => serde_v8::to_v8(scope, err), + } + } +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct OpError { @@ -93,13 +104,13 @@ pub fn serialize_op_result( result: Result, state: Rc>, ) -> OpResponse { - OpResponse::Value(Box::new(match result { - Ok(v) => OpResult::Ok(v), + OpResponse::Value(match result { + Ok(v) => OpResult::Ok(v.into()), Err(err) => OpResult::Err(OpError { class_name: (state.borrow().get_error_class_fn)(&err), message: err.to_string(), }), - })) + }) } /// Maintains the resources and ops inside a JS runtime. diff --git a/core/runtime.rs b/core/runtime.rs index 98bcc018a2..e974bdbb28 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -1501,18 +1501,19 @@ pub mod tests { } fn dispatch( - op_state: Rc>, + rc_op_state: Rc>, payload: OpPayload, buf: Option, ) -> Op { - let op_state_ = op_state.borrow(); + let rc_op_state2 = rc_op_state.clone(); + let op_state_ = rc_op_state2.borrow(); let test_state = op_state_.borrow::(); test_state.dispatch_count.fetch_add(1, Ordering::Relaxed); match test_state.mode { Mode::Async => { let control: u8 = payload.deserialize().unwrap(); assert_eq!(control, 42); - let resp = (0, OpResponse::Value(Box::new(43))); + let resp = (0, serialize_op_result(Ok(43), rc_op_state)); Op::Async(Box::pin(futures::future::ready(resp))) } Mode::AsyncUnref => { @@ -1521,7 +1522,7 @@ pub mod tests { let fut = async { // This future never finish. futures::future::pending::<()>().await; - (0, OpResponse::Value(Box::new(43))) + (0, serialize_op_result(Ok(43), rc_op_state)) }; Op::AsyncUnref(Box::pin(fut)) } @@ -1531,7 +1532,7 @@ pub mod tests { assert_eq!(buf.len(), 1); } - let resp = OpResponse::Value(Box::new(43)); + let resp = serialize_op_result(Ok(43), rc_op_state); Op::Async(Box::pin(futures::future::ready((0, resp)))) } } @@ -1972,11 +1973,11 @@ pub mod tests { let dispatch_count = Arc::new(AtomicUsize::new(0)); let dispatch_count_ = dispatch_count.clone(); - let dispatcher = move |_state, payload: OpPayload, _buf| -> Op { + let dispatcher = move |state, payload: OpPayload, _buf| -> Op { dispatch_count_.fetch_add(1, Ordering::Relaxed); let control: u8 = payload.deserialize().unwrap(); assert_eq!(control, 42); - let resp = (0, OpResponse::Value(Box::new(43))); + let resp = (0, serialize_op_result(Ok(43), state)); Op::Async(Box::pin(futures::future::ready(resp))) };