Skip to content

Commit

Permalink
Revert "Remove unstable native plugins (#10908)"
Browse files Browse the repository at this point in the history
This reverts commit 7dd4090.
  • Loading branch information
ry authored Jul 12, 2021
1 parent eea6000 commit 511c48a
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 4 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"cli",
"core",
"runtime",
"test_plugin",
"test_util",
"extensions/broadcast_channel",
"extensions/console",
Expand Down
31 changes: 31 additions & 0 deletions cli/dts/lib.deno.unstable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,37 @@ declare namespace Deno {
speed: number | undefined;
}

/** **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.
*
* 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
*/
export function openPlugin(filename: string): number;

/** The log category for a diagnostic message. */
export enum DiagnosticCategory {
Warning = 0,
Expand Down
8 changes: 4 additions & 4 deletions cli/tests/integration/lsp_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ fn lsp_hover_unstable_enabled() {
"uri": "file:///a/file.ts",
"languageId": "typescript",
"version": 1,
"text": "console.log(Deno.ppid);\n"
"text": "console.log(Deno.openPlugin);\n"
}
}),
);
Expand All @@ -495,9 +495,9 @@ fn lsp_hover_unstable_enabled() {
"contents":[
{
"language":"typescript",
"value":"const Deno.ppid: number"
"value":"function Deno.openPlugin(filename: string): number"
},
"The pid of the current process's parent."
"**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"
],
"range":{
"start":{
Expand All @@ -506,7 +506,7 @@ fn lsp_hover_unstable_enabled() {
},
"end":{
"line":0,
"character":21
"character":27
}
}
}))
Expand Down
16 changes: 16 additions & 0 deletions runtime/js/40_plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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);
1 change: 1 addition & 0 deletions runtime/js/90_deno_ns.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
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,
Expand Down
1 change: 1 addition & 0 deletions runtime/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod fs_events;
pub mod io;
pub mod os;
pub mod permissions;
pub mod plugin;
pub mod process;
pub mod runtime;
pub mod signal;
Expand Down
86 changes: 86 additions & 0 deletions runtime/ops/plugin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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())
}
}
1 change: 1 addition & 0 deletions runtime/web_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ impl WebWorker {
deno_net::init::<Permissions>(options.unstable),
ops::os::init(),
ops::permissions::init(),
ops::plugin::init(),
ops::process::init(),
ops::signal::init(),
ops::tty::init(),
Expand Down
1 change: 1 addition & 0 deletions runtime/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl MainWorker {
deno_net::init::<Permissions>(options.unstable),
ops::os::init(),
ops::permissions::init(),
ops::plugin::init(),
ops::process::init(),
ops::signal::init(),
ops::tty::init(),
Expand Down
19 changes: 19 additions & 0 deletions test_plugin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

[package]
name = "test_plugin"
version = "0.0.1"
authors = ["the deno authors"]
edition = "2018"
publish = false

[lib]
crate-type = ["cdylib"]

[dependencies]
deno_core = { path = "../core" }
futures = "0.3.15"
serde = "1"

[dev-dependencies]
test_util = { path = "../test_util" }
9 changes: 9 additions & 0 deletions test_plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `test_plugin` crate

## To run this test manually

```
cd test_plugin
../target/debug/deno run --unstable --allow-plugin tests/test.js debug
```
114 changes: 114 additions & 0 deletions test_plugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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(),
)
}
Loading

0 comments on commit 511c48a

Please sign in to comment.