0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

docs(core): document several concepts around JsRuntime and ops (#7897)

This commit is contained in:
Jan Haller 2020-11-06 02:26:14 +01:00 committed by GitHub
parent 5f7c80986f
commit 6162807a7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 1 deletions

View file

@ -31,6 +31,7 @@ pub enum Op {
NotFound,
}
/// Maintains the resources and ops inside a JS runtime.
pub struct OpState {
pub resource_table: crate::ResourceTable,
pub op_table: OpTable,
@ -151,6 +152,29 @@ fn op_table() {
)
}
/// Creates an op that passes data synchronously using JSON.
///
/// The provided function `op_fn` has the following parameters:
/// * `&mut OpState`: the op state, can be used to read/write resources in the runtime from an op.
/// * `Value`: the JSON value that is passed to the Rust function.
/// * `&mut [ZeroCopyBuf]`: raw bytes passed along, usually not needed if the JSON value is used.
///
/// `op_fn` returns a JSON value, which is directly returned to JavaScript.
///
/// When registering an op like this...
/// ```ignore
/// let mut runtime = JsRuntime::new(...);
/// runtime.register_op("hello", deno_core::json_op_sync(Self::hello_op));
/// ```
///
/// ...it can be invoked from JS using the provided name, for example:
/// ```js
/// Deno.core.ops();
/// let result = Deno.core.jsonOpSync("function_name", args);
/// ```
///
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
/// A more complete example is available in the examples directory.
pub fn json_op_sync<F>(op_fn: F) -> Box<OpFn>
where
F: Fn(&mut OpState, Value, &mut [ZeroCopyBuf]) -> Result<Value, AnyError>
@ -166,6 +190,30 @@ where
})
}
/// Creates an op that passes data asynchronously using JSON.
///
/// The provided function `op_fn` has the following parameters:
/// * `Rc<RefCell<OpState>`: the op state, can be used to read/write resources in the runtime from an op.
/// * `Value`: the JSON value that is passed to the Rust function.
/// * `BufVec`: raw bytes passed along, usually not needed if the JSON value is used.
///
/// `op_fn` returns a future, whose output is a JSON value. This value will be asynchronously
/// returned to JavaScript.
///
/// When registering an op like this...
/// ```ignore
/// let mut runtime = JsRuntime::new(...);
/// runtime.register_op("hello", deno_core::json_op_async(Self::hello_op));
/// ```
///
/// ...it can be invoked from JS using the provided name, for example:
/// ```js
/// Deno.core.ops();
/// let future = Deno.core.jsonOpAsync("function_name", args);
/// ```
///
/// The `Deno.core.ops()` statement is needed once before any op calls, for initialization.
/// A more complete example is available in the examples directory.
pub fn json_op_async<F, R>(op_fn: F) -> Box<OpFn>
where
F: Fn(Rc<RefCell<OpState>>, Value, BufVec) -> R + 'static,

View file

