diff --git a/Cargo.lock b/Cargo.lock index 95a28c5b0e0a56..f4d9ef44094a5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,7 @@ dependencies = [ "deno 0.24.0", "deno_typescript 0.24.0", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dlopen 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -360,6 +361,27 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dlopen" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dlopen_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "downcast-rs" version = "1.1.1" @@ -972,6 +994,14 @@ name = "proc-macro-nested" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "1.0.6" @@ -992,6 +1022,14 @@ dependencies = [ "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "1.0.2" @@ -1439,6 +1477,16 @@ name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.7" @@ -1789,6 +1837,11 @@ name = "unicode-width" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.2.0" @@ -2078,6 +2131,8 @@ dependencies = [ "checksum ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +"checksum dlopen 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +"checksum dlopen_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" "checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" @@ -2147,8 +2202,10 @@ dependencies = [ "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" @@ -2197,6 +2254,7 @@ dependencies = [ "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" "checksum synstructure 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "575be94ccb86e8da37efb894a87e2b660be299b41d8ef347f9d6d79fbe61b1ba" "checksum sys-info 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0079fe39cec2c8215e21b0bc4ccec9031004c160b88358f531b601e96b77f0df" @@ -2229,6 +2287,7 @@ dependencies = [ "checksum unicode-normalization 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf" "checksum unicode-segmentation 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49f5526225fd8b77342d5986ab5f6055552e9c0776193b5b63fd53b46debfad7" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7bacd031aa774d..5c234c58a99acf 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -31,6 +31,7 @@ base64 = "0.11.0" byteorder = "1.3.2" clap = "2.33.0" dirs = "2.0.2" +dlopen = "0.1.8" futures = { version = "0.3", features = [ "compat", "io-compat" ] } http = "0.1.19" hyper = "0.12.35" diff --git a/cli/deno_error.rs b/cli/deno_error.rs index 346149cd16da26..7c4c24e3c90141 100644 --- a/cli/deno_error.rs +++ b/cli/deno_error.rs @@ -6,6 +6,7 @@ pub use crate::msg::ErrorKind; use deno::AnyError; use deno::ErrBox; use deno::ModuleResolutionError; +use dlopen::Error as DlopenError; use http::uri; use hyper; use reqwest; @@ -287,6 +288,19 @@ mod unix { } } +impl GetErrorKind for DlopenError { + fn kind(&self) -> ErrorKind { + use dlopen::Error::*; + match self { + NullCharacter(_) => ErrorKind::Other, + OpeningLibraryError(e) => GetErrorKind::kind(e), + SymbolGettingError(e) => GetErrorKind::kind(e), + NullSymbol => ErrorKind::Other, + AddrNotMatchingDll(e) => GetErrorKind::kind(e), + } + } +} + impl GetErrorKind for dyn AnyError { fn kind(&self) -> ErrorKind { use self::GetErrorKind as Get; @@ -320,6 +334,7 @@ impl GetErrorKind for dyn AnyError { .downcast_ref::() .map(Get::kind) }) + .or_else(|| self.downcast_ref::().map(Get::kind)) .or_else(|| unix_error_kind(self)) .unwrap_or_else(|| { panic!("Can't get ErrorKind for {:?}", self); diff --git a/cli/flags.rs b/cli/flags.rs index 393ca48b6072cb..99e370c817908f 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -48,6 +48,7 @@ pub struct DenoFlags { pub net_whitelist: Vec, pub allow_env: bool, pub allow_run: bool, + pub allow_native: bool, pub allow_hrtime: bool, pub no_prompts: bool, pub no_fetch: bool, @@ -105,6 +106,11 @@ fn add_run_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { .long("allow-run") .help("Allow running subprocesses"), ) + .arg( + Arg::with_name("allow-native") + .long("allow-native") + .help("Allow opening native plugins"), + ) .arg( Arg::with_name("allow-hrtime") .long("allow-hrtime") @@ -837,6 +843,9 @@ fn parse_run_args(mut flags: DenoFlags, matches: &ArgMatches) -> DenoFlags { if matches.is_present("allow-run") { flags.allow_run = true; } + if matches.is_present("allow-native") { + flags.allow_native = true; + } if matches.is_present("allow-hrtime") { flags.allow_hrtime = true; } @@ -847,6 +856,7 @@ fn parse_run_args(mut flags: DenoFlags, matches: &ArgMatches) -> DenoFlags { flags.allow_run = true; flags.allow_read = true; flags.allow_write = true; + flags.allow_native = true; flags.allow_hrtime = true; } if matches.is_present("no-fetch") { @@ -964,6 +974,7 @@ pub fn flags_from_vec( flags.allow_run = true; flags.allow_read = true; flags.allow_write = true; + flags.allow_native = true; flags.allow_hrtime = true; let code: &str = eval_match.value_of("code").unwrap(); argv.extend(vec![code.to_string()]); @@ -1041,6 +1052,7 @@ pub fn flags_from_vec( flags.allow_net = true; flags.allow_env = true; flags.allow_run = true; + flags.allow_native = true; argv.push(INSTALLER_URL.to_string()); if install_match.is_present("dir") { @@ -1131,6 +1143,7 @@ pub fn flags_from_vec( flags.allow_run = true; flags.allow_read = true; flags.allow_write = true; + flags.allow_native = true; flags.allow_hrtime = true; argv.push(XEVAL_URL.to_string()); @@ -1174,6 +1187,7 @@ pub fn flags_from_vec( flags.allow_run = true; flags.allow_read = true; flags.allow_write = true; + flags.allow_native = true; flags.allow_hrtime = true; DenoSubcommand::Repl } diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 9b33d59189ec72..d039d5e132680c 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -13,6 +13,7 @@ pub mod fetch; pub mod files; pub mod fs; pub mod io; +pub mod native_plugins; pub mod net; pub mod os; pub mod permissions; diff --git a/cli/ops/native_plugins.rs b/cli/ops/native_plugins.rs new file mode 100644 index 00000000000000..e62a442e71b8c1 --- /dev/null +++ b/cli/ops/native_plugins.rs @@ -0,0 +1,103 @@ +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; +use std::sync::Mutex; + +pub fn init( + i: &mut Isolate, + s: &ThreadSafeState, + isolate_arc: Arc>, +) { + let isolate_arc_ = isolate_arc.clone(); + i.register_op( + "open_native_plugin", + s.core_op(json_op(s.stateful_op(move |state, args, zero_copy| { + op_open_native_plugin(&isolate_arc_, state, args, zero_copy) + }))), + ); +} + +fn open_plugin>(lib_path: P) -> Result { + debug!("Loading Native Plugin: {:#?}", lib_path.as_ref()); + + Library::open(lib_path).map_err(ErrBox::from) +} + +struct NativePluginResource { + lib: Library, + ops: Vec<(String, OpId)>, +} + +impl Resource for NativePluginResource {} + +struct InitContext { + isolate: Arc>, + plugin_rid: ResourceId, + ops: Vec<(String, OpId)>, +} + +impl PluginInitContext for InitContext { + fn register_op( + &mut self, + name: &str, + op: Box) -> CoreOp + Send + Sync + 'static>, + ) -> OpId { + let mut i = self.isolate.lock().unwrap(); + let opid = i.register_op(&format!("{}_{}", self.plugin_rid, name), op); + self.ops.push((name.to_string(), opid)); + opid + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct OpenNativePluginArgs { + filename: String, +} + +pub fn op_open_native_plugin( + isolate: &Arc>, + state: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + let args: OpenNativePluginArgs = serde_json::from_value(args)?; + let (filename, filename_) = deno_fs::resolve_from_cwd(&args.filename)?; + + state.check_native(&filename_)?; + + let lib = open_plugin(filename)?; + let plugin_resource = NativePluginResource { + lib, + ops: Vec::new(), + }; + let mut table = state.lock_resource_table(); + let rid = table.add("native_plugin", Box::new(plugin_resource)); + let plugin_resource = table.get_mut::(rid).unwrap(); + + let init_fn = *unsafe { + plugin_resource + .lib + .symbol::("native_plugin_init") + }?; + let mut init_context = InitContext { + isolate: isolate.clone(), + plugin_rid: rid, + ops: Vec::new(), + }; + init_fn(&mut init_context); + plugin_resource.ops.append(&mut init_context.ops); + let ops: HashMap = plugin_resource + .ops + .iter() + .map(|record| (record.0.clone(), record.1)) + .collect(); + + Ok(JsonOp::Sync(json!({ "rid": rid, "ops": ops }))) +} diff --git a/cli/permissions.rs b/cli/permissions.rs index fe0a7d473c89ac..f1e61e26ee0c14 100644 --- a/cli/permissions.rs +++ b/cli/permissions.rs @@ -141,6 +141,7 @@ pub struct DenoPermissions { pub net_whitelist: Arc>, pub allow_env: PermissionAccessor, pub allow_run: PermissionAccessor, + pub allow_native: PermissionAccessor, pub allow_hrtime: PermissionAccessor, } @@ -157,6 +158,7 @@ impl DenoPermissions { net_whitelist: Arc::new(flags.net_whitelist.iter().cloned().collect()), allow_env: PermissionAccessor::from(flags.allow_env), allow_run: PermissionAccessor::from(flags.allow_run), + allow_native: PermissionAccessor::from(flags.allow_native), allow_hrtime: PermissionAccessor::from(flags.allow_hrtime), } } @@ -300,6 +302,13 @@ impl DenoPermissions { .request("Deno requests to access to high precision time.") } + pub fn check_native(&self, filename: &str) -> Result<(), ErrBox> { + self.allow_native.get_state().check( + &format!("access to open a native plugin: {}", filename), + "run again with the --allow-run flag", + ) + } + pub fn get_permission_state( &self, name: &str, diff --git a/cli/state.rs b/cli/state.rs index 245919e7f000c7..c421dcc990e798 100644 --- a/cli/state.rs +++ b/cli/state.rs @@ -288,6 +288,11 @@ impl ThreadSafeState { self.permissions.check_run() } + #[inline] + pub fn check_native(&self, filename: &str) -> Result<(), ErrBox> { + self.permissions.check_native(filename) + } + pub fn check_dyn_import( self: &Self, module_specifier: &ModuleSpecifier, diff --git a/core/lib.rs b/core/lib.rs index 31f717769e929b..f1becb5d7e8f9b 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -14,6 +14,7 @@ mod libdeno; mod module_specifier; mod modules; mod ops; +mod plugins; mod resources; mod shared_queue; @@ -27,6 +28,7 @@ pub use crate::libdeno::PinnedBuf; pub use crate::module_specifier::*; pub use crate::modules::*; pub use crate::ops::*; +pub use crate::plugins::*; pub use crate::resources::*; pub fn v8_version() -> &'static str { diff --git a/core/plugins.rs b/core/plugins.rs new file mode 100644 index 00000000000000..0c20bc0e45fad0 --- /dev/null +++ b/core/plugins.rs @@ -0,0 +1,22 @@ +use crate::libdeno::{OpId, PinnedBuf}; +use crate::ops::CoreOp; + +pub type PluginInitFn = fn(context: &mut dyn PluginInitContext); + +pub trait PluginInitContext { + fn register_op( + &mut self, + name: &str, + op: Box) -> CoreOp + Send + Sync + 'static>, + ) -> OpId; +} + +#[macro_export] +macro_rules! init_fn { + ($fn:path) => { + #[no_mangle] + pub fn native_plugin_init(context: &mut dyn PluginInitContext) { + $fn(context) + } + }; +}