From 7c3b9b4f4f2f4ec8fdeb0e77bb853fd22ffaa476 Mon Sep 17 00:00:00 2001 From: Andy Finch Date: Thu, 5 Dec 2019 15:30:20 -0500 Subject: feat: first pass at native plugins (#3372) --- cli/ops/plugins.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 cli/ops/plugins.rs (limited to 'cli/ops/plugins.rs') diff --git a/cli/ops/plugins.rs b/cli/ops/plugins.rs new file mode 100644 index 000000000..2673e3d6a --- /dev/null +++ b/cli/ops/plugins.rs @@ -0,0 +1,96 @@ +use super::dispatch_json::{Deserialize, JsonOp, Value}; +use crate::fs as deno_fs; +use crate::ops::json_op; +use crate::state::ThreadSafeState; +use deno::*; +use dlopen::symbor::Library; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::sync::Arc; + +pub fn init(i: &mut Isolate, s: &ThreadSafeState, r: Arc) { + let r_ = r.clone(); + i.register_op( + "open_plugin", + s.core_op(json_op(s.stateful_op(move |state, args, zero_copy| { + op_open_plugin(&r_, state, args, zero_copy) + }))), + ); +} + +fn open_plugin>(lib_path: P) -> Result { + debug!("Loading Plugin: {:#?}", lib_path.as_ref()); + + Library::open(lib_path).map_err(ErrBox::from) +} + +struct PluginResource { + lib: Library, + ops: HashMap, +} + +impl Resource for PluginResource {} + +struct InitContext { + ops: HashMap>, +} + +impl PluginInitContext for InitContext { + fn register_op(&mut self, name: &str, op: Box) { + let existing = self.ops.insert(name.to_string(), op); + assert!( + existing.is_none(), + format!("Op already registered: {}", name) + ); + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct OpenPluginArgs { + filename: String, +} + +pub fn op_open_plugin( + registry: &Arc, + state: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + let args: OpenPluginArgs = serde_json::from_value(args)?; + let (filename, filename_) = deno_fs::resolve_from_cwd(&args.filename)?; + + state.check_plugin(&filename_)?; + + let lib = open_plugin(filename)?; + let plugin_resource = PluginResource { + lib, + ops: HashMap::new(), + }; + let mut table = state.lock_resource_table(); + let rid = table.add("plugin", Box::new(plugin_resource)); + let plugin_resource = table.get_mut::(rid).unwrap(); + + let init_fn = *unsafe { + plugin_resource + .lib + .symbol::("deno_plugin_init") + }?; + let mut init_context = InitContext { + ops: HashMap::new(), + }; + init_fn(&mut init_context); + for op in init_context.ops { + // Register each plugin op in the `OpRegistry` with the name + // formated like this `plugin_{plugin_rid}_{name}`. + // The inclusion of prefix and rid is designed to avoid any + // op name collision beyond the bound of a single loaded + // plugin instance. + let op_id = registry.register(&format!("plugin_{}_{}", rid, op.0), op.1); + plugin_resource.ops.insert(op.0, op_id); + } + + Ok(JsonOp::Sync( + json!({ "rid": rid, "ops": plugin_resource.ops }), + )) +} -- cgit v1.2.3