@ -10,13 +10,21 @@ use std::any::Any;
use std::collections::HashMap;
/// ResourceId is Deno's version of a file descriptor. ResourceId is also referred
/// to as rid in the code base.
/// to as `rid` in the code base.
pub type ResourceId = u32;
/// These store Deno's file descriptors. These are not necessarily the operating
/// system ones.
type ResourceMap = HashMap<ResourceId, (String, Box<dyn Any>)>;
/// Map-like data structure storing Deno's resources (equivalent to file descriptors).
///
/// Provides basic methods for element access. A resource can be of any type.
/// Different types of resources can be stored in the same map, and provided
/// with a name for description.
///
/// Each resource is identified through a _resource ID (rid)_, which acts as
/// the key in the map.
#[derive(Default)]
pub struct ResourceTable {
map: ResourceMap,
@ -24,15 +32,22 @@ pub struct ResourceTable {
}
impl ResourceTable {
/// Checks if the given resource ID is contained.
pub fn has(&self, rid: ResourceId) -> bool {
self.map.contains_key(&rid)
}
/// Returns a shared reference to a resource.
///
/// Returns `None`, if `rid` is not stored or has a type different from `T`.
pub fn get<T: Any>(&self, rid: ResourceId) -> Option<&T> {
let (_, resource) = self.map.get(&rid)?;
resource.downcast_ref::<T>()
}
/// Returns a mutable reference to a resource.
///
/// Returns `None`, if `rid` is not stored or has a type different from `T`.
pub fn get_mut<T: Any>(&mut self, rid: ResourceId) -> Option<&mut T> {
let (_, resource) = self.map.get_mut(&rid)?;
resource.downcast_mut::<T>()
@ -45,6 +60,12 @@ impl ResourceTable {
next_rid as ResourceId
}
/// Inserts a resource, taking ownership of it.
///
/// The resource type is erased at runtime and must be statically known
/// when retrieving it through `get()`.
///
/// Returns a unique resource ID, which acts as a key for this resource.
pub fn add(&mut self, name: &str, resource: Box<dyn Any>) -> ResourceId {
let rid = self.next_rid();
let r = self.map.insert(rid, (name.to_string(), resource));
@ -52,6 +73,10 @@ impl ResourceTable {
rid
}
/// Returns a map of resource IDs to names.
///
/// The name is the one specified during `add()`. To access resources themselves,
/// use the `get()` or `get_mut()` functions.
pub fn entries(&self) -> HashMap<ResourceId, String> {
self
.map
@ -66,6 +91,13 @@ impl ResourceTable {
self.map.remove(&rid).map(|(_name, _resource)| ())
}
/// Removes the resource identified by `rid` and returns it.
///
/// When the provided `rid` is stored, the associated resource will be removed.
/// Otherwise, nothing happens and `None` is returned.
///
/// If the type `T` matches the resource's type, the resource will be returned.
/// If the type mismatches, `None` is returned, but the resource is still removed.
pub fn remove<T: Any>(&mut self, rid: ResourceId) -> Option<Box<T>> {
if let Some((_name, resource)) = self.map.remove(&rid) {
let res = match resource.downcast::<T>() {

View file

@ -191,6 +191,7 @@ pub struct RuntimeOptions {
}
impl JsRuntime {
/// Only constructor, configuration is done through `options`.
pub fn new(mut options: RuntimeOptions) -> Self {
static DENO_INIT: Once = Once::new();
DENO_INIT.call_once(|| {
@ -320,6 +321,8 @@ impl JsRuntime {
}
}
/// Returns the runtime's op state, which can be used to maintain ops
/// and access resources between op calls.
pub fn op_state(&mut self) -> Rc<RefCell<OpState>> {
let state_rc = Self::state(self.v8_isolate());
let state = state_rc.borrow();
@ -328,6 +331,9 @@ impl JsRuntime {
/// Executes traditional JavaScript code (traditional = not ES modules)
///
/// The execution takes place on the current global context, so it is possible
/// to maintain local JS state and invoke this method multiple times.
///
/// `AnyError` can be downcast to a type that exposes additional information
/// about the V8 exception. By default this type is `JsError`, however it may
/// be a different type if `RuntimeOptions::js_error_create_fn` has been set.
@ -391,6 +397,15 @@ impl JsRuntime {
snapshot
}
/// Registers an op that can be called from JavaScript.
///
/// The _op_ mechanism allows to expose Rust functions to the JS runtime,
/// which can be called using the provided `name`.
///
/// This function provides byte-level bindings. To pass data via JSON, the
/// following functions can be passed as an argument for `op_fn`:
/// * [json_op_sync()](fn.json_op_sync.html)
/// * [json_op_async()](fn.json_op_async.html)
pub fn register_op<F>(&mut self, name: &str, op_fn: F) -> OpId
where
F: Fn(Rc<RefCell<OpState>>, BufVec) -> Op + 'static,