summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml12
-rw-r--r--Cargo.lock52
-rw-r--r--Cargo.toml3
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/diagnostics.rs2
-rw-r--r--cli/dts/lib.deno.ns.d.ts8
-rw-r--r--cli/dts/lib.deno.unstable.d.ts76
-rw-r--r--cli/flags.rs54
-rw-r--r--cli/standalone.rs2
-rw-r--r--cli/tests/integration/lsp_tests.rs12
-rw-r--r--cli/tests/test/allow_all.out4
-rw-r--r--cli/tests/test/allow_all.ts2
-rw-r--r--cli/tests/test/allow_none.out6
-rw-r--r--cli/tests/test/allow_none.ts2
-rw-r--r--cli/tests/test/ignore_permissions.ts2
-rw-r--r--cli/tests/unit/test_util.ts4
-rw-r--r--cli/tests/workers/no_permissions_worker.js4
-rw-r--r--cli/tools/standalone.rs2
-rw-r--r--extensions/ffi/00_ffi.js30
-rw-r--r--extensions/ffi/Cargo.toml20
-rw-r--r--extensions/ffi/README.md3
-rw-r--r--extensions/ffi/lib.rs397
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/js/11_workers.js6
-rw-r--r--runtime/js/40_permissions.js4
-rw-r--r--runtime/js/40_plugins.js16
-rw-r--r--runtime/js/90_deno_ns.js2
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/ops/mod.rs1
-rw-r--r--runtime/ops/permissions.rs7
-rw-r--r--runtime/ops/plugin.rs86
-rw-r--r--runtime/ops/worker_host.rs43
-rw-r--r--runtime/permissions.rs161
-rw-r--r--runtime/web_worker.rs3
-rw-r--r--runtime/worker.rs3
-rw-r--r--test_ffi/Cargo.toml (renamed from test_plugin/Cargo.toml)9
-rw-r--r--test_ffi/README.md1
-rw-r--r--test_ffi/src/lib.rs9
-rw-r--r--test_ffi/tests/integration_tests.rs (renamed from test_plugin/tests/integration_tests.rs)27
-rw-r--r--test_ffi/tests/test.js33
-rw-r--r--test_plugin/README.md9
-rw-r--r--test_plugin/src/lib.rs114
-rw-r--r--test_plugin/tests/test.js135
44 files changed, 860 insertions, 511 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fe25f6645..c9d98b524 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -164,11 +164,13 @@ jobs:
sudo apt-get update
sudo apt-get install debootstrap
- # Note: git, nc, strace, and time, are needed to run the benchmarks.
- sudo debootstrap \
- --include=ca-certificates,curl,git,netcat-openbsd,strace,time \
- --no-merged-usr --variant=minbase bionic /sysroot \
- http://azure.archive.ubuntu.com/ubuntu
+ # `file` and `make` are needed to build libffi-sys.
+ # `curl` is needed to build rusty_v8.
+ # `git`, `nc`, `strace`, and `time`, are needed to run the benchmarks.
+ sudo debootstrap \
+ --include=ca-certificates,curl,file,git,make,netcat-openbsd,strace,time \
+ --no-merged-usr --variant=minbase bionic /sysroot \
+ http://azure.archive.ubuntu.com/ubuntu
sudo mount --rbind /dev /sysroot/dev
sudo mount --rbind /sys /sysroot/sys
sudo mount --rbind /home /sysroot/home
diff --git a/Cargo.lock b/Cargo.lock
index 4820b8541..09bcfceb4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,6 +13,12 @@ dependencies = [
]
[[package]]
+name = "abort_on_panic"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955f37ac58af2416bac687c8ab66a4ccba282229bd7422a28d2281a5e66a6116"
+
+[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -568,6 +574,7 @@ dependencies = [
"deno_crypto",
"deno_doc",
"deno_fetch",
+ "deno_ffi",
"deno_http",
"deno_lint",
"deno_net",
@@ -630,6 +637,27 @@ dependencies = [
]
[[package]]
+name = "deno-libffi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a844ceea9e6233005c62dfe0bb7c1adab786ea78bab4ac1e5ea5cd2a5d47761"
+dependencies = [
+ "abort_on_panic",
+ "deno-libffi-sys",
+ "libc",
+]
+
+[[package]]
+name = "deno-libffi-sys"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15a12b5205cc4f3944cefa192e851a0ee15c226b41733468b2660d4ab2bdf555"
+dependencies = [
+ "cc",
+ "make-cmd",
+]
+
+[[package]]
name = "deno_bench_util"
version = "0.7.0"
dependencies = [
@@ -726,6 +754,16 @@ dependencies = [
]
[[package]]
+name = "deno_ffi"
+version = "0.1.0"
+dependencies = [
+ "deno-libffi",
+ "deno_core",
+ "dlopen",
+ "serde",
+]
+
+[[package]]
name = "deno_http"
version = "0.4.0"
dependencies = [
@@ -786,6 +824,7 @@ dependencies = [
"deno_core",
"deno_crypto",
"deno_fetch",
+ "deno_ffi",
"deno_http",
"deno_net",
"deno_timers",
@@ -2107,6 +2146,12 @@ dependencies = [
]
[[package]]
+name = "make-cmd"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
+
+[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3831,12 +3876,9 @@ dependencies = [
]
[[package]]
-name = "test_plugin"
-version = "0.0.1"
+name = "test_ffi"
+version = "0.1.0"
dependencies = [
- "deno_core",
- "futures",
- "serde",
"test_util",
]
diff --git a/Cargo.toml b/Cargo.toml
index e90f005f2..8d0768697 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,12 +6,13 @@ members = [
"cli",
"core",
"runtime",
- "test_plugin",
+ "test_ffi",
"test_util",
"extensions/broadcast_channel",
"extensions/console",
"extensions/crypto",
"extensions/fetch",
+ "extensions/ffi",
"extensions/http",
"extensions/net",
"extensions/timers",
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 5d71f6037..22b734f13 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -25,6 +25,7 @@ deno_console = { version = "0.13.0", path = "../extensions/console" }
deno_core = { version = "0.95.0", path = "../core" }
deno_crypto = { version = "0.27.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.36.0", path = "../extensions/fetch" }
+deno_ffi = { version = "0.1.0", path = "../extensions/ffi" }
deno_http = { version = "0.4.0", path = "../extensions/http" }
deno_net = { version = "0.4.0", path = "../extensions/net" }
deno_timers = { version = "0.11.0", path = "../extensions/timers" }
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
index 18506ccc8..01c096a11 100644
--- a/cli/diagnostics.rs
+++ b/cli/diagnostics.rs
@@ -54,7 +54,7 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"listen",
"listenDatagram",
"loadavg",
- "openPlugin",
+ "dlopen",
"osRelease",
"ppid",
"resolveDns",
diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts
index a312f4bda..40ef4a76c 100644
--- a/cli/dts/lib.deno.ns.d.ts
+++ b/cli/dts/lib.deno.ns.d.ts
@@ -2131,7 +2131,7 @@ declare namespace Deno {
| "write"
| "net"
| "env"
- | "plugin"
+ | "ffi"
| "hrtime";
/** The current status of the permission. */
@@ -2167,8 +2167,8 @@ declare namespace Deno {
variable?: string;
}
- export interface PluginPermissionDescriptor {
- name: "plugin";
+ export interface FFIPermissionDescriptor {
+ name: "ffi";
}
export interface HrtimePermissionDescriptor {
@@ -2183,7 +2183,7 @@ declare namespace Deno {
| WritePermissionDescriptor
| NetPermissionDescriptor
| EnvPermissionDescriptor
- | PluginPermissionDescriptor
+ | FFIPermissionDescriptor
| HrtimePermissionDescriptor;
export interface PermissionStatusEventMap {
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index 64efa0a2f..ab4a63729 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -107,36 +107,44 @@ declare namespace Deno {
swapFree: number;
}
- /** **UNSTABLE**: new API, yet to be vetted.
- *
- * Open and initialize a plugin.
- *
- * ```ts
- * import { assert } from "https://deno.land/std/testing/asserts.ts";
- * const rid = Deno.openPlugin("./path/to/some/plugin.so");
- *
- * // The Deno.core namespace is needed to interact with plugins, but this is
- * // internal so we use ts-ignore to skip type checking these calls.
- * // @ts-ignore
- * const { op_test_sync, op_test_async } = Deno.core.ops();
- *
- * assert(op_test_sync);
- * assert(op_test_async);
- *
- * // @ts-ignore
- * const result = Deno.core.opSync("op_test_sync");
- *
- * // @ts-ignore
- * const result = await Deno.core.opAsync("op_test_sync");
- * ```
- *
- * Requires `allow-plugin` permission.
+ /** All possible types for interfacing with foreign functions */
+ export type NativeType =
+ | "void"
+ | "u8"
+ | "i8"
+ | "u16"
+ | "i16"
+ | "u32"
+ | "i32"
+ | "u64"
+ | "i64"
+ | "usize"
+ | "isize"
+ | "f32"
+ | "f64";
+
+ /** A foreign function as defined by its parameter and result types */
+ export interface ForeignFunction {
+ parameters: NativeType[];
+ result: NativeType;
+ }
+
+ /** A dynamic library resource */
+ export interface DynamicLibrary<S extends Record<string, ForeignFunction>> {
+ /** All of the registered symbols along with functions for calling them */
+ symbols: { [K in keyof S]: (...args: unknown[]) => unknown };
+
+ close(): void;
+ }
+
+ /** **UNSTABLE**: new API
*
- * The plugin system is not stable and will change in the future, hence the
- * lack of docs. For now take a look at the example
- * https://github.com/denoland/deno/tree/main/test_plugin
+ * Opens a dynamic library and registers symbols
*/
- export function openPlugin(filename: string): number;
+ export function dlopen<S extends Record<string, ForeignFunction>>(
+ filename: string,
+ symbols: S,
+ ): DynamicLibrary<S>;
/** The log category for a diagnostic message. */
export enum DiagnosticCategory {
@@ -1043,14 +1051,14 @@ declare namespace Deno {
*/
net?: "inherit" | boolean | string[];
- /** Specifies if the `plugin` permission should be requested or revoked.
- * If set to `"inherit"`, the current `plugin` permission will be inherited.
- * If set to `true`, the global `plugin` permission will be requested.
- * If set to `false`, the global `plugin` permission will be revoked.
+ /** Specifies if the `ffi` permission should be requested or revoked.
+ * If set to `"inherit"`, the current `ffi` permission will be inherited.
+ * If set to `true`, the global `ffi` permission will be requested.
+ * If set to `false`, the global `ffi` permission will be revoked.
*
* Defaults to "inherit".
*/
- plugin?: "inherit" | boolean;
+ ffi?: "inherit" | boolean;
/** Specifies if the `read` permission should be requested or revoked.
* If set to `"inherit"`, the current `read` permission will be inherited.
@@ -1137,7 +1145,7 @@ declare interface WorkerOptions {
* For example: `["https://deno.land", "localhost:8080"]`.
*/
net?: "inherit" | boolean | string[];
- plugin?: "inherit" | boolean;
+ ffi?: "inherit" | boolean;
read?: "inherit" | boolean | Array<string | URL>;
run?: "inherit" | boolean | Array<string | URL>;
write?: "inherit" | boolean | Array<string | URL>;
diff --git a/cli/flags.rs b/cli/flags.rs
index 086b20e10..f42dd771c 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -133,7 +133,7 @@ pub struct Flags {
pub allow_env: Option<Vec<String>>,
pub allow_hrtime: bool,
pub allow_net: Option<Vec<String>>,
- pub allow_plugin: bool,
+ pub allow_ffi: Option<Vec<String>>,
pub allow_read: Option<Vec<PathBuf>>,
pub allow_run: Option<Vec<String>>,
pub allow_write: Option<Vec<PathBuf>>,
@@ -235,8 +235,15 @@ impl Flags {
_ => {}
}
- if self.allow_plugin {
- args.push("--allow-plugin".to_string());
+ match &self.allow_ffi {
+ Some(ffi_allowlist) if ffi_allowlist.is_empty() => {
+ args.push("--allow-ffi".to_string());
+ }
+ Some(ffi_allowlist) => {
+ let s = format!("--allow-ffi={}", ffi_allowlist.join(","));
+ args.push(s);
+ }
+ _ => {}
}
if self.allow_hrtime {
@@ -253,7 +260,7 @@ impl From<Flags> for PermissionsOptions {
allow_env: flags.allow_env,
allow_hrtime: flags.allow_hrtime,
allow_net: flags.allow_net,
- allow_plugin: flags.allow_plugin,
+ allow_ffi: flags.allow_ffi,
allow_read: flags.allow_read,
allow_run: flags.allow_run,
allow_write: flags.allow_write,
@@ -1228,9 +1235,13 @@ fn permission_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
.help("Allow running subprocesses"),
)
.arg(
- Arg::with_name("allow-plugin")
- .long("allow-plugin")
- .help("Allow loading plugins"),
+ Arg::with_name("allow-ffi")
+ .long("allow-ffi")
+ .min_values(0)
+ .takes_value(true)
+ .use_delimiter(true)
+ .require_equals(true)
+ .help("Allow loading dynamic libraries"),
)
.arg(
Arg::with_name("allow-hrtime")
@@ -1577,7 +1588,7 @@ fn eval_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
flags.allow_run = Some(vec![]);
flags.allow_read = Some(vec![]);
flags.allow_write = Some(vec![]);
- flags.allow_plugin = true;
+ flags.allow_ffi = Some(vec![]);
flags.allow_hrtime = true;
// TODO(@satyarohith): remove this flag in 2.0.
let as_typescript = matches.is_present("ts");
@@ -1696,7 +1707,7 @@ fn repl_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
flags.allow_run = Some(vec![]);
flags.allow_read = Some(vec![]);
flags.allow_write = Some(vec![]);
- flags.allow_plugin = true;
+ flags.allow_ffi = Some(vec![]);
flags.allow_hrtime = true;
}
@@ -1876,9 +1887,12 @@ fn permission_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
debug!("run allowlist: {:#?}", &flags.allow_run);
}
- if matches.is_present("allow-plugin") {
- flags.allow_plugin = true;
+ if let Some(ffi_wl) = matches.values_of("allow-ffi") {
+ let ffi_allowlist: Vec<String> = ffi_wl.map(ToString::to_string).collect();
+ flags.allow_ffi = Some(ffi_allowlist);
+ debug!("ffi allowlist: {:#?}", &flags.allow_ffi);
}
+
if matches.is_present("allow-hrtime") {
flags.allow_hrtime = true;
}
@@ -1888,7 +1902,7 @@ fn permission_args_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
flags.allow_net = Some(vec![]);
flags.allow_run = Some(vec![]);
flags.allow_write = Some(vec![]);
- flags.allow_plugin = true;
+ flags.allow_ffi = Some(vec![]);
flags.allow_hrtime = true;
}
if matches.is_present("prompt") {
@@ -2227,7 +2241,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2564,7 +2578,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2587,7 +2601,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2611,7 +2625,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2648,7 +2662,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2678,7 +2692,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2698,7 +2712,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
@@ -2732,7 +2746,7 @@ mod tests {
allow_run: Some(vec![]),
allow_read: Some(vec![]),
allow_write: Some(vec![]),
- allow_plugin: true,
+ allow_ffi: Some(vec![]),
allow_hrtime: true,
..Flags::default()
}
diff --git a/cli/standalone.rs b/cli/standalone.rs
index 9a693d961..3c8dabd3a 100644
--- a/cli/standalone.rs
+++ b/cli/standalone.rs
@@ -195,7 +195,7 @@ fn metadata_to_flags(metadata: &Metadata) -> Flags {
allow_env: permissions.allow_env,
allow_hrtime: permissions.allow_hrtime,
allow_net: permissions.allow_net,
- allow_plugin: permissions.allow_plugin,
+ allow_ffi: permissions.allow_ffi,
allow_read: permissions.allow_read,
allow_run: permissions.allow_run,
allow_write: permissions.allow_write,
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index e66b59c2b..a27ebec45 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -502,7 +502,7 @@ fn lsp_hover_unstable_disabled() {
"uri": "file:///a/file.ts",
"languageId": "typescript",
"version": 1,
- "text": "console.log(Deno.openPlugin);\n"
+ "text": "console.log(Deno.dlopen);\n"
}
}),
);
@@ -537,7 +537,7 @@ fn lsp_hover_unstable_disabled() {
},
"end": {
"line": 0,
- "character": 27
+ "character": 23
}
}
}))
@@ -555,7 +555,7 @@ fn lsp_hover_unstable_enabled() {
"uri": "file:///a/file.ts",
"languageId": "typescript",
"version": 1,
- "text": "console.log(Deno.openPlugin);\n"
+ "text": "console.log(Deno.ppid);\n"
}
}),
);
@@ -580,9 +580,9 @@ fn lsp_hover_unstable_enabled() {
"contents":[
{
"language":"typescript",
- "value":"function Deno.openPlugin(filename: string): number"
+ "value":"const Deno.ppid: number"
},
- "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nimport { assert } from \"https://deno.land/std/testing/asserts.ts\";\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\n\n// The Deno.core namespace is needed to interact with plugins, but this is\n// internal so we use ts-ignore to skip type checking these calls.\n// @ts-ignore\nconst { op_test_sync, op_test_async } = Deno.core.ops();\n\nassert(op_test_sync);\nassert(op_test_async);\n\n// @ts-ignore\nconst result = Deno.core.opSync(\"op_test_sync\");\n\n// @ts-ignore\nconst result = await Deno.core.opAsync(\"op_test_sync\");\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin"
+ "The pid of the current process's parent."
],
"range":{
"start":{
@@ -591,7 +591,7 @@ fn lsp_hover_unstable_enabled() {
},
"end":{
"line":0,
- "character":27
+ "character":21
}
}
}))
diff --git a/cli/tests/test/allow_all.out b/cli/tests/test/allow_all.out
index 3be26c6e0..9b7367b94 100644
--- a/cli/tests/test/allow_all.out
+++ b/cli/tests/test/allow_all.out
@@ -10,8 +10,8 @@ test env false ... ok [WILDCARD]
test env true ... ok [WILDCARD]
test run false ... ok [WILDCARD]
test run true ... ok [WILDCARD]
-test plugin false ... ok [WILDCARD]
-test plugin true ... ok [WILDCARD]
+test ffi false ... ok [WILDCARD]
+test ffi true ... ok [WILDCARD]
test hrtime false ... ok [WILDCARD]
test hrtime true ... ok [WILDCARD]
diff --git a/cli/tests/test/allow_all.ts b/cli/tests/test/allow_all.ts
index e4e12144e..b8f8c647d 100644
--- a/cli/tests/test/allow_all.ts
+++ b/cli/tests/test/allow_all.ts
@@ -6,7 +6,7 @@ const permissions: Deno.PermissionName[] = [
"net",
"env",
"run",
- "plugin",
+ "ffi",
"hrtime",
];
diff --git a/cli/tests/test/allow_none.out b/cli/tests/test/allow_none.out
index 96fb72278..b79c7e6bf 100644
--- a/cli/tests/test/allow_none.out
+++ b/cli/tests/test/allow_none.out
@@ -5,7 +5,7 @@ test write ... FAILED [WILDCARD]
test net ... FAILED [WILDCARD]
test env ... FAILED [WILDCARD]
test run ... FAILED [WILDCARD]
-test plugin ... FAILED [WILDCARD]
+test ffi ... FAILED [WILDCARD]
test hrtime ... FAILED [WILDCARD]
failures:
@@ -30,7 +30,7 @@ run
PermissionDenied: Can't escalate parent thread permissions
[WILDCARD]
-plugin
+ffi
PermissionDenied: Can't escalate parent thread permissions
[WILDCARD]
@@ -45,7 +45,7 @@ failures:
net
env
run
- plugin
+ ffi
hrtime
test result: FAILED. 0 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out [WILDCARD]
diff --git a/cli/tests/test/allow_none.ts b/cli/tests/test/allow_none.ts
index c0a930eb1..7872c774e 100644
--- a/cli/tests/test/allow_none.ts
+++ b/cli/tests/test/allow_none.ts
@@ -6,7 +6,7 @@ const permissions: Deno.PermissionName[] = [
"net",
"env",
"run",
- "plugin",
+ "ffi",
"hrtime",
];
diff --git a/cli/tests/test/ignore_permissions.ts b/cli/tests/test/ignore_permissions.ts
index bd0567a46..ff3084441 100644
--- a/cli/tests/test/ignore_permissions.ts
+++ b/cli/tests/test/ignore_permissions.ts
@@ -6,7 +6,7 @@ Deno.test({
net: true,
env: true,
run: true,
- plugin: true,
+ ffi: true,
hrtime: true,
},
ignore: true,
diff --git a/cli/tests/unit/test_util.ts b/cli/tests/unit/test_util.ts
index 79e93d70d..6745946c6 100644
--- a/cli/tests/unit/test_util.ts
+++ b/cli/tests/unit/test_util.ts
@@ -32,7 +32,7 @@ interface UnitTestPermissions {
net?: boolean;
env?: boolean;
run?: boolean;
- plugin?: boolean;
+ ffi?: boolean;
hrtime?: boolean;
}
@@ -84,7 +84,7 @@ export function unitTest(
net: false,
env: false,
run: false,
- plugin: false,
+ ffi: false,
hrtime: false,
}, options.perms),
};
diff --git a/cli/tests/workers/no_permissions_worker.js b/cli/tests/workers/no_permissions_worker.js
index 8a4f79d57..db0d911ac 100644
--- a/cli/tests/workers/no_permissions_worker.js
+++ b/cli/tests/workers/no_permissions_worker.js
@@ -1,14 +1,14 @@
self.onmessage = async () => {
const hrtime = await Deno.permissions.query({ name: "hrtime" });
const net = await Deno.permissions.query({ name: "net" });
- const plugin = await Deno.permissions.query({ name: "plugin" });
+ const ffi = await Deno.permissions.query({ name: "ffi" });
const read = await Deno.permissions.query({ name: "read" });
const run = await Deno.permissions.query({ name: "run" });
const write = await Deno.permissions.query({ name: "write" });
self.postMessage(
hrtime.state === "denied" &&
net.state === "denied" &&
- plugin.state === "denied" &&
+ ffi.state === "denied" &&
read.state === "denied" &&
run.state === "denied" &&
write.state === "denied",
diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs
index dfec6c7dd..5f89b592d 100644
--- a/cli/tools/standalone.rs
+++ b/cli/tools/standalone.rs
@@ -201,7 +201,7 @@ pub fn compile_to_runtime_flags(
allow_env: flags.allow_env,
allow_hrtime: flags.allow_hrtime,
allow_net: flags.allow_net,
- allow_plugin: flags.allow_plugin,
+ allow_ffi: flags.allow_ffi,
allow_read: flags.allow_read,
allow_run: flags.allow_run,
allow_write: flags.allow_write,
diff --git a/extensions/ffi/00_ffi.js b/extensions/ffi/00_ffi.js
new file mode 100644
index 000000000..3c4112a47
--- /dev/null
+++ b/extensions/ffi/00_ffi.js
@@ -0,0 +1,30 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+"use strict";
+
+((window) => {
+ const core = window.Deno.core;
+
+ class DynamicLibrary {
+ #rid;
+ symbols = {};
+
+ constructor(path, symbols) {
+ this.#rid = core.opSync("op_ffi_load", { path, symbols });
+
+ for (const symbol in symbols) {
+ this.symbols[symbol] = (...parameters) =>
+ core.opSync("op_ffi_call", { rid: this.#rid, symbol, parameters });
+ }
+ }
+
+ close() {
+ core.close(this.#rid);
+ }
+ }
+
+ function dlopen(path, symbols) {
+ return new DynamicLibrary(path, symbols);
+ }
+
+ window.__bootstrap.ffi = { dlopen };
+})(this);
diff --git a/extensions/ffi/Cargo.toml b/extensions/ffi/Cargo.toml
new file mode 100644
index 000000000..510b09292
--- /dev/null
+++ b/extensions/ffi/Cargo.toml
@@ -0,0 +1,20 @@
+# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_ffi"
+version = "0.1.0"
+authors = ["the Deno authors"]
+edition = "2018"
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/denoland/deno"
+description = "Dynamic library ffi for deno"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+deno_core = { version = "0.95.0", path = "../../core" }
+dlopen = "0.1.8"
+libffi = { version = "0.0.3", package = "deno-libffi" }
+serde = { version = "1.0.125", features = ["derive"] }
diff --git a/extensions/ffi/README.md b/extensions/ffi/README.md
new file mode 100644
index 000000000..cc2d81cd2
--- /dev/null
+++ b/extensions/ffi/README.md
@@ -0,0 +1,3 @@
+# deno_ffi
+
+This crate implements dynamic library ffi.
diff --git a/extensions/ffi/lib.rs b/extensions/ffi/lib.rs
new file mode 100644
index 000000000..125e6da99
--- /dev/null
+++ b/extensions/ffi/lib.rs
@@ -0,0 +1,397 @@
+// Copyright 2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::include_js_files;
+use deno_core::op_sync;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::Extension;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use dlopen::raw::Library;
+use libffi::middle::Arg;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::convert::TryFrom;
+use std::ffi::c_void;
+use std::rc::Rc;
+
+pub struct Unstable(pub bool);
+
+fn check_unstable(state: &OpState, api_name: &str) {
+ let unstable = state.borrow::<Unstable>();
+
+ if !unstable.0 {
+ eprintln!(
+ "Unstable API '{}'. The --unstable flag must be provided.",
+ api_name
+ );
+ std::process::exit(70);
+ }
+}
+
+pub trait FfiPermissions {
+ fn check(&mut self, path: &str) -> Result<(), AnyError>;
+}
+
+pub struct NoFfiPermissions;
+
+impl FfiPermissions for NoFfiPermissions {
+ fn check(&mut self, _path: &str) -> Result<(), AnyError> {
+ Ok(())
+ }
+}
+
+struct Symbol {
+ cif: libffi::middle::Cif,
+ ptr: libffi::middle::CodePtr,
+ parameter_types: Vec<NativeType>,
+ result_type: NativeType,
+}
+
+struct DynamicLibraryResource {
+ lib: Library,
+ symbols: HashMap<String, Symbol>,
+}
+
+impl Resource for DynamicLibraryResource {
+ fn name(&self) -> Cow<str> {
+ "dynamicLibrary".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ drop(self)
+ }
+}
+
+impl DynamicLibraryResource {
+ fn register(
+ &mut self,
+ symbol: String,
+ foreign_fn: ForeignFunction,
+ ) -> Result<(), AnyError> {
+ let fn_ptr = unsafe { self.lib.symbol::<*const c_void>(&symbol) }?;
+ let ptr = libffi::middle::CodePtr::from_ptr(fn_ptr as _);
+ let parameter_types =
+ foreign_fn.parameters.into_iter().map(NativeType::from);
+ let result_type = NativeType::from(foreign_fn.result);
+ let cif = libffi::middle::Cif::new(
+ parameter_types.clone().map(libffi::middle::Type::from),
+ result_type.into(),
+ );
+
+ self.symbols.insert(
+ symbol,
+ Symbol {
+ cif,
+ ptr,
+ parameter_types: parameter_types.collect(),
+ result_type,
+ },
+ );
+
+ Ok(())
+ }
+}
+
+pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
+ Extension::builder()
+ .js(include_js_files!(
+ prefix "deno:extensions/ffi",
+ "00_ffi.js",
+ ))
+ .ops(vec![
+ ("op_ffi_load", op_sync(op_ffi_load::<P>)),
+ ("op_ffi_call", op_sync(op_ffi_call)),
+ ])
+ .state(move |state| {
+ // Stolen from deno_webgpu, is there a better option?
+ state.put(Unstable(unstable));
+ Ok(())
+ })
+ .build()
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
+#[serde(rename_all = "lowercase")]
+enum NativeType {
+ Void,
+ U8,
+ I8,
+ U16,
+ I16,
+ U32,
+ I32,
+ U64,
+ I64,
+ USize,
+ ISize,
+ F32,
+ F64,
+}
+
+impl From<NativeType> for libffi::middle::Type {
+ fn from(native_type: NativeType) -> Self {
+ match native_type {
+ NativeType::Void => libffi::middle::Type::void(),
+ NativeType::U8 => libffi::middle::Type::u8(),
+ NativeType::I8 => libffi::middle::Type::i8(),
+ NativeType::U16 => libffi::middle::Type::u16(),
+ NativeType::I16 => libffi::middle::Type::i16(),
+ NativeType::U32 => libffi::middle::Type::u32(),
+ NativeType::I32 => libffi::middle::Type::i32(),
+ NativeType::U64 => libffi::middle::Type::u64(),
+ NativeType::I64 => libffi::middle::Type::i64(),
+ NativeType::USize => libffi::middle::Type::usize(),
+ NativeType::ISize => libffi::middle::Type::isize(),
+ NativeType::F32 => libffi::middle::Type::f32(),
+ NativeType::F64 => libffi::middle::Type::f64(),
+ }
+ }
+}
+
+impl From<String> for NativeType {
+ fn from(string: String) -> Self {
+ match string.as_str() {
+ "void" => NativeType::Void,
+ "u8" => NativeType::U8,
+ "i8" => NativeType::I8,
+ "u16" => NativeType::U16,
+ "i16" => NativeType::I16,
+ "u32" => NativeType::U32,
+ "i32" => NativeType::I32,
+ "u64" => NativeType::U64,
+ "i64" => NativeType::I64,
+ "usize" => NativeType::USize,
+ "isize" => NativeType::ISize,
+ "f32" => NativeType::F32,
+ "f64" => NativeType::F64,
+ _ => unimplemented!(),
+ }
+ }
+}
+
+#[repr(C)]
+union NativeValue {
+ void_value: (),
+ u8_value: u8,
+ i8_value: i8,
+ u16_value: u16,
+ i16_value: i16,
+ u32_value: u32,
+ i32_value: i32,
+ u64_value: u64,
+ i64_value: i64,
+ usize_value: usize,
+ isize_value: isize,
+ f32_value: f32,
+ f64_value: f64,
+}
+
+impl NativeValue {
+ fn new(native_type: NativeType, value: Value) -> Self {
+ match native_type {
+ NativeType::Void => Self { void_value: () },
+ NativeType::U8 => Self {
+ u8_value: value_as_uint::<u8>(value),
+ },
+ NativeType::I8 => Self {
+ i8_value: value_as_int::<i8>(value),
+ },
+ NativeType::U16 => Self {
+ u16_value: value_as_uint::<u16>(value),
+ },
+ NativeType::I16 => Self {
+ i16_value: value_as_int::<i16>(value),
+ },
+ NativeType::U32 => Self {
+ u32_value: value_as_uint::<u32>(value),
+ },
+ NativeType::I32 => Self {
+ i32_value: value_as_int::<i32>(value),
+ },
+ NativeType::U64 => Self {
+ u64_value: value_as_uint::<u64>(value),
+ },
+ NativeType::I64 => Self {
+ i64_value: value_as_int::<i64>(value),
+ },
+ NativeType::USize => Self {
+ usize_value: value_as_uint::<usize>(value),
+ },
+ NativeType::ISize => Self {
+ isize_value: value_as_int::<isize>(value),
+ },
+ NativeType::F32 => Self {
+ f32_value: value_as_f32(value),
+ },
+ NativeType::F64 => Self {
+ f64_value: value_as_f64(value),
+ },
+ }
+ }
+
+ unsafe fn as_arg(&self, native_type: NativeType) -> Arg {
+ match native_type {
+ NativeType::Void => Arg::new(&self.void_value),
+ NativeType::U8 => Arg::new(&self.u8_value),
+ NativeType::I8 => Arg::new(&self.i8_value),
+ NativeType::U16 => Arg::new(&self.u16_value),
+ NativeType::I16 => Arg::new(&self.i16_value),
+ NativeType::U32 => Arg::new(&self.u32_value),
+ NativeType::I32 => Arg::new(&self.i32_value),
+ NativeType::U64 => Arg::new(&self.u64_value),
+ NativeType::I64 => Arg::new(&self.i64_value),
+ NativeType::USize => Arg::new(&self.usize_value),
+ NativeType::ISize => Arg::new(&self.isize_value),
+ NativeType::F32 => Arg::new(&self.f32_value),
+ NativeType::F64 => Arg::new(&self.f64_value),
+ }
+ }
+}
+
+fn value_as_uint<T: TryFrom<u64>>(value: Value) -> T {
+ value
+ .as_u64()
+ .and_then(|v| T::try_from(v).ok())
+ .expect("Expected ffi arg value to be an unsigned integer")
+}
+
+fn value_as_int<T: TryFrom<i64>>(value: Value) -> T {
+ value
+ .as_i64()
+ .and_then(|v| T::try_from(v).ok())
+ .expect("Expected ffi arg value to be a signed integer")
+}
+
+fn value_as_f32(value: Value) -> f32 {
+ value_as_f64(value) as f32
+}
+
+fn value_as_f64(value: Value) -> f64 {
+ value
+ .as_f64()
+ .expect("Expected ffi arg value to be a float")
+}
+
+#[derive(Deserialize, Debug)]
+struct ForeignFunction {
+ parameters: Vec<String>,
+ result: String,
+}
+
+#[derive(Deserialize, Debug)]
+struct FfiLoadArgs {
+ path: String,
+ symbols: HashMap<String, ForeignFunction>,
+}
+
+fn op_ffi_load<FP>(
+ state: &mut deno_core::OpState,
+ args: FfiLoadArgs,
+ _: (),
+) -> Result<ResourceId, AnyError>
+where
+ FP: FfiPermissions + 'static,
+{
+ check_unstable(state, "Deno.dlopen");
+ let permissions = state.borrow_mut::<FP>();
+ permissions.check(&args.path)?;
+
+ let lib = Library::open(args.path)?;
+ let mut resource = DynamicLibraryResource {
+ lib,
+ symbols: HashMap::new(),
+ };
+
+ for (symbol, foreign_fn) in args.symbols {
+ resource.register(symbol, foreign_fn)?;
+ }
+
+ Ok(state.resource_table.add(resource))
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FfiCallArgs {
+ rid: ResourceId,
+ symbol: String,
+ parameters: Vec<Value>,
+}
+
+fn op_ffi_call(
+ state: &mut deno_core::OpState,
+ args: FfiCallArgs,
+ _: (),
+) -> Result<Value, AnyError> {
+ let resource = state
+ .resource_table
+ .get::<DynamicLibraryResource>(args.rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let symbol = resource
+ .symbols
+ .get(&args.symbol)
+ .ok_or_else(bad_resource_id)?;
+
+ let native_values = symbol
+ .parameter_types
+ .iter()
+ .zip(args.parameters.into_iter())
+ .map(|(&native_type, value)| NativeValue::new(native_type, value))
+ .collect::<Vec<_>>();
+
+ let call_args = symbol
+ .parameter_types
+ .iter()
+ .zip(native_values.iter())
+ .map(|(&native_type, native_value)| unsafe {
+ native_value.as_arg(native_type)
+ })
+ .collect::<Vec<_>>();
+
+ Ok(match symbol.result_type {
+ NativeType::Void => {
+ json!(unsafe { symbol.cif.call::<()>(symbol.ptr, &call_args) })
+ }
+ NativeType::U8 => {
+ json!(unsafe { symbol.cif.call::<u8>(symbol.ptr, &call_args) })
+ }
+ NativeType::I8 => {
+ json!(unsafe { symbol.cif.call::<i8>(symbol.ptr, &call_args) })
+ }
+ NativeType::U16 => {
+ json!(unsafe { symbol.cif.call::<u16>(symbol.ptr, &call_args) })
+ }
+ NativeType::I16 => {
+ json!(unsafe { symbol.cif.call::<i16>(symbol.ptr, &call_args) })
+ }
+ NativeType::U32 => {
+ json!(unsafe { symbol.cif.call::<u32>(symbol.ptr, &call_args) })
+ }
+ NativeType::I32 => {
+ json!(unsafe { symbol.cif.call::<i32>(symbol.ptr, &call_args) })
+ }
+ NativeType::U64 => {
+ json!(unsafe { symbol.cif.call::<u64>(symbol.ptr, &call_args) })
+ }
+ NativeType::I64 => {
+ json!(unsafe { symbol.cif.call::<i64>(symbol.ptr, &call_args) })
+ }
+ NativeType::USize => {
+ json!(unsafe { symbol.cif.call::<usize>(symbol.ptr, &call_args) })
+ }
+ NativeType::ISize => {
+ json!(unsafe { symbol.cif.call::<isize>(symbol.ptr, &call_args) })
+ }
+ NativeType::F32 => {
+ json!(unsafe { symbol.cif.call::<f32>(symbol.ptr, &call_args) })
+ }
+ NativeType::F64 => {
+ json!(unsafe { symbol.cif.call::<f64>(symbol.ptr, &call_args) })
+ }
+ })
+}
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 044c3a1a1..b2bad9844 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -23,6 +23,7 @@ deno_console = { version = "0.13.0", path = "../extensions/console" }
deno_core = { version = "0.95.0", path = "../core" }
deno_crypto = { version = "0.27.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.36.0", path = "../extensions/fetch" }
+deno_ffi = { version = "0.1.0", path = "../extensions/ffi" }
deno_http = { version = "0.4.0", path = "../extensions/http" }
deno_net = { version = "0.4.0", path = "../extensions/net" }
deno_timers = { version = "0.11.0", path = "../extensions/timers" }
@@ -43,6 +44,7 @@ deno_console = { version = "0.13.0", path = "../extensions/console" }
deno_core = { version = "0.95.0", path = "../core" }
deno_crypto = { version = "0.27.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.36.0", path = "../extensions/fetch" }
+deno_ffi = { version = "0.1.0", path = "../extensions/ffi" }
deno_http = { version = "0.4.0", path = "../extensions/http" }
deno_net = { version = "0.4.0", path = "../extensions/net" }
deno_timers = { version = "0.11.0", path = "../extensions/timers" }
diff --git a/runtime/build.rs b/runtime/build.rs
index 4e061c438..bb7947f36 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -60,6 +60,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
false, // No --unstable.
),
+ deno_ffi::init::<deno_ffi::NoFfiPermissions>(false),
deno_net::init::<deno_net::NoNetPermissions>(None, false), // No --unstable.
deno_http::init(),
];
diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js
index b59832635..38267f571 100644
--- a/runtime/js/11_workers.js
+++ b/runtime/js/11_workers.js
@@ -119,7 +119,7 @@
env = "inherit",
hrtime = "inherit",
net = "inherit",
- plugin = "inherit",
+ ffi = "inherit",
read = "inherit",
run = "inherit",
write = "inherit",
@@ -128,7 +128,7 @@
env: parseUnitPermission(env, "env"),
hrtime: parseUnitPermission(hrtime, "hrtime"),
net: parseArrayPermission(net, "net"),
- plugin: parseUnitPermission(plugin, "plugin"),
+ ffi: parseUnitPermission(ffi, "ffi"),
read: parseArrayPermission(read, "read"),
run: parseUnitPermission(run, "run"),
write: parseArrayPermission(write, "write"),
@@ -175,7 +175,7 @@
env: false,
hrtime: false,
net: false,
- plugin: false,
+ ffi: false,
read: false,
run: false,
write: false,
diff --git a/runtime/js/40_permissions.js b/runtime/js/40_permissions.js
index c84f8fde8..1b053f938 100644
--- a/runtime/js/40_permissions.js
+++ b/runtime/js/40_permissions.js
@@ -28,14 +28,14 @@
* @property {PermissionStatus} status
*/
- /** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "run" | "plugin" | "hrtime">} */
+ /** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "run" | "ffi" | "hrtime">} */
const permissionNames = [
"read",
"write",
"net",
"env",
"run",
- "plugin",
+ "ffi",
"hrtime",
];
diff --git a/runtime/js/40_plugins.js b/runtime/js/40_plugins.js
deleted file mode 100644
index 0796fd5ce..000000000
--- a/runtime/js/40_plugins.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
-
- function openPlugin(filename) {
- const rid = core.opSync("op_open_plugin", filename);
- core.syncOpsCache();
- return rid;
- }
-
- window.__bootstrap.plugins = {
- openPlugin,
- };
-})(this);
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index aee07eae7..796361d7a 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -110,7 +110,6 @@
Signal: __bootstrap.signals.Signal,
SignalStream: __bootstrap.signals.SignalStream,
emit: __bootstrap.compilerApi.emit,
- openPlugin: __bootstrap.plugins.openPlugin,
kill: __bootstrap.process.kill,
setRaw: __bootstrap.tty.setRaw,
consoleSize: __bootstrap.tty.consoleSize,
@@ -136,5 +135,6 @@
HttpClient: __bootstrap.fetch.HttpClient,
createHttpClient: __bootstrap.fetch.createHttpClient,
http: __bootstrap.http,
+ dlopen: __bootstrap.ffi.dlopen,
};
})(this);
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 2358899d4..d7aaa8eec 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -4,6 +4,7 @@ pub use deno_broadcast_channel;
pub use deno_console;
pub use deno_crypto;
pub use deno_fetch;
+pub use deno_ffi;
pub use deno_http;
pub use deno_net;
pub use deno_timers;
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index 82ccf0506..e08ddd1c0 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -6,7 +6,6 @@ pub mod http;
pub mod io;
pub mod os;
pub mod permissions;
-pub mod plugin;
pub mod process;
pub mod runtime;
pub mod signal;
diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs
index 3395430e4..d9f341633 100644
--- a/runtime/ops/permissions.rs
+++ b/runtime/ops/permissions.rs
@@ -28,6 +28,7 @@ pub struct PermissionArgs {
host: Option<String>,
variable: Option<String>,
command: Option<String>,
+ library: Option<String>,
}
pub fn op_query_permission(
@@ -49,7 +50,7 @@ pub fn op_query_permission(
),
"env" => permissions.env.query(args.variable.as_deref()),
"run" => permissions.run.query(args.command.as_deref()),
- "plugin" => permissions.plugin.query(),
+ "ffi" => permissions.ffi.query(args.library.as_deref()),
"hrtime" => permissions.hrtime.query(),
n => {
return Err(custom_error(
@@ -80,7 +81,7 @@ pub fn op_revoke_permission(
),
"env" => permissions.env.revoke(args.variable.as_deref()),
"run" => permissions.run.revoke(args.command.as_deref()),
- "plugin" => permissions.plugin.revoke(),
+ "ffi" => permissions.ffi.revoke(args.library.as_deref()),
"hrtime" => permissions.hrtime.revoke(),
n => {
return Err(custom_error(
@@ -111,7 +112,7 @@ pub fn op_request_permission(
),
"env" => permissions.env.request(args.variable.as_deref()),
"run" => permissions.run.request(args.command.as_deref()),
- "plugin" => permissions.plugin.request(),
+ "ffi" => permissions.ffi.request(args.library.as_deref()),
"hrtime" => permissions.hrtime.request(),
n => {
return Err(custom_error(
diff --git a/runtime/ops/plugin.rs b/runtime/ops/plugin.rs
deleted file mode 100644
index cc3bf93d5..000000000
--- a/runtime/ops/plugin.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use crate::permissions::Permissions;
-use deno_core::error::AnyError;
-use deno_core::op_sync;
-use deno_core::Extension;
-use deno_core::OpState;
-use deno_core::Resource;
-use deno_core::ResourceId;
-use dlopen::symbor::Library;
-use log::debug;
-use std::borrow::Cow;
-use std::mem;
-use std::path::PathBuf;
-use std::rc::Rc;
-
-/// A default `init` function for plugins which mimics the way the internal
-/// extensions are initalized. Plugins currently do not support all extension
-/// features and are most likely not going to in the future. Currently only
-/// `init_state` and `init_ops` are supported while `init_middleware` and `init_js`
-/// are not. Currently the `PluginResource` does not support being closed due to
-/// certain risks in unloading the dynamic library without unloading dependent
-/// functions and resources.
-pub type InitFn = fn() -> Extension;
-
-pub fn init() -> Extension {
- Extension::builder()
- .ops(vec![("op_open_plugin", op_sync(op_open_plugin))])
- .build()
-}
-
-pub fn op_open_plugin(
- state: &mut OpState,
- filename: String,
- _: (),
-) -> Result<ResourceId, AnyError> {
- let filename = PathBuf::from(&filename);
-
- super::check_unstable(state, "Deno.openPlugin");
- let permissions = state.borrow_mut::<Permissions>();
- permissions.plugin.check()?;
-
- debug!("Loading Plugin: {:#?}", filename);
- let plugin_lib = Library::open(filename).map(Rc::new)?;
- let plugin_resource = PluginResource::new(&plugin_lib);
-
- // Forgets the plugin_lib value to prevent segfaults when the process exits
- mem::forget(plugin_lib);
-
- let init = *unsafe { plugin_resource.0.symbol::<InitFn>("init") }?;
- let rid = state.resource_table.add(plugin_resource);
- let mut extension = init();
-
- if !extension.init_js().is_empty() {
- panic!("Plugins do not support loading js");
- }
-
- if extension.init_middleware().is_some() {
- panic!("Plugins do not support middleware");
- }
-
- extension.init_state(state)?;
- let ops = extension.init_ops().unwrap_or_default();
- for (name, opfn) in ops {
- state.op_table.register_op(name, opfn);
- }
-
- Ok(rid)
-}
-
-struct PluginResource(Rc<Library>);
-
-impl Resource for PluginResource {
- fn name(&self) -> Cow<str> {
- "plugin".into()
- }
-
- fn close(self: Rc<Self>) {
- unimplemented!();
- }
-}
-
-impl PluginResource {
- fn new(lib: &Rc<Library>) -> Self {
- Self(lib.clone())
- }
-}
diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs
index 2cd9a14ad..5315ff5c7 100644
--- a/runtime/ops/worker_host.rs
+++ b/runtime/ops/worker_host.rs
@@ -3,6 +3,7 @@
use crate::permissions::resolve_read_allowlist;
use crate::permissions::resolve_write_allowlist;
use crate::permissions::EnvDescriptor;
+use crate::permissions::FfiDescriptor;
use crate::permissions::NetDescriptor;
use crate::permissions::PermissionState;
use crate::permissions::Permissions;
@@ -218,6 +219,26 @@ fn merge_run_permission(
Ok(main)
}
+fn merge_ffi_permission(
+ mut main: UnaryPermission<FfiDescriptor>,
+ worker: Option<UnaryPermission<FfiDescriptor>>,
+) -> Result<UnaryPermission<FfiDescriptor>, AnyError> {
+ if let Some(worker) = worker {
+ if (worker.global_state < main.global_state)
+ || !worker.granted_list.iter().all(|x| main.check(&x.0).is_ok())
+ {
+ return Err(custom_error(
+ "PermissionDenied",
+ "Can't escalate parent thread permissions",
+ ));
+ } else {
+ main.global_state = worker.global_state;
+ main.granted_list = worker.granted_list;
+ }
+ }
+ Ok(main)
+}
+
pub fn create_worker_permissions(
main_perms: Permissions,
worker_perms: PermissionsArg,
@@ -226,7 +247,7 @@ pub fn create_worker_permissions(
env: merge_env_permission(main_perms.env, worker_perms.env)?,
hrtime: merge_boolean_permission(main_perms.hrtime, worker_perms.hrtime)?,
net: merge_net_permission(main_perms.net, worker_perms.net)?,
- plugin: merge_boolean_permission(main_perms.plugin, worker_perms.plugin)?,
+ ffi: merge_ffi_permission(main_perms.ffi, worker_perms.ffi)?,
read: merge_read_permission(main_perms.read, worker_perms.read)?,
run: merge_run_permission(main_perms.run, worker_perms.run)?,
write: merge_write_permission(main_perms.write, worker_perms.write)?,
@@ -241,8 +262,8 @@ pub struct PermissionsArg {
hrtime: Option<PermissionState>,
#[serde(default, deserialize_with = "as_unary_net_permission")]
net: Option<UnaryPermission<NetDescriptor>>,
- #[serde(default, deserialize_with = "as_permission_state")]
- plugin: Option<PermissionState>,
+ #[serde(default, deserialize_with = "as_unary_ffi_permission")]
+ ffi: Option<UnaryPermission<FfiDescriptor>>,
#[serde(default, deserialize_with = "as_unary_read_permission")]
read: Option<UnaryPermission<ReadDescriptor>>,
#[serde(default, deserialize_with = "as_unary_run_permission")]
@@ -414,6 +435,22 @@ where
}))
}
+fn as_unary_ffi_permission<'de, D>(
+ deserializer: D,
+) -> Result<Option<UnaryPermission<FfiDescriptor>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ let value: UnaryPermissionBase =
+ deserializer.deserialize_any(ParseBooleanOrStringVec)?;
+
+ Ok(Some(UnaryPermission::<FfiDescriptor> {
+ global_state: value.global_state,
+ granted_list: value.paths.into_iter().map(FfiDescriptor).collect(),
+ ..Default::default()
+ }))
+}
+
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateWorkerArgs {
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
index 5215743e3..9e97ac234 100644
--- a/runtime/permissions.rs
+++ b/runtime/permissions.rs
@@ -202,6 +202,9 @@ pub struct EnvDescriptor(pub String);
#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)]
pub struct RunDescriptor(pub String);
+#[derive(Clone, Eq, PartialEq, Hash, Debug, Default, Deserialize)]
+pub struct FfiDescriptor(pub String);
+
impl UnaryPermission<ReadDescriptor> {
pub fn query(&self, path: Option<&Path>) -> PermissionState {
let path = path.map(|p| resolve_from_cwd(p).unwrap());
@@ -787,6 +790,104 @@ impl UnaryPermission<RunDescriptor> {
}
}
+impl UnaryPermission<FfiDescriptor> {
+ pub fn query(&self, lib: Option<&str>) -> PermissionState {
+ if self.global_state == PermissionState::Denied
+ && match lib {
+ None => true,
+ Some(lib) => self.denied_list.iter().any(|lib_| lib_.0 == lib),
+ }
+ {
+ PermissionState::Denied
+ } else if self.global_state == PermissionState::Granted
+ || match lib {
+ None => false,
+ Some(lib) => self.granted_list.iter().any(|lib_| lib_.0 == lib),
+ }
+ {
+ PermissionState::Granted
+ } else {
+ PermissionState::Prompt
+ }
+ }
+
+ pub fn request(&mut self, lib: Option<&str>) -> PermissionState {
+ if let Some(lib) = lib {
+ let state = self.query(Some(lib));
+ if state == PermissionState::Prompt {
+ if permission_prompt(&format!("ffi access to \"{}\"", lib)) {
+ self.granted_list.retain(|lib_| lib_.0 != lib);
+ self.granted_list.insert(FfiDescriptor(lib.to_string()));
+ PermissionState::Granted
+ } else {
+ self.denied_list.retain(|lib_| lib_.0 != lib);
+ self.denied_list.insert(FfiDescriptor(lib.to_string()));
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
+ }
+ } else {
+ state
+ }
+ } else {
+ let state = self.query(None);
+ if state == PermissionState::Prompt {
+ if permission_prompt("ffi access") {
+ self.granted_list.clear();
+ self.global_state = PermissionState::Granted;
+ PermissionState::Granted
+ } else {
+ self.global_state = PermissionState::Denied;
+ PermissionState::Denied
+ }
+ } else {
+ state
+ }
+ }
+ }
+
+ pub fn revoke(&mut self, lib: Option<&str>) -> PermissionState {
+ if let Some(lib) = lib {
+ self.granted_list.retain(|lib_| lib_.0 != lib);
+ } else {
+ self.granted_list.clear();
+ if self.global_state == PermissionState::Granted {
+ self.global_state = PermissionState::Prompt;
+ }
+ }
+ self.query(lib)
+ }
+
+ pub fn check(&mut self, lib: &str) -> Result<(), AnyError> {
+ let (result, prompted) = self.query(Some(lib)).check(
+ self.name,
+ Some(&format!("\"{}\"", lib)),
+ self.prompt,
+ );
+ if prompted {
+ if result.is_ok() {
+ self.granted_list.insert(FfiDescriptor(lib.to_string()));
+ } else {
+ self.denied_list.insert(FfiDescriptor(lib.to_string()));
+ self.global_state = PermissionState::Denied;
+ }
+ }
+ result
+ }
+
+ pub fn check_all(&mut self) -> Result<(), AnyError> {
+ let (result, prompted) =
+ self.query(None).check(self.name, Some("all"), self.prompt);
+ if prompted {
+ if result.is_ok() {
+ self.global_state = PermissionState::Granted;
+ } else {
+ self.global_state = PermissionState::Denied;
+ }
+ }
+ result
+ }
+}
+
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Permissions {
pub read: UnaryPermission<ReadDescriptor>,
@@ -794,7 +895,7 @@ pub struct Permissions {
pub net: UnaryPermission<NetDescriptor>,
pub env: UnaryPermission<EnvDescriptor>,
pub run: UnaryPermission<RunDescriptor>,
- pub plugin: UnitPermission,
+ pub ffi: UnaryPermission<FfiDescriptor>,
pub hrtime: UnitPermission,
}
@@ -803,7 +904,7 @@ pub struct PermissionsOptions {
pub allow_env: Option<Vec<String>>,
pub allow_hrtime: bool,
pub allow_net: Option<Vec<String>>,
- pub allow_plugin: bool,
+ pub allow_ffi: Option<Vec<String>>,
pub allow_read: Option<Vec<PathBuf>>,
pub allow_run: Option<Vec<String>>,
pub allow_write: Option<Vec<PathBuf>>,
@@ -904,8 +1005,21 @@ impl Permissions {
}
}
- pub fn new_plugin(state: bool, prompt: bool) -> UnitPermission {
- unit_permission_from_flag_bool(state, "plugin", "open a plugin", prompt)
+ pub fn new_ffi(
+ state: &Option<Vec<String>>,
+ prompt: bool,
+ ) -> UnaryPermission<FfiDescriptor> {
+ UnaryPermission::<FfiDescriptor> {
+ name: "ffi",
+ description: "load a dynamic library",
+ global_state: global_state_from_option(state),
+ granted_list: state
+ .as_ref()
+ .map(|v| v.iter().map(|x| FfiDescriptor(x.clone())).collect())
+ .unwrap_or_else(HashSet::new),
+ denied_list: Default::default(),
+ prompt,
+ }
}
pub fn new_hrtime(state: bool, prompt: bool) -> UnitPermission {
@@ -924,7 +1038,7 @@ impl Permissions {
net: Permissions::new_net(&opts.allow_net, opts.prompt),
env: Permissions::new_env(&opts.allow_env, opts.prompt),
run: Permissions::new_run(&opts.allow_run, opts.prompt),
- plugin: Permissions::new_plugin(opts.allow_plugin, opts.prompt),
+ ffi: Permissions::new_ffi(&opts.allow_ffi, opts.prompt),
hrtime: Permissions::new_hrtime(opts.allow_hrtime, opts.prompt),
}
}
@@ -936,7 +1050,7 @@ impl Permissions {
net: Permissions::new_net(&Some(vec![]), false),
env: Permissions::new_env(&Some(vec![]), false),
run: Permissions::new_run(&Some(vec![]), false),
- plugin: Permissions::new_plugin(true, false),
+ ffi: Permissions::new_ffi(&Some(vec![]), false),
hrtime: Permissions::new_hrtime(true, false),
}
}
@@ -1005,6 +1119,12 @@ impl deno_websocket::WebSocketPermissions for Permissions {
}
}
+impl deno_ffi::FfiPermissions for Permissions {
+ fn check(&mut self, path: &str) -> Result<(), AnyError> {
+ self.ffi.check(path)
+ }
+}
+
fn unit_permission_from_flag_bool(
flag: bool,
name: &'static str,
@@ -1457,9 +1577,9 @@ mod tests {
global_state: PermissionState::Prompt,
..Permissions::new_run(&Some(svec!["deno"]), false)
},
- plugin: UnitPermission {
- state: PermissionState::Prompt,
- ..Default::default()
+ ffi: UnaryPermission {
+ global_state: PermissionState::Prompt,
+ ..Permissions::new_ffi(&Some(svec!["deno"]), false)
},
hrtime: UnitPermission {
state: PermissionState::Prompt,
@@ -1490,8 +1610,10 @@ mod tests {
assert_eq!(perms1.run.query(Some(&"deno".to_string())), PermissionState::Granted);
assert_eq!(perms2.run.query(None), PermissionState::Prompt);
assert_eq!(perms2.run.query(Some(&"deno".to_string())), PermissionState::Granted);
- assert_eq!(perms1.plugin.query(), PermissionState::Granted);
- assert_eq!(perms2.plugin.query(), PermissionState::Prompt);
+ assert_eq!(perms1.ffi.query(None), PermissionState::Granted);
+ assert_eq!(perms1.ffi.query(Some(&"deno".to_string())), PermissionState::Granted);
+ assert_eq!(perms2.ffi.query(None), PermissionState::Prompt);
+ assert_eq!(perms2.ffi.query(Some(&"deno".to_string())), PermissionState::Granted);
assert_eq!(perms1.hrtime.query(), PermissionState::Granted);
assert_eq!(perms2.hrtime.query(), PermissionState::Prompt);
};
@@ -1528,9 +1650,10 @@ mod tests {
set_prompt_result(false);
assert_eq!(perms.run.request(Some(&"deno".to_string())), PermissionState::Granted);
set_prompt_result(true);
- assert_eq!(perms.plugin.request(), PermissionState::Granted);
+ assert_eq!(perms.ffi.request(Some(&"deno".to_string())), PermissionState::Granted);
+ assert_eq!(perms.ffi.query(None), PermissionState::Prompt);
set_prompt_result(false);
- assert_eq!(perms.plugin.request(), PermissionState::Granted);
+ assert_eq!(perms.ffi.request(Some(&"deno".to_string())), PermissionState::Granted);
set_prompt_result(false);
assert_eq!(perms.hrtime.request(), PermissionState::Denied);
set_prompt_result(true);
@@ -1561,9 +1684,9 @@ mod tests {
global_state: PermissionState::Prompt,
..Permissions::new_run(&Some(svec!["deno"]), false)
},
- plugin: UnitPermission {
- state: PermissionState::Prompt,
- ..Default::default()
+ ffi: UnaryPermission {
+ global_state: PermissionState::Prompt,
+ ..Permissions::new_ffi(&Some(svec!["deno"]), false)
},
hrtime: UnitPermission {
state: PermissionState::Denied,
@@ -1582,7 +1705,7 @@ mod tests {
assert_eq!(perms.net.revoke(Some(&("127.0.0.1", None))), PermissionState::Prompt);
assert_eq!(perms.env.revoke(Some(&"HOME".to_string())), PermissionState::Prompt);
assert_eq!(perms.run.revoke(Some(&"deno".to_string())), PermissionState::Prompt);
- assert_eq!(perms.plugin.revoke(), PermissionState::Prompt);
+ assert_eq!(perms.ffi.revoke(Some(&"deno".to_string())), PermissionState::Prompt);
assert_eq!(perms.hrtime.revoke(), PermissionState::Denied);
};
}
@@ -1595,7 +1718,7 @@ mod tests {
net: Permissions::new_net(&None, true),
env: Permissions::new_env(&None, true),
run: Permissions::new_run(&None, true),
- plugin: Permissions::new_plugin(false, true),
+ ffi: Permissions::new_ffi(&None, true),
hrtime: Permissions::new_hrtime(false, true),
};
@@ -1648,7 +1771,7 @@ mod tests {
net: Permissions::new_net(&None, true),
env: Permissions::new_env(&None, true),
run: Permissions::new_run(&None, true),
- plugin: Permissions::new_plugin(false, true),
+ ffi: Permissions::new_ffi(&None, true),
hrtime: Permissions::new_hrtime(false, true),
};
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 773fce80f..74e5fbafe 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -315,6 +315,8 @@ impl WebWorker {
deno_crypto::init(options.seed),
deno_webgpu::init(options.unstable),
deno_timers::init::<Permissions>(),
+ // ffi
+ deno_ffi::init::<Permissions>(options.unstable),
// Metrics
metrics::init(),
// Permissions ext (worker specific state)
@@ -340,7 +342,6 @@ impl WebWorker {
),
ops::os::init(),
ops::permissions::init(),
- ops::plugin::init(),
ops::process::init(),
ops::signal::init(),
ops::tty::init(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 94edd6f1e..c64ef2baf 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -115,6 +115,8 @@ impl MainWorker {
),
deno_webgpu::init(options.unstable),
deno_timers::init::<Permissions>(),
+ // ffi
+ deno_ffi::init::<Permissions>(options.unstable),
// Metrics
metrics::init(),
// Runtime ops
@@ -127,7 +129,6 @@ impl MainWorker {
deno_net::init::<Permissions>(options.ca_data.clone(), options.unstable),
ops::os::init(),
ops::permissions::init(),
- ops::plugin::init(),
ops::process::init(),
ops::signal::init(),
ops::tty::init(),
diff --git a/test_plugin/Cargo.toml b/test_ffi/Cargo.toml
index 53a94c473..737efe21e 100644
--- a/test_plugin/Cargo.toml
+++ b/test_ffi/Cargo.toml
@@ -1,8 +1,8 @@
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
[package]
-name = "test_plugin"
-version = "0.0.1"
+name = "test_ffi"
+version = "0.1.0"
authors = ["the deno authors"]
edition = "2018"
publish = false
@@ -10,10 +10,5 @@ publish = false
[lib]
crate-type = ["cdylib"]
-[dependencies]
-deno_core = { path = "../core" }
-futures = "0.3.15"
-serde = "1"
-
[dev-dependencies]
test_util = { path = "../test_util" }
diff --git a/test_ffi/README.md b/test_ffi/README.md
new file mode 100644
index 000000000..685385e4f
--- /dev/null
+++ b/test_ffi/README.md
@@ -0,0 +1 @@
+# `test_ffi` crate
diff --git a/test_ffi/src/lib.rs b/test_ffi/src/lib.rs
new file mode 100644
index 000000000..d9e950d4f
--- /dev/null
+++ b/test_ffi/src/lib.rs
@@ -0,0 +1,9 @@
+#[no_mangle]
+pub extern "C" fn print_something() {
+ println!("something");
+}
+
+#[no_mangle]
+pub extern "C" fn add(a: u32, b: u32) -> u32 {
+ a + b
+}
diff --git a/test_plugin/tests/integration_tests.rs b/test_ffi/tests/integration_tests.rs
index e408f59db..7aa8a0611 100644
--- a/test_plugin/tests/integration_tests.rs
+++ b/test_ffi/tests/integration_tests.rs
@@ -13,7 +13,7 @@ const BUILD_VARIANT: &str = "release";
fn basic() {
let mut build_plugin_base = Command::new("cargo");
let mut build_plugin =
- build_plugin_base.arg("build").arg("-p").arg("test_plugin");
+ build_plugin_base.arg("build").arg("-p").arg("test_ffi");
if BUILD_VARIANT == "release" {
build_plugin = build_plugin.arg("--release");
}
@@ -21,10 +21,11 @@ fn basic() {
assert!(build_plugin_output.status.success());
let output = deno_cmd()
.arg("run")
- .arg("--allow-plugin")
+ .arg("--allow-ffi")
+ .arg("--allow-read")
.arg("--unstable")
.arg("tests/test.js")
- .arg(BUILD_VARIANT)
+ .env("NO_COLOR", "1")
.output()
.unwrap();
let stdout = std::str::from_utf8(&output.stdout).unwrap();
@@ -36,23 +37,9 @@ fn basic() {
println!("{:?}", output.status);
assert!(output.status.success());
let expected = "\
- Plugin rid: 3\n\
- Hello from sync plugin op.\n\
- args: TestArgs { val: \"1\" }\n\
- zero_copy: test\n\
- op_test_sync returned: test\n\
- Hello from async plugin op.\n\
- args: TestArgs { val: \"1\" }\n\
- zero_copy: 123\n\
- op_test_async returned: test\n\
- Hello from resource_table.add plugin op.\n\
- TestResource rid: 4\n\
- Hello from resource_table.get plugin op.\n\
- TestResource get value: hello plugin!\n\
- Hello from sync plugin op.\n\
- args: TestArgs { val: \"1\" }\n\
- Ops completed count is correct!\n\
- Ops dispatched count is correct!\n";
+ something\n\
+ 579\n\
+ Correct number of resources\n";
assert_eq!(stdout, expected);
assert_eq!(stderr, "");
}
diff --git a/test_ffi/tests/test.js b/test_ffi/tests/test.js
new file mode 100644
index 000000000..a7b0aba6d
--- /dev/null
+++ b/test_ffi/tests/test.js
@@ -0,0 +1,33 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+// deno-lint-ignore-file
+
+const targetDir = Deno.execPath().replace(/[^\/\\]+$/, "");
+const [libPrefix, libSuffix] = {
+ darwin: ["lib", "dylib"],
+ linux: ["lib", "so"],
+ windows: ["", "dll"],
+}[Deno.build.os];
+const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`;
+
+const resourcesPre = Deno.resources();
+const dylib = Deno.dlopen(libPath, {
+ "print_something": { parameters: [], result: "void" },
+ "add": { parameters: ["u32", "u32"], result: "u32" },
+});
+
+dylib.symbols.print_something();
+console.log(dylib.symbols.add(123, 456));
+
+dylib.close();
+const resourcesPost = Deno.resources();
+
+const preStr = JSON.stringify(resourcesPre, null, 2);
+const postStr = JSON.stringify(resourcesPost, null, 2);
+if (preStr !== postStr) {
+ throw new Error(
+ `Difference in open resources before dlopen and after closing:
+Before: ${preStr}
+After: ${postStr}`,
+ );
+}
+console.log("Correct number of resources");
diff --git a/test_plugin/README.md b/test_plugin/README.md
deleted file mode 100644
index b340389ce..000000000
--- a/test_plugin/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# `test_plugin` crate
-
-## To run this test manually
-
-```
-cd test_plugin
-
-../target/debug/deno run --unstable --allow-plugin tests/test.js debug
-```
diff --git a/test_plugin/src/lib.rs b/test_plugin/src/lib.rs
deleted file mode 100644
index 88761edcf..000000000
--- a/test_plugin/src/lib.rs
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-
-use std::borrow::Cow;
-use std::cell::RefCell;
-use std::rc::Rc;
-
-use deno_core::error::bad_resource_id;
-use deno_core::error::AnyError;
-use deno_core::op_async;
-use deno_core::op_sync;
-use deno_core::Extension;
-use deno_core::OpState;
-use deno_core::Resource;
-use deno_core::ResourceId;
-use deno_core::ZeroCopyBuf;
-use serde::Deserialize;
-
-#[no_mangle]
-pub fn init() -> Extension {
- Extension::builder()
- .ops(vec![
- ("op_test_sync", op_sync(op_test_sync)),
- ("op_test_async", op_async(op_test_async)),
- (
- "op_test_resource_table_add",
- op_sync(op_test_resource_table_add),
- ),
- (
- "op_test_resource_table_get",
- op_sync(op_test_resource_table_get),
- ),
- ])
- .build()
-}
-
-#[derive(Debug, Deserialize)]
-struct TestArgs {
- val: String,
-}
-
-fn op_test_sync(
- _state: &mut OpState,
- args: TestArgs,
- zero_copy: Option<ZeroCopyBuf>,
-) -> Result<String, AnyError> {
- println!("Hello from sync plugin op.");
-
- println!("args: {:?}", args);
-
- if let Some(buf) = zero_copy {
- let buf_str = std::str::from_utf8(&buf[..])?;
- println!("zero_copy: {}", buf_str);
- }
-
- Ok("test".to_string())
-}
-
-async fn op_test_async(
- _state: Rc<RefCell<OpState>>,
- args: TestArgs,
- zero_copy: Option<ZeroCopyBuf>,
-) -> Result<String, AnyError> {
- println!("Hello from async plugin op.");
-
- println!("args: {:?}", args);
-
- if let Some(buf) = zero_copy {
- let buf_str = std::str::from_utf8(&buf[..])?;
- println!("zero_copy: {}", 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());
-
- Ok("test".to_string())
-}
-
-struct TestResource(String);
-impl Resource for TestResource {
- fn name(&self) -> Cow<str> {
- "TestResource".into()
- }
-}
-
-fn op_test_resource_table_add(
- state: &mut OpState,
- text: String,
- _: (),
-) -> Result<u32, AnyError> {
- println!("Hello from resource_table.add plugin op.");
-
- Ok(state.resource_table.add(TestResource(text)))
-}
-
-fn op_test_resource_table_get(
- state: &mut OpState,
- rid: ResourceId,
- _: (),
-) -> Result<String, AnyError> {
- println!("Hello from resource_table.get plugin op.");
-
- Ok(
- state
- .resource_table
- .get::<TestResource>(rid)
- .ok_or_else(bad_resource_id)?
- .0
- .clone(),
- )
-}
diff --git a/test_plugin/tests/test.js b/test_plugin/tests/test.js
deleted file mode 100644
index 2a2fa66b3..000000000
--- a/test_plugin/tests/test.js
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-// deno-lint-ignore-file
-
-const filenameBase = "test_plugin";
-
-let filenameSuffix = ".so";
-let filenamePrefix = "lib";
-
-if (Deno.build.os === "windows") {
- filenameSuffix = ".dll";
- filenamePrefix = "";
-} else if (Deno.build.os === "darwin") {
- filenameSuffix = ".dylib";
-}
-
-const filename = `../target/${
- Deno.args[0]
-}/${filenamePrefix}${filenameBase}${filenameSuffix}`;
-
-const resourcesPre = Deno.resources();
-
-const pluginRid = Deno.openPlugin(filename);
-console.log(`Plugin rid: ${pluginRid}`);
-
-const {
- op_test_sync,
- op_test_async,
- op_test_resource_table_add,
- op_test_resource_table_get,
-} = Deno.core.ops();
-
-if (
- op_test_sync === null ||
- op_test_async === null ||
- op_test_resource_table_add === null ||
- op_test_resource_table_get === null
-) {
- throw new Error("Not all expected ops were registered");
-}
-
-function runTestSync() {
- const result = Deno.core.opSync(
- "op_test_sync",
- { val: "1" },
- new Uint8Array([116, 101, 115, 116]),
- );
-
- console.log(`op_test_sync returned: ${result}`);
-
- if (result !== "test") {
- throw new Error("op_test_sync returned an unexpected value!");
- }
-}
-
-async function runTestAsync() {
- const promise = Deno.core.opAsync(
- "op_test_async",
- { val: "1" },
- new Uint8Array([49, 50, 51]),
- );
-
- if (!(promise instanceof Promise)) {
- throw new Error("Expected promise!");
- }
-
- const result = await promise;
- console.log(`op_test_async returned: ${result}`);
-
- if (result !== "test") {
- throw new Error("op_test_async promise resolved to an unexpected value!");
- }
-}
-
-function runTestResourceTable() {
- const expect = "hello plugin!";
-
- const testRid = Deno.core.opSync("op_test_resource_table_add", expect);
- console.log(`TestResource rid: ${testRid}`);
-
- if (testRid === null || Deno.resources()[testRid] !== "TestResource") {
- throw new Error("TestResource was not found!");
- }
-
- const testValue = Deno.core.opSync("op_test_resource_table_get", testRid);
- console.log(`TestResource get value: ${testValue}`);
-
- if (testValue !== expect) {
- throw new Error("Did not get correct resource value!");
- }
-
- Deno.close(testRid);
-}
-
-function runTestOpCount() {
- const start = Deno.metrics();
-
- Deno.core.opSync("op_test_sync", { val: "1" });
-
- const end = Deno.metrics();
-
- if (end.opsCompleted - start.opsCompleted !== 1) {
- throw new Error("The opsCompleted metric is not correct!");
- }
- console.log("Ops completed count is correct!");
-
- if (end.opsDispatched - start.opsDispatched !== 1) {
- throw new Error("The opsDispatched metric is not correct!");
- }
- console.log("Ops dispatched count is correct!");
-}
-
-function runTestPluginClose() {
- // Closing does not yet work
- Deno.close(pluginRid);
-
- const resourcesPost = Deno.resources();
-
- const preStr = JSON.stringify(resourcesPre, null, 2);
- const postStr = JSON.stringify(resourcesPost, null, 2);
- if (preStr !== postStr) {
- throw new Error(
- `Difference in open resources before openPlugin and after Plugin.close():
-Before: ${preStr}
-After: ${postStr}`,
- );
- }
- console.log("Correct number of resources");
-}
-
-runTestSync();
-await runTestAsync();
-runTestResourceTable();
-
-runTestOpCount();
-// runTestPluginClose();