summaryrefslogtreecommitdiff
path: root/core/runtime/ops.rs
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-06-24 13:54:10 +0200
committerGitHub <noreply@github.com>2023-06-24 13:54:10 +0200
commit65d9bfb53361bfce6dc594c6a9df92c017dea6cb (patch)
tree63886c7225b52444165be3abd53c4e745ca77512 /core/runtime/ops.rs
parent8d6dbda90ec0593f3f6e10c6696e320bdff7daa9 (diff)
refactor(ops): Adding op2 macro and implementing in a couple of places (#19534)
This is a new op system that will eventually replace `#[op]`. Features - More maintainable, generally less-coupled code - More modern Rust proc-macro libraries - Enforces correct `fast` labelling for fast ops, allowing for visual scanning of fast ops - Explicit marking of `#[string]`, `#[serde]` and `#[smi]` parameters. This first version of op2 supports integer and Option<integer> parameters only, and allows us to start working on converting ops and adding features.
Diffstat (limited to 'core/runtime/ops.rs')
-rw-r--r--core/runtime/ops.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/core/runtime/ops.rs b/core/runtime/ops.rs
index 1a67a1d66..fb71de8cb 100644
--- a/core/runtime/ops.rs
+++ b/core/runtime/ops.rs
@@ -145,3 +145,140 @@ pub fn queue_async_op<'s>(
.spawn(unsafe { crate::task::MaskFutureAsSend::new(pinned) });
None
}
+
+macro_rules! try_number {
+ ($n:ident $type:ident $is:ident) => {
+ if $n.$is() {
+ // SAFETY: v8 handles can be transmuted
+ let n: &v8::Uint32 = unsafe { std::mem::transmute($n) };
+ return n.value() as _;
+ }
+ };
+}
+
+pub fn to_u32(number: &v8::Value) -> u32 {
+ try_number!(number Uint32 is_uint32);
+ try_number!(number Int32 is_int32);
+ try_number!(number Number is_number);
+ if number.is_big_int() {
+ // SAFETY: v8 handles can be transmuted
+ let n: &v8::BigInt = unsafe { std::mem::transmute(number) };
+ return n.u64_value().0 as _;
+ }
+ 0
+}
+
+pub fn to_i32(number: &v8::Value) -> i32 {
+ try_number!(number Uint32 is_uint32);
+ try_number!(number Int32 is_int32);
+ try_number!(number Number is_number);
+ if number.is_big_int() {
+ // SAFETY: v8 handles can be transmuted
+ let n: &v8::BigInt = unsafe { std::mem::transmute(number) };
+ return n.i64_value().0 as _;
+ }
+ 0
+}
+
+#[allow(unused)]
+pub fn to_u64(number: &v8::Value) -> u32 {
+ try_number!(number Uint32 is_uint32);
+ try_number!(number Int32 is_int32);
+ try_number!(number Number is_number);
+ if number.is_big_int() {
+ // SAFETY: v8 handles can be transmuted
+ let n: &v8::BigInt = unsafe { std::mem::transmute(number) };
+ return n.u64_value().0 as _;
+ }
+ 0
+}
+
+#[allow(unused)]
+pub fn to_i64(number: &v8::Value) -> i32 {
+ try_number!(number Uint32 is_uint32);
+ try_number!(number Int32 is_int32);
+ try_number!(number Number is_number);
+ if number.is_big_int() {
+ // SAFETY: v8 handles can be transmuted
+ let n: &v8::BigInt = unsafe { std::mem::transmute(number) };
+ return n.i64_value().0 as _;
+ }
+ 0
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::FastString;
+ use crate::JsRuntime;
+ use crate::RuntimeOptions;
+ use deno_ops::op2;
+
+ crate::extension!(testing, ops = [op_test_add, op_test_add_option]);
+
+ /// Run a test for a single op.
+ fn run_test(
+ op: &'static str,
+ test: &'static str,
+ f: impl FnOnce(Result<&v8::Value, anyhow::Error>, &mut v8::HandleScope),
+ ) {
+ let mut runtime = JsRuntime::new(RuntimeOptions {
+ extensions: vec![testing::init_ops_and_esm()],
+ ..Default::default()
+ });
+ let value: Result<v8::Global<v8::Value>, anyhow::Error> = runtime
+ .execute_script(
+ "",
+ FastString::Owned(
+ format!("const {{ {op} }} = Deno.core.ensureFastOps(); {test}")
+ .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),
+ }
+ }
+
+ #[op2(core, fast)]
+ pub fn op_test_add(a: u32, b: u32) -> u32 {
+ a + b
+ }
+
+ #[tokio::test]
+ 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(())
+ }
+
+ #[op2(core)]
+ pub fn op_test_add_option(a: u32, b: Option<u32>) -> u32 {
+ a + b.unwrap_or(100)
+ }
+
+ #[tokio::test]
+ pub async fn test_op_add_option() -> Result<(), Box<dyn std::error::Error>> {
+ run_test(
+ "op_test_add_option",
+ "op_test_add_option(1, 11)",
+ |value, scope| {
+ assert_eq!(value.unwrap().int32_value(scope), Some(12));
+ },
+ );
+ run_test(
+ "op_test_add_option",
+ "op_test_add_option(1, null)",
+ |value, scope| {
+ assert_eq!(value.unwrap().int32_value(scope), Some(101));
+ },
+ );
+ Ok(())
+ }
+}