Skip to content

Commit

Permalink
Remove core/plugin.rs (denoland#4824)
Browse files Browse the repository at this point in the history
This simplifies the plugin interface in order to deliver op crates with a similar API
  • Loading branch information
ry committed Apr 20, 2020
1 parent 437e35c commit 6e5f345
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 190 deletions.
2 changes: 1 addition & 1 deletion cli/js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export {
PermissionStatus,
Permissions,
} from "./permissions.ts";
export { openPlugin } from "./plugins.ts";
export { openPlugin } from "./ops/plugins.ts";
export { kill } from "./ops/process.ts";
export { run, RunOptions, Process, ProcessStatus } from "./process.ts";
export { DirEntry, readdirSync, readdir } from "./ops/fs/read_dir.ts";
Expand Down
34 changes: 11 additions & 23 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1796,35 +1796,23 @@ declare namespace Deno {
* Requires `allow-write` permission. */
export function truncate(name: string, len?: number): Promise<void>;

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;
};
}

/** **UNSTABLE**: new API, yet to be vetted.
*
* Open and initalize a plugin.
*
* 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]));
* const rid = Deno.openPlugin("./path/to/some/plugin.so");
* const opId = Deno.core.ops()["some_op"];
* const response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4]));
* console.log(`Response from plugin ${response}`);
*
* Requires `allow-plugin` permission. */
export function openPlugin(filename: string): Plugin;
* Requires `allow-plugin` permission.
*
* 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/master/test_plugin
*/
export function openPlugin(filename: string): number;

