mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
feat: first pass at native plugins (#3372)
This commit is contained in:
parent
214b3eb29a
commit
7c3b9b4f4f
26 changed files with 574 additions and 7 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -293,6 +293,7 @@ dependencies = [
|
|||
"deno 0.25.0",
|
||||
"deno_typescript 0.25.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)",
|
||||
|
@ -357,6 +358,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"
|
||||
|
@ -969,6 +991,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"
|
||||
|
@ -989,6 +1019,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"
|
||||
|
@ -1436,6 +1474,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"
|
||||
|
@ -1487,6 +1535,15 @@ dependencies = [
|
|||
"wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_plugin"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"deno 0.25.0",
|
||||
"deno_cli 0.25.0",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
|
@ -1786,6 +1843,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"
|
||||
|
@ -2075,6 +2137,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"
|
||||
|
@ -2144,8 +2208,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"
|
||||
|
@ -2194,6 +2260,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"
|
||||
|
@ -2226,6 +2293,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"
|
||||
|
|
|
@ -4,4 +4,5 @@ members = [
|
|||
"core",
|
||||
"tools/hyper_hello",
|
||||
"deno_typescript",
|
||||
"test_plugin"
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
@ -292,6 +293,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;
|
||||
|
@ -325,6 +339,7 @@ impl GetErrorKind for dyn AnyError {
|
|||
.downcast_ref::<serde_json::error::Error>()
|
||||
.map(Get::kind)
|
||||
})
|
||||
.or_else(|| self.downcast_ref::<DlopenError>().map(Get::kind))
|
||||
.or_else(|| unix_error_kind(self))
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Can't get ErrorKind for {:?}", self);
|
||||
|
|
17
cli/flags.rs
17
cli/flags.rs
|
@ -82,6 +82,7 @@ pub struct DenoFlags {
|
|||
pub net_whitelist: Vec<String>,
|
||||
pub allow_env: bool,
|
||||
pub allow_run: bool,
|
||||
pub allow_plugin: bool,
|
||||
pub allow_hrtime: bool,
|
||||
pub no_prompts: bool,
|
||||
pub no_remote: bool,
|
||||
|
@ -346,6 +347,7 @@ fn xeval_parse(flags: &mut DenoFlags, matches: &clap::ArgMatches) {
|
|||
flags.allow_run = true;
|
||||
flags.allow_read = true;
|
||||
flags.allow_write = true;
|
||||
flags.allow_plugin = true;
|
||||
flags.allow_hrtime = true;
|
||||
flags.argv.push(XEVAL_URL.to_string());
|
||||
|
||||
|
@ -373,6 +375,7 @@ fn repl_parse(flags: &mut DenoFlags, matches: &clap::ArgMatches) {
|
|||
flags.allow_run = true;
|
||||
flags.allow_read = true;
|
||||
flags.allow_write = true;
|
||||
flags.allow_plugin = true;
|
||||
flags.allow_hrtime = true;
|
||||
}
|
||||
|
||||
|
@ -383,6 +386,7 @@ fn eval_parse(flags: &mut DenoFlags, matches: &clap::ArgMatches) {
|
|||
flags.allow_run = true;
|
||||
flags.allow_read = true;
|
||||
flags.allow_write = true;
|
||||
flags.allow_plugin = true;
|
||||
flags.allow_hrtime = true;
|
||||
let code: &str = matches.value_of("code").unwrap();
|
||||
flags.argv.extend(vec![code.to_string()]);
|
||||
|
@ -465,6 +469,9 @@ fn run_test_args_parse(flags: &mut DenoFlags, matches: &clap::ArgMatches) {
|
|||
if matches.is_present("allow-run") {
|
||||
flags.allow_run = true;
|
||||
}
|
||||
if matches.is_present("allow-plugin") {
|
||||
flags.allow_plugin = true;
|
||||
}
|
||||
if matches.is_present("allow-hrtime") {
|
||||
flags.allow_hrtime = true;
|
||||
}
|
||||
|
@ -475,6 +482,7 @@ fn run_test_args_parse(flags: &mut DenoFlags, matches: &clap::ArgMatches) {
|
|||
flags.allow_run = true;
|
||||
flags.allow_read = true;
|
||||
flags.allow_write = true;
|
||||
flags.allow_plugin = true;
|
||||
flags.allow_hrtime = true;
|
||||
}
|
||||
if matches.is_present("cached-only") {
|
||||
|
@ -942,6 +950,11 @@ fn run_test_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
|||
.long("allow-run")
|
||||
.help("Allow running subprocesses"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow-plugin")
|
||||
.long("allow-plugin")
|
||||
.help("Allow loading plugins"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("allow-hrtime")
|
||||
.long("allow-hrtime")
|
||||
|
@ -1408,6 +1421,7 @@ mod tests {
|
|||
allow_run: true,
|
||||
allow_read: true,
|
||||
allow_write: true,
|
||||
allow_plugin: true,
|
||||
allow_hrtime: true,
|
||||
..DenoFlags::default()
|
||||
}
|
||||
|
@ -1581,6 +1595,7 @@ mod tests {
|
|||
allow_run: true,
|
||||
allow_read: true,
|
||||
allow_write: true,
|
||||
allow_plugin: true,
|
||||
allow_hrtime: true,
|
||||
..DenoFlags::default()
|
||||
}
|
||||
|
@ -1600,6 +1615,7 @@ mod tests {
|
|||
allow_run: true,
|
||||
allow_read: true,
|
||||
allow_write: true,
|
||||
allow_plugin: true,
|
||||
allow_hrtime: true,
|
||||
..DenoFlags::default()
|
||||
}
|
||||
|
@ -1635,6 +1651,7 @@ mod tests {
|
|||
allow_run: true,
|
||||
allow_read: true,
|
||||
allow_write: true,
|
||||
allow_plugin: true,
|
||||
allow_hrtime: true,
|
||||
..DenoFlags::default()
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ export {
|
|||
} from "./permissions.ts";
|
||||
export { truncateSync, truncate } from "./truncate.ts";
|
||||
export { FileInfo } from "./file_info.ts";
|
||||
export { openPlugin } from "./plugins.ts";
|
||||
export { connect, dial, listen, Listener, Conn } from "./net.ts";
|
||||
export { dialTLS, listenTLS } from "./tls.ts";
|
||||
export { metrics, Metrics } from "./metrics.ts";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import * as minimal from "./dispatch_minimal.ts";
|
||||
import * as json from "./dispatch_json.ts";
|
||||
import { AsyncHandler } from "./plugins.ts";
|
||||
|
||||
// These consts are shared with Rust. Update with care.
|
||||
export let OP_READ: number;
|
||||
|
@ -67,6 +68,16 @@ export let OP_CWD: number;
|
|||
export let OP_FETCH_ASSET: number;
|
||||
export let OP_DIAL_TLS: number;
|
||||
export let OP_HOSTNAME: number;
|
||||
export let OP_OPEN_PLUGIN: number;
|
||||
|
||||
const PLUGIN_ASYNC_HANDLER_MAP: Map<number, AsyncHandler> = new Map();
|
||||
|
||||
export function setPluginAsyncHandler(
|
||||
opId: number,
|
||||
handler: AsyncHandler
|
||||
): void {
|
||||
PLUGIN_ASYNC_HANDLER_MAP.set(opId, handler);
|
||||
}
|
||||
|
||||
export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
|
||||
switch (opId) {
|
||||
|
@ -111,6 +122,11 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
|
|||
json.asyncMsgFromRust(opId, ui8);
|
||||
break;
|
||||
default:
|
||||
throw Error("bad async opId");
|
||||
const handler = PLUGIN_ASYNC_HANDLER_MAP.get(opId);
|
||||
if (handler) {
|
||||
handler(ui8);
|
||||
} else {
|
||||
throw Error("bad async opId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
35
cli/js/lib.deno_runtime.d.ts
vendored
35
cli/js/lib.deno_runtime.d.ts
vendored
|
@ -907,6 +907,7 @@ declare namespace Deno {
|
|||
| "write"
|
||||
| "net"
|
||||
| "env"
|
||||
| "plugin"
|
||||
| "hrtime";
|
||||
/** https://w3c.github.io/permissions/#status-of-a-permission */
|
||||
export type PermissionState = "granted" | "denied" | "prompt";
|
||||
|
@ -924,6 +925,9 @@ declare namespace Deno {
|
|||
interface EnvPermissionDescriptor {
|
||||
name: "env";
|
||||
}
|
||||
interface PluginPermissionDescriptor {
|
||||
name: "plugin";
|
||||
}
|
||||
interface HrtimePermissionDescriptor {
|
||||
name: "hrtime";
|
||||
}
|
||||
|
@ -933,6 +937,7 @@ declare namespace Deno {
|
|||
| ReadWritePermissionDescriptor
|
||||
| NetPermissionDescriptor
|
||||
| EnvPermissionDescriptor
|
||||
| PluginPermissionDescriptor
|
||||
| HrtimePermissionDescriptor;
|
||||
|
||||
export class Permissions {
|
||||
|
@ -982,6 +987,36 @@ declare namespace Deno {
|
|||
*/
|
||||
export function truncate(name: string, len?: number): Promise<void>;
|
||||
|
||||
// @url js/plugins.d.ts
|
||||
|
||||
export interface AsyncHandler {
|
||||
(msg: Uint8Array): void;
|
||||
}
|
||||
|
||||
export interface PluginOp {
|
||||
dispatch(
|
||||
control: Uint8Array,
|
||||
zeroCopy?: ArrayBufferView | null
|
||||
): Uint8Array | null;
|
||||
setAsyncHandler(handler: AsyncHandler): void;
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
ops: {
|
||||
[name: string]: PluginOp;
|
||||
};
|
||||
}
|
||||
|
||||
/** Open and initalize a plugin.
|
||||
* Requires the `--allow-plugin` flag.
|
||||
*
|
||||
* const plugin = Deno.openPlugin("./path/to/some/plugin.so");
|
||||
* const some_op = plugin.ops.some_op;
|
||||
* const response = some_op.dispatch(new Uint8Array([1,2,3,4]));
|
||||
* console.log(`Response from plugin ${response}`);
|
||||
*/
|
||||
export function openPlugin(filename: string): Plugin;
|
||||
|
||||
// @url js/net.d.ts
|
||||
|
||||
type Transport = "tcp";
|
||||
|
|
|
@ -11,6 +11,7 @@ export type PermissionName =
|
|||
| "net"
|
||||
| "env"
|
||||
| "run"
|
||||
| "plugin"
|
||||
| "hrtime";
|
||||
// NOTE: Keep in sync with cli/permissions.rs
|
||||
|
||||
|
@ -31,6 +32,9 @@ interface NetPermissionDescriptor {
|
|||
interface EnvPermissionDescriptor {
|
||||
name: "env";
|
||||
}
|
||||
interface PluginPermissionDescriptor {
|
||||
name: "plugin";
|
||||
}
|
||||
interface HrtimePermissionDescriptor {
|
||||
name: "hrtime";
|
||||
}
|
||||
|
@ -40,6 +44,7 @@ type PermissionDescriptor =
|
|||
| ReadWritePermissionDescriptor
|
||||
| NetPermissionDescriptor
|
||||
| EnvPermissionDescriptor
|
||||
| PluginPermissionDescriptor
|
||||
| HrtimePermissionDescriptor;
|
||||
|
||||
/** https://w3c.github.io/permissions/#permissionstatus */
|
||||
|
|
|
@ -7,11 +7,12 @@ const knownPermissions: Deno.PermissionName[] = [
|
|||
"write",
|
||||
"net",
|
||||
"env",
|
||||
"plugin",
|
||||
"hrtime"
|
||||
];
|
||||
|
||||
for (const grant of knownPermissions) {
|
||||
testPerm({ [grant]: true }, async function envGranted(): Promise<void> {
|
||||
function genFunc(grant: Deno.PermissionName): () => Promise<void> {
|
||||
const gen: () => Promise<void> = async function Granted(): Promise<void> {
|
||||
const status0 = await Deno.permissions.query({ name: grant });
|
||||
assert(status0 != null);
|
||||
assertEquals(status0.state, "granted");
|
||||
|
@ -19,7 +20,14 @@ for (const grant of knownPermissions) {
|
|||
const status1 = await Deno.permissions.revoke({ name: grant });
|
||||
assert(status1 != null);
|
||||
assertEquals(status1.state, "prompt");
|
||||
});
|
||||
};
|
||||
// Properly name these generated functions.
|
||||
Object.defineProperty(gen, "name", { value: grant + "Granted" });
|
||||
return gen;
|
||||
}
|
||||
|
||||
for (const grant of knownPermissions) {
|
||||
testPerm({ [grant]: true }, genFunc(grant));
|
||||
}
|
||||
|
||||
test(async function permissionInvalidName(): Promise<void> {
|
||||
|
|
66
cli/js/plugins.ts
Normal file
66
cli/js/plugins.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { sendSync } from "./dispatch_json.ts";
|
||||
import { OP_OPEN_PLUGIN, setPluginAsyncHandler } from "./dispatch.ts";
|
||||
import { core } from "./core.ts";
|
||||
|
||||
export interface AsyncHandler {
|
||||
(msg: Uint8Array): void;
|
||||
}
|
||||
|
||||
interface PluginOp {
|
||||
dispatch(
|
||||
control: Uint8Array,
|
||||
zeroCopy?: ArrayBufferView | null
|
||||
): Uint8Array | null;
|
||||
setAsyncHandler(handler: AsyncHandler): void;
|
||||
}
|
||||
|
||||
class PluginOpImpl implements PluginOp {
|
||||
constructor(private readonly opId: number) {}
|
||||
|
||||
dispatch(
|
||||
control: Uint8Array,
|
||||
zeroCopy?: ArrayBufferView | null
|
||||
): Uint8Array | null {
|
||||
return core.dispatch(this.opId, control, zeroCopy);
|
||||
}
|
||||
|
||||
setAsyncHandler(handler: AsyncHandler): void {
|
||||
setPluginAsyncHandler(this.opId, handler);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(afinch7): add close method.
|
||||
|
||||
interface Plugin {
|
||||
ops: {
|
||||
[name: string]: PluginOp;
|
||||
};
|
||||
}
|
||||
|
||||
class PluginImpl implements Plugin {
|
||||
private _ops: { [name: string]: PluginOp } = {};
|
||||
|
||||
constructor(private readonly rid: number, ops: { [name: string]: number }) {
|
||||
for (const op in ops) {
|
||||
this._ops[op] = new PluginOpImpl(ops[op]);
|
||||
}
|
||||
}
|
||||
|
||||
get ops(): { [name: string]: PluginOp } {
|
||||
return Object.assign({}, this._ops);
|
||||
}
|
||||
}
|
||||
|
||||
interface OpenPluginResponse {
|
||||
rid: number;
|
||||
ops: {
|
||||
[name: string]: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function openPlugin(filename: string): Plugin {
|
||||
const response: OpenPluginResponse = sendSync(OP_OPEN_PLUGIN, {
|
||||
filename
|
||||
});
|
||||
return new PluginImpl(response.rid, response.ops);
|
||||
}
|
|
@ -26,6 +26,7 @@ interface TestPermissions {
|
|||
net?: boolean;
|
||||
env?: boolean;
|
||||
run?: boolean;
|
||||
plugin?: boolean;
|
||||
hrtime?: boolean;
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,7 @@ export interface Permissions {
|
|||
net: boolean;
|
||||
env: boolean;
|
||||
run: boolean;
|
||||
plugin: boolean;
|
||||
hrtime: boolean;
|
||||
}
|
||||
|
||||
|
@ -48,6 +50,7 @@ async function getProcessPermissions(): Promise<Permissions> {
|
|||
write: await isGranted("write"),
|
||||
net: await isGranted("net"),
|
||||
env: await isGranted("env"),
|
||||
plugin: await isGranted("plugin"),
|
||||
hrtime: await isGranted("hrtime")
|
||||
};
|
||||
}
|
||||
|
@ -75,8 +78,9 @@ function permToString(perms: Permissions): string {
|
|||
const n = perms.net ? 1 : 0;
|
||||
const e = perms.env ? 1 : 0;
|
||||
const u = perms.run ? 1 : 0;
|
||||
const p = perms.plugin ? 1 : 0;
|
||||
const h = perms.hrtime ? 1 : 0;
|
||||
return `permR${r}W${w}N${n}E${e}U${u}H${h}`;
|
||||
return `permR${r}W${w}N${n}E${e}U${u}P${p}H${h}`;
|
||||
}
|
||||
|
||||
function registerPermCombination(perms: Permissions): void {
|
||||
|
@ -93,6 +97,7 @@ function normalizeTestPermissions(perms: TestPermissions): Permissions {
|
|||
net: !!perms.net,
|
||||
run: !!perms.run,
|
||||
env: !!perms.env,
|
||||
plugin: !!perms.plugin,
|
||||
hrtime: !!perms.hrtime
|
||||
};
|
||||
}
|
||||
|
@ -120,6 +125,7 @@ export function test(fn: testing.TestFunction): void {
|
|||
net: false,
|
||||
env: false,
|
||||
run: false,
|
||||
plugin: false,
|
||||
hrtime: false
|
||||
},
|
||||
fn
|
||||
|
@ -176,6 +182,7 @@ test(function permissionsMatches(): void {
|
|||
net: false,
|
||||
env: false,
|
||||
run: false,
|
||||
plugin: false,
|
||||
hrtime: false
|
||||
},
|
||||
normalizeTestPermissions({ read: true })
|
||||
|
@ -190,6 +197,7 @@ test(function permissionsMatches(): void {
|
|||
net: false,
|
||||
env: false,
|
||||
run: false,
|
||||
plugin: false,
|
||||
hrtime: false
|
||||
},
|
||||
normalizeTestPermissions({})
|
||||
|
@ -204,6 +212,7 @@ test(function permissionsMatches(): void {
|
|||
net: true,
|
||||
env: true,
|
||||
run: true,
|
||||
plugin: true,
|
||||
hrtime: true
|
||||
},
|
||||
normalizeTestPermissions({ read: true })
|
||||
|
@ -219,6 +228,7 @@ test(function permissionsMatches(): void {
|
|||
net: true,
|
||||
env: false,
|
||||
run: false,
|
||||
plugin: false,
|
||||
hrtime: false
|
||||
},
|
||||
normalizeTestPermissions({ read: true })
|
||||
|
@ -234,6 +244,7 @@ test(function permissionsMatches(): void {
|
|||
net: true,
|
||||
env: true,
|
||||
run: true,
|
||||
plugin: true,
|
||||
hrtime: true
|
||||
},
|
||||
{
|
||||
|
@ -242,6 +253,7 @@ test(function permissionsMatches(): void {
|
|||
net: true,
|
||||
env: true,
|
||||
run: true,
|
||||
plugin: true,
|
||||
hrtime: true
|
||||
}
|
||||
)
|
||||
|
|
|
@ -16,6 +16,7 @@ pub mod io;
|
|||
pub mod net;
|
||||
pub mod os;
|
||||
pub mod permissions;
|
||||
pub mod plugins;
|
||||
pub mod process;
|
||||
pub mod random;
|
||||
pub mod repl;
|
||||
|
|
|
@ -55,6 +55,7 @@ pub fn op_revoke_permission(
|
|||
"write" => permissions.allow_write.revoke(),
|
||||
"net" => permissions.allow_net.revoke(),
|
||||
"env" => permissions.allow_env.revoke(),
|
||||
"plugin" => permissions.allow_plugin.revoke(),
|
||||
"hrtime" => permissions.allow_hrtime.revoke(),
|
||||
_ => {}
|
||||
};
|
||||
|
@ -83,6 +84,7 @@ pub fn op_request_permission(
|
|||
}
|
||||
"net" => permissions.request_net(&args.url.as_ref().map(String::as_str)),
|
||||
"env" => Ok(permissions.request_env()),
|
||||
"plugin" => Ok(permissions.request_plugin()),
|
||||
"hrtime" => Ok(permissions.request_hrtime()),
|
||||
n => Err(type_error(format!("No such permission name: {}", n))),
|
||||
}?;
|
||||
|
|
96
cli/ops/plugins.rs
Normal file
96
cli/ops/plugins.rs
Normal file
|
@ -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<deno::OpRegistry>) {
|
||||
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<P: AsRef<OsStr>>(lib_path: P) -> Result<Library, ErrBox> {
|
||||
debug!("Loading Plugin: {:#?}", lib_path.as_ref());
|
||||
|
||||
Library::open(lib_path).map_err(ErrBox::from)
|
||||
}
|
||||
|
||||
struct PluginResource {
|
||||
lib: Library,
|
||||
ops: HashMap<String, OpId>,
|
||||
}
|
||||
|
||||
impl Resource for PluginResource {}
|
||||
|
||||
struct InitContext {
|
||||
ops: HashMap<String, Box<OpDispatcher>>,
|
||||
}
|
||||
|
||||
impl PluginInitContext for InitContext {
|
||||
fn register_op(&mut self, name: &str, op: Box<OpDispatcher>) {
|
||||
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<deno::OpRegistry>,
|
||||
state: &ThreadSafeState,
|
||||
args: Value,
|
||||
_zero_copy: Option<PinnedBuf>,
|
||||
) -> Result<JsonOp, ErrBox> {
|
||||
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::<PluginResource>(rid).unwrap();
|
||||
|
||||
let init_fn = *unsafe {
|
||||
plugin_resource
|
||||
.lib
|
||||
.symbol::<PluginInitFn>("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 }),
|
||||
))
|
||||
}
|
|
@ -227,7 +227,12 @@ fn op_host_get_worker_closed(
|
|||
};
|
||||
let op = future.then(move |_result| {
|
||||
let mut workers_table = state_.workers.lock().unwrap();
|
||||
workers_table.remove(&id);
|
||||
let maybe_worker = workers_table.remove(&id);
|
||||
if let Some(worker) = maybe_worker {
|
||||
let mut channels = worker.state.worker_channels.lock().unwrap();
|
||||
channels.sender.close_channel();
|
||||
channels.receiver.close();
|
||||
};
|
||||
futures::future::ok(json!({}))
|
||||
});
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ pub struct DenoPermissions {
|
|||
pub net_whitelist: HashSet<String>,
|
||||
pub allow_env: PermissionState,
|
||||
pub allow_run: PermissionState,
|
||||
pub allow_plugin: PermissionState,
|
||||
pub allow_hrtime: PermissionState,
|
||||
}
|
||||
|
||||
|
@ -122,6 +123,7 @@ impl DenoPermissions {
|
|||
net_whitelist: flags.net_whitelist.iter().cloned().collect(),
|
||||
allow_env: PermissionState::from(flags.allow_env),
|
||||
allow_run: PermissionState::from(flags.allow_run),
|
||||
allow_plugin: PermissionState::from(flags.allow_plugin),
|
||||
allow_hrtime: PermissionState::from(flags.allow_hrtime),
|
||||
}
|
||||
}
|
||||
|
@ -207,6 +209,13 @@ impl DenoPermissions {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn check_plugin(&self, filename: &str) -> Result<(), ErrBox> {
|
||||
self.allow_plugin.check(
|
||||
&format!("access to open a plugin: {}", filename),
|
||||
"run again with the --allow-plugin flag",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn request_run(&mut self) -> PermissionState {
|
||||
self
|
||||
.allow_run
|
||||
|
@ -258,6 +267,10 @@ impl DenoPermissions {
|
|||
.request("Deno requests to access to high precision time.")
|
||||
}
|
||||
|
||||
pub fn request_plugin(&mut self) -> PermissionState {
|
||||
self.allow_plugin.request("Deno requests to open plugins.")
|
||||
}
|
||||
|
||||
pub fn get_permission_state(
|
||||
&self,
|
||||
name: &str,
|
||||
|
@ -270,6 +283,7 @@ impl DenoPermissions {
|
|||
"write" => Ok(self.get_state_write(path)),
|
||||
"net" => self.get_state_net_url(url),
|
||||
"env" => Ok(self.allow_env),
|
||||
"plugin" => Ok(self.allow_plugin),
|
||||
"hrtime" => Ok(self.allow_hrtime),
|
||||
n => Err(type_error(format!("No such permission name: {}", n))),
|
||||
}
|
||||
|
@ -652,6 +666,21 @@ mod tests {
|
|||
assert_eq!(perms1.request_env(), PermissionState::Deny);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permissions_request_plugin() {
|
||||
let mut perms0 = DenoPermissions::from_flags(&DenoFlags {
|
||||
..Default::default()
|
||||
});
|
||||
set_prompt_result(true);
|
||||
assert_eq!(perms0.request_plugin(), PermissionState::Allow);
|
||||
|
||||
let mut perms1 = DenoPermissions::from_flags(&DenoFlags {
|
||||
..Default::default()
|
||||
});
|
||||
set_prompt_result(false);
|
||||
assert_eq!(perms1.request_plugin(), PermissionState::Deny);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permissions_request_hrtime() {
|
||||
let mut perms0 = DenoPermissions::from_flags(&DenoFlags {
|
||||
|
|
|
@ -295,6 +295,11 @@ impl ThreadSafeState {
|
|||
self.permissions.lock().unwrap().check_run()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn check_plugin(&self, filename: &str) -> Result<(), ErrBox> {
|
||||
self.permissions.lock().unwrap().check_plugin(filename)
|
||||
}
|
||||
|
||||
pub fn check_dyn_import(
|
||||
self: &Self,
|
||||
module_specifier: &ModuleSpecifier,
|
||||
|
|
|
@ -50,6 +50,7 @@ impl Worker {
|
|||
let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false)));
|
||||
{
|
||||
let mut i = isolate.lock().unwrap();
|
||||
let op_registry = i.op_registry.clone();
|
||||
|
||||
ops::compiler::init(&mut i, &state);
|
||||
ops::errors::init(&mut i, &state);
|
||||
|
@ -57,6 +58,7 @@ impl Worker {
|
|||
ops::files::init(&mut i, &state);
|
||||
ops::fs::init(&mut i, &state);
|
||||
ops::io::init(&mut i, &state);
|
||||
ops::plugins::init(&mut i, &state, op_registry);
|
||||
ops::net::init(&mut i, &state);
|
||||
ops::tls::init(&mut i, &state);
|
||||
ops::os::init(&mut i, &state);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -27,7 +27,7 @@ pub type CoreError = ();
|
|||
pub type CoreOp = Op<CoreError>;
|
||||
|
||||
/// Main type describing op
|
||||
type OpDispatcher =
|
||||
pub type OpDispatcher =
|
||||
dyn Fn(&[u8], Option<PinnedBuf>) -> CoreOp + Send + Sync + 'static;
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
22
core/plugins.rs
Normal file
22
core/plugins.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::libdeno::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<dyn Fn(&[u8], Option<PinnedBuf>) -> CoreOp + Send + Sync + 'static>,
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! init_fn {
|
||||
($fn:path) => {
|
||||
#[no_mangle]
|
||||
pub fn deno_plugin_init(context: &mut dyn PluginInitContext) {
|
||||
$fn(context)
|
||||
}
|
||||
};
|
||||
}
|
14
test_plugin/Cargo.toml
Normal file
14
test_plugin/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "test_plugin"
|
||||
version = "0.0.1"
|
||||
authors = ["the deno authors"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
deno = { path = "../core" }
|
||||
deno_cli = { path = "../cli" }
|
53
test_plugin/src/lib.rs
Normal file
53
test_plugin/src/lib.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
#[macro_use]
|
||||
extern crate deno;
|
||||
extern crate futures;
|
||||
|
||||
use deno::CoreOp;
|
||||
use deno::Op;
|
||||
use deno::PluginInitContext;
|
||||
use deno::{Buf, PinnedBuf};
|
||||
use futures::future::FutureExt;
|
||||
|
||||
fn init(context: &mut dyn PluginInitContext) {
|
||||
context.register_op("testSync", Box::new(op_test_sync));
|
||||
context.register_op("testAsync", Box::new(op_test_async));
|
||||
}
|
||||
init_fn!(init);
|
||||
|
||||
pub fn op_test_sync(data: &[u8], zero_copy: Option<PinnedBuf>) -> CoreOp {
|
||||
if let Some(buf) = zero_copy {
|
||||
let data_str = std::str::from_utf8(&data[..]).unwrap();
|
||||
let buf_str = std::str::from_utf8(&buf[..]).unwrap();
|
||||
println!(
|
||||
"Hello from plugin. data: {} | zero_copy: {}",
|
||||
data_str, buf_str
|
||||
);
|
||||
}
|
||||
let result = b"test";
|
||||
let result_box: Buf = Box::new(*result);
|
||||
Op::Sync(result_box)
|
||||
}
|
||||
|
||||
pub fn op_test_async(data: &[u8], zero_copy: Option<PinnedBuf>) -> CoreOp {
|
||||
let data_str = std::str::from_utf8(&data[..]).unwrap().to_string();
|
||||
let fut = async move {
|
||||
if let Some(buf) = zero_copy {
|
||||
let buf_str = std::str::from_utf8(&buf[..]).unwrap();
|
||||
println!(
|
||||
"Hello from plugin. data: {} | zero_copy: {}",
|
||||
data_str, buf_str
|
||||
);
|
||||
}
|
||||
let (tx, rx) = futures::channel::oneshot::channel::<Result<(), ()>>();
|
||||
std::thread::spawn(move || {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
tx.send(Ok(())).unwrap();
|
||||
});
|
||||
assert!(rx.await.is_ok());
|
||||
let result = b"test";
|
||||
let result_box: Buf = Box::new(*result);
|
||||
Ok(result_box)
|
||||
};
|
||||
|
||||
Op::Async(fut.boxed())
|
||||
}
|
44
test_plugin/tests/integration_tests.rs
Normal file
44
test_plugin/tests/integration_tests.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use deno_cli::test_util::*;
|
||||
use std::process::Command;
|
||||
|
||||
fn deno_cmd() -> Command {
|
||||
assert!(deno_exe_path().exists());
|
||||
Command::new(deno_exe_path())
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
const BUILD_VARIANT: &str = "debug";
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
const BUILD_VARIANT: &str = "release";
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let mut build_plugin_base = Command::new("cargo");
|
||||
let mut build_plugin =
|
||||
build_plugin_base.arg("build").arg("-p").arg("test_plugin");
|
||||
if BUILD_VARIANT == "release" {
|
||||
build_plugin = build_plugin.arg("--release");
|
||||
}
|
||||
let _build_plugin_output = build_plugin.output().unwrap();
|
||||
let output = deno_cmd()
|
||||
.arg("--allow-plugin")
|
||||
.arg("tests/test.js")
|
||||
.arg(BUILD_VARIANT)
|
||||
.output()
|
||||
.unwrap();
|
||||
let stdout = std::str::from_utf8(&output.stdout).unwrap();
|
||||
let stderr = std::str::from_utf8(&output.stderr).unwrap();
|
||||
if !output.status.success() {
|
||||
println!("stdout {}", stdout);
|
||||
println!("stderr {}", stderr);
|
||||
}
|
||||
assert!(output.status.success());
|
||||
let expected = if cfg!(target_os = "windows") {
|
||||
"Hello from plugin. data: test | zero_copy: test\nPlugin Sync Response: test\r\nHello from plugin. data: test | zero_copy: test\nPlugin Async Response: test\r\n"
|
||||
} else {
|
||||
"Hello from plugin. data: test | zero_copy: test\nPlugin Sync Response: test\nHello from plugin. data: test | zero_copy: test\nPlugin Async Response: test\n"
|
||||
};
|
||||
assert_eq!(stdout, expected);
|
||||
assert_eq!(stderr, "");
|
||||
}
|
47
test_plugin/tests/test.js
Normal file
47
test_plugin/tests/test.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
const filenameBase = "test_plugin";
|
||||
|
||||
let filenameSuffix = ".so";
|
||||
let filenamePrefix = "lib";
|
||||
|
||||
if (Deno.build.os === "win") {
|
||||
filenameSuffix = ".dll";
|
||||
filenamePrefix = "";
|
||||
}
|
||||
if (Deno.build.os === "mac") {
|
||||
filenameSuffix = ".dylib";
|
||||
}
|
||||
|
||||
const filename = `../target/${Deno.args[1]}/${filenamePrefix}${filenameBase}${filenameSuffix}`;
|
||||
|
||||
const plugin = Deno.openPlugin(filename);
|
||||
|
||||
const { testSync, testAsync } = plugin.ops;
|
||||
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
function runTestSync() {
|
||||
const response = testSync.dispatch(
|
||||
new Uint8Array([116, 101, 115, 116]),
|
||||
new Uint8Array([116, 101, 115, 116])
|
||||
);
|
||||
|
||||
console.log(`Plugin Sync Response: ${textDecoder.decode(response)}`);
|
||||
}
|
||||
|
||||
testAsync.setAsyncHandler(response => {
|
||||
console.log(`Plugin Async Response: ${textDecoder.decode(response)}`);
|
||||
});
|
||||
|
||||
function runTestAsync() {
|
||||
const response = testAsync.dispatch(
|
||||
new Uint8Array([116, 101, 115, 116]),
|
||||
new Uint8Array([116, 101, 115, 116])
|
||||
);
|
||||
|
||||
if (response != null || response != undefined) {
|
||||
throw new Error("Expected null response!");
|
||||
}
|
||||
}
|
||||
|
||||
runTestSync();
|
||||
runTestAsync();
|
Loading…
Reference in a new issue