Attribute Macro deno_core::op2

source ·
#[op2]
Expand description

A macro designed to provide an extremely fast V8->Rust interface layer.

§op2

#[op2] is the in-progress replacement for #[op].

§Strings

Strings in Rust are always UTF-8. Strings in v8, however, are either two-byte UTF-16 or one-byte Latin-1. One-byte Latin-1 strings are not byte-compatible with UTF-8, as characters with the index 128-255 require two bytes to encode in UTF-8.

Because of this, Strings in ops always require a copy (at least) to ensure that we are not incorrectly passing Latin-1 data to methods that expect a UTF-8 string. At this time there is no way to avoid this copy, though the op code does attempt to avoid any allocations where possible by making use of a stack buffer.

§Fallible ops

An op function may be declared to return Result to indicate that the op is fallible. The error type must implement Into<anyhow::Error>. When the function returns Err, an exception is thrown.

§async calls

Asynchronous calls are supported in two forms:

async fn op_xyz(/* ... */) -> X {}

and

fn op_xyz(/* ... */) -> impl Future<Output = X> {}

These are desugared to a function that adds a hidden promise_id argument, and returns Option<X> instead. Deno will eagerly poll the op, and if it is immediately ready, the function will return Some(X). If the op is not ready, the function will return None and the future will be handled by Deno’s pending op system.

fn op_xyz(promise_id: i32 /* ... */) -> Option<X> {}

§Eager async calls: async

By default, async functions are eagerly polled, which reduces the latency of the call dramatically if the async function is ready to return a value immediately.

§async(lazy)

async calls may be marked as lazy, which allows the runtime to defer polling the op until a later time. The submission of an async(lazy) op might be faster, but the latency will be higher for ops that would have been ready on the first poll.

NOTE: You may need to use this to get the maximum performance out of a set of async tasks, but it should only be used alongside careful benchmarking. In some cases it will allow for higher throughput at the expense of latency.

Lazy async calls may be fastcalls, though the resolution will still happen on a slow path.

§async(deferred)

async calls may also be marked as deferred, which will allow the runtime to poll the op immediately, but any results that are ready are deferred until a later run of the event loop.

NOTE: This is almost certainly not what you want to use and should only be used if you really know what you are doing.

Lazy async(deferred) calls may be fastcalls, though the resolution will still happen on a slow path.

§fastcalls

op2 requires fastcall-compatible ops to be annotated with fast. If you wish to avoid fastcalls for some reason (this is unlikely), you can specify nofast instead.

You may also choose an alternate op function to use as the fastcall equivalent to a slow function. In this case, you can specify fast(op_XYZ). The other op must be decorated with #[op2(fast)], and does not need to be registered. When v8 optimized the slow function to a fastcall, it will switch the implementation over if the parameters are compatible. This is useful for a function that takes any buffer type in the slow path and wishes to use the very fast typed u8 buffer for the fast path.

§Parameters