export interface NetAddr {
transport: "tcp" | "udp";
hostname: string;
Expand Down
12 changes: 3 additions & 9 deletions cli/js/ops/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { sendSync } from "./dispatch_json.ts";

interface OpenPluginResponse {
rid: number;
ops: {
[name: string]: number;
};
}

export function openPlugin(filename: string): OpenPluginResponse {
return sendSync("op_open_plugin", { filename });
export function openPlugin(filename: string): number {
const rid = sendSync("op_open_plugin", { filename });
return rid;
}
64 changes: 0 additions & 64 deletions cli/js/plugins.ts

This file was deleted.

54 changes: 9 additions & 45 deletions cli/ops/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ use crate::op_error::OpError;
use crate::ops::json_op;
use crate::state::State;
use deno_core::Isolate;
use deno_core::OpDispatcher;
use deno_core::OpId;
use deno_core::PluginInitContext;
use deno_core::PluginInitFn;
use deno_core::ZeroCopyBuf;
use dlopen::symbor::Library;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::path::Path;

pub type PluginInitFn = fn(isolate: &mut deno_core::Isolate);

pub fn init(i: &mut Isolate, s: &State) {
i.register_op(
"op_open_plugin",
Expand All @@ -23,27 +20,11 @@ pub fn init(i: &mut Isolate, s: &State) {

fn open_plugin<P: AsRef<OsStr>>(lib_path: P) -> Result<Library, OpError> {
debug!("Loading Plugin: {:#?}", lib_path.as_ref());

Library::open(lib_path).map_err(OpError::from)
}

struct PluginResource {
lib: Library,
ops: HashMap<String, OpId>,
}

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)]
Expand All @@ -58,16 +39,13 @@ pub fn op_open_plugin(
args: Value,
_zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, OpError> {
let args: OpenPluginArgs = serde_json::from_value(args)?;
let args: OpenPluginArgs = serde_json::from_value(args).unwrap();
let filename = deno_fs::resolve_from_cwd(Path::new(&args.filename))?;

state.check_plugin(&filename)?;

let lib = open_plugin(filename)?;
let plugin_resource = PluginResource {
lib,
ops: HashMap::new(),
};
let lib = open_plugin(filename).unwrap();
let plugin_resource = PluginResource { lib };
let mut state_ = state.borrow_mut();
let rid = state_
.resource_table
Expand All @@ -77,27 +55,13 @@ pub fn op_open_plugin(
.get_mut::<PluginResource>(rid)
.unwrap();

let init_fn = *unsafe {
let deno_plugin_init = *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 = isolate
.register_op(&format!("plugin_{}_{}", rid, op.0), state.core_op(op.1));
plugin_resource.ops.insert(op.0, op_id);
}
.unwrap();
deno_plugin_init(isolate);

Ok(JsonOp::Sync(
json!({ "rid": rid, "ops": plugin_resource.ops }),
))
Ok(JsonOp::Sync(json!(rid)))
}
2 changes: 1 addition & 1 deletion cli/ops/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn op_close(
struct CloseArgs {
rid: i32,
}
let args: CloseArgs = serde_json::from_value(args)?;
let args: CloseArgs = serde_json::from_value(args).unwrap();
let mut state = state.borrow_mut();
state
.resource_table
Expand Down
2 changes: 0 additions & 2 deletions core/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ mod js_errors;
mod module_specifier;
mod modules;
mod ops;
mod plugins;
mod resources;
mod shared_queue;

Expand All @@ -31,7 +30,6 @@ pub use crate::js_errors::*;
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 {
Expand Down
24 changes: 0 additions & 24 deletions core/plugins.rs

This file was deleted.

13 changes: 6 additions & 7 deletions test_plugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#[macro_use]
extern crate deno_core;
extern crate futures;

use deno_core::Buf;
use deno_core::Op;
use deno_core::PluginInitContext;
use deno_core::{Buf, ZeroCopyBuf};
use deno_core::ZeroCopyBuf;
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));
#[no_mangle]
pub fn deno_plugin_init(isolate: &mut deno_core::Isolate) {
isolate.register_op("testSync", op_test_sync);
isolate.register_op("testAsync", op_test_async);
}
init_fn!(init);

pub fn op_test_sync(
_isolate: &mut deno_core::Isolate,
Expand Down
10 changes: 5 additions & 5 deletions test_plugin/tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// To run this test manually:
// cd test_plugin
// ../target/debug/deno --allow-plugin tests/test.js debug

// TODO(ry) Re-enable this test on windows. It is flaky for an unknown reason.
#![cfg(not(windows))]

Expand Down Expand Up @@ -38,11 +42,7 @@ fn basic() {
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"
};
let expected = "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, "");
}
26 changes: 17 additions & 9 deletions test_plugin/tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,35 @@ const filename = `../target/${Deno.args[0]}/${filenamePrefix}${filenameBase}${fi
// in runTestClose() below.
const resourcesPre = Deno.resources();

const plugin = Deno.openPlugin(filename);
const rid = Deno.openPlugin(filename);

const { testSync, testAsync } = plugin.ops;
const { testSync, testAsync } = Deno.core.ops();
if (!(testSync > 0)) {
throw "bad op id for testSync";
}
if (!(testAsync > 0)) {
throw "bad op id for testAsync";
}

const textDecoder = new TextDecoder();

function runTestSync() {
const response = testSync.dispatch(
const response = Deno.core.dispatch(
testSync,
new Uint8Array([116, 101, 115, 116]),
new Uint8Array([116, 101, 115, 116])
);

console.log(`Plugin Sync Response: ${textDecoder.decode(response)}`);
}

testAsync.setAsyncHandler((response) => {
Deno.core.setAsyncHandler(testAsync, (response) => {
console.log(`Plugin Async Response: ${textDecoder.decode(response)}`);
});

function runTestAsync() {
const response = testAsync.dispatch(
const response = Deno.core.dispatch(
testAsync,
new Uint8Array([116, 101, 115, 116]),
new Uint8Array([116, 101, 115, 116])
);
Expand All @@ -50,22 +58,22 @@ function runTestAsync() {
function runTestOpCount() {
const start = Deno.metrics();

testSync.dispatch(new Uint8Array([116, 101, 115, 116]));
Deno.core.dispatch(testSync, new Uint8Array([116, 101, 115, 116]));

const end = Deno.metrics();

if (end.opsCompleted - start.opsCompleted !== 2) {
if (end.opsCompleted - start.opsCompleted !== 1) {
// one op for the plugin and one for Deno.metrics
throw new Error("The opsCompleted metric is not correct!");
}
if (end.opsDispatched - start.opsDispatched !== 2) {
if (end.opsDispatched - start.opsDispatched !== 1) {
// one op for the plugin and one for Deno.metrics
throw new Error("The opsDispatched metric is not correct!");
}
}

function runTestPluginClose() {
plugin.close();
Deno.close(rid);

const resourcesPost = Deno.resources();

Expand Down

0 comments on commit 6e5f345

Please sign in to comment.