RustFastcallv8
bool
Bool
i8
Uint32, Int32, Number, BigInt
u8
Uint32, Int32, Number, BigInt
i16
Uint32, Int32, Number, BigInt
u16
Uint32, Int32, Number, BigInt
i32
Uint32, Int32, Number, BigInt
u32
Uint32, Int32, Number, BigInt
#[smi] ResourceId
Uint32, Int32, Number, BigInt SMI is internally represented as a signed integer, but unsigned `#[smi]` types will be bit-converted to unsigned values for the Rust call. JavaScript code will continue to see signed integers.
#[bigint] i64
Uint32, Int32, Number, BigInt
#[bigint] u64
Uint32, Int32, Number, BigInt
#[bigint] isize
Uint32, Int32, Number, BigInt
#[bigint] usize
Uint32, Int32, Number, BigInt
f32
Uint32, Int32, Number, BigInt
f64
Uint32, Int32, Number, BigInt
#[string] String
String Fastcall available only if string is Latin-1. Will always create an allocated, UTF-8 copy of the String data.
#[string] &str
String Fastcall available only if string is Latin-1. Will create an owned `String` copy of the String data if it doesn't fit on the stack. Will never allocate in a fastcall, but will copy Latin-1 -> UTF-8.
#[string] Cow<str>
String Fastcall available only if string is Latin-1. Will create a `Cow::Owned` copy of the String data if it doesn't fit on the stack. Will always be `Cow::Borrowed` in a fastcall, but will copy Latin-1 -> UTF-8.
#[string(onebyte)] Cow<[u8]>
String Fastest `String`-type method. If the string is not Latin-1, will throw a TypeError.
&v8::Value
any
&v8::String
String
&v8::Object
Object
&v8::Function
Function
&v8::...
...
v8::Local<v8::Value>
any
v8::Local<v8::String>
String
v8::Local<v8::Object>
Object
v8::Local<v8::Function>
Function
v8::Local<v8::...>
...
#[global] v8::Global<v8::Value>
any ⚠️ Slower than `v8::Local`.
#[global] v8::Global<v8::String>
String ⚠️ Slower than `v8::Local`.
#[global] v8::Global<v8::Object>
Object ⚠️ Slower than `v8::Local`.
#[global] v8::Global<v8::Function>
Function ⚠️ Slower than `v8::Local`.
#[global] v8::Global<v8::...>
... ⚠️ Slower than `v8::Local`.
#[serde] SerdeType
any ⚠️ May be slow.
#[serde] (Tuple, Tuple)
any ⚠️ May be slow.
#[arraybuffer] &mut [u8]
ArrayBuffer (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.
#[arraybuffer] &[u8]
ArrayBuffer (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.
#[arraybuffer] *mut u8
ArrayBuffer (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.
#[arraybuffer] *const u8
ArrayBuffer (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly. Because of how V8 treats empty arrays in fastcalls, they will always be passed as null.
#[arraybuffer(copy)] Vec<u8>
ArrayBuffer (resizable=true,false) Safe, but forces a copy.
#[arraybuffer(copy)] Box<[u8]>
ArrayBuffer (resizable=true,false) Safe, but forces a copy.
#[arraybuffer(copy)] bytes::Bytes
ArrayBuffer (resizable=true,false) Safe, but forces a copy.
#[buffer(copy)] Vec<u8>
UInt8Array (resizable=true,false) Safe, but forces a copy.
#[buffer(copy)] Box<[u8]>
UInt8Array (resizable=true,false) Safe, but forces a copy.
#[buffer(copy)] bytes::Bytes
UInt8Array (resizable=true,false) Safe, but forces a copy.
#[buffer] &mut [u32]
UInt32Array (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.
#[buffer] &[u32]
UInt32Array (resizable=true,false) ⚠️ JS may modify the contents of the slice if V8 is called re-entrantly.
#[buffer(copy)] Vec<u32>
UInt32Array (resizable=true,false) Safe, but forces a copy.
#[buffer(copy)] Box<[u32]>
UInt32Array (resizable=true,false) Safe, but forces a copy.
#[buffer(detach)] JsBuffer
ArrayBufferView (resizable=true,false) Safe.
*const std::ffi::c_void
External
*mut std::ffi::c_void
External
#[memory(caller)] &[u8]
WASM When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.
#[memory(caller)] &mut [u8]
WASM When called from WASM code, contains a pointer to the WASM module's memory. Throws an exception if called from another context.
#[memory(caller)] Option<&[u8]>
WASM When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.
#[memory(caller)] Option<&mut [u8]>
WASM When called from WASM code, contains a pointer to the WASM module's memory, otherwise `None`.
&OpState
&mut OpState
Rc<RefCell<OpState>>
#[state] &StateObject
Extracts an object from `OpState`.
#[state] &mut StateObject
Extracts an object from `OpState`.
&JsRuntimeState
Only usable in `deno_core`.
*mut v8::Isolate
⚠️ Extremely dangerous, may crash if you don't use `nofast` depending on what you do.

§Return Values

RustFastcallAsyncv8
bool
i8
u8
i16
u16
i32
u32
#[smi] ResourceId
SMI is internally represented as a signed integer, but unsigned `#[smi]` types will be bit-converted to unsigned values for the Rust call. JavaScript code will continue to see signed integers.
#[bigint] i64
#[bigint] u64
#[bigint] isize
#[bigint] usize
#[number] i64
Result must fit within `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`
#[number] u64
Result must fit within `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`
#[number] isize
Result must fit within `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`
#[number] usize
Result must fit within `Number.MIN_SAFE_INTEGER` and `Number.MAX_SAFE_INTEGER`
f32
f64
#[string] String
#[string] &str
#[string] Cow<str>
#[string(onebyte)] Cow<[u8]>
#[arraybuffer] V8Slice<u8>
#[arraybuffer] Vec<u8>
#[arraybuffer] Box<[u8]>
#[arraybuffer] bytes::BytesMut
#[buffer] V8Slice<u8>
#[buffer] Vec<u8>
#[buffer] Box<[u8]>
#[buffer] bytes::BytesMut
#[buffer] V8Slice<u32>
*const std::ffi::c_void
*mut std::ffi::c_void
v8::Local<v8::Value>
v8::Local<v8::String>
v8::Local<v8::Object>
v8::Local<v8::Function>
v8::Local<v8::...>
#[global] v8::Global<v8::Value>
#[global] v8::Global<v8::String>
#[global] v8::Global<v8::Object>
#[global] v8::Global<v8::Function>
#[global] v8::Global<v8::...>
#[serde] SerdeType
#[serde] (Tuple, Tuple)