Skip to content

Commit b350c03

Browse files
committed
Revert "Remove unstable native plugins (denoland#10908)"
This reverts commit 7dd4090.
1 parent eea6000 commit b350c03

15 files changed

+487
-4
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"cli",
77
"core",
88
"runtime",
9+
"test_plugin",
910
"test_util",
1011
"extensions/broadcast_channel",
1112
"extensions/console",

cli/dts/lib.deno.unstable.d.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,37 @@ declare namespace Deno {
129129
speed: number | undefined;
130130
}
131131

132+
/** **UNSTABLE**: new API, yet to be vetted.
133+
*
134+
* Open and initialize a plugin.
135+
*
136+
* ```ts
137+
* import { assert } from "https://deno.land/std/testing/asserts.ts";
138+
* const rid = Deno.openPlugin("./path/to/some/plugin.so");
139+
*
140+
* // The Deno.core namespace is needed to interact with plugins, but this is
141+
* // internal so we use ts-ignore to skip type checking these calls.
142+
* // @ts-ignore
143+
* const { op_test_sync, op_test_async } = Deno.core.ops();
144+
*
145+
* assert(op_test_sync);
146+
* assert(op_test_async);
147+
*
148+
* // @ts-ignore
149+
* const result = Deno.core.opSync("op_test_sync");
150+
*
151+
* // @ts-ignore
152+
* const result = await Deno.core.opAsync("op_test_sync");
153+
* ```
154+
*
155+
* Requires `allow-plugin` permission.
156+
*
157+
* The plugin system is not stable and will change in the future, hence the
158+
* lack of docs. For now take a look at the example
159+
* https://github.com/denoland/deno/tree/main/test_plugin
160+
*/
161+
export function openPlugin(filename: string): number;
162+
132163
/** The log category for a diagnostic message. */
133164
export enum DiagnosticCategory {
134165
Warning = 0,

cli/tests/integration/lsp_tests.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ fn lsp_hover_unstable_enabled() {
470470
"uri": "file:///a/file.ts",
471471
"languageId": "typescript",
472472
"version": 1,
473-
"text": "console.log(Deno.ppid);\n"
473+
"text": "console.log(Deno.openPlugin);\n"
474474
}
475475
}),
476476
);
@@ -495,9 +495,9 @@ fn lsp_hover_unstable_enabled() {
495495
"contents":[
496496
{
497497
"language":"typescript",
498-
"value":"const Deno.ppid: number"
498+
"value":"function Deno.openPlugin(filename: string): number"
499499
},
500-
"The pid of the current process's parent."
500+
"**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"
501501
],
502502
"range":{
503503
"start":{
@@ -506,7 +506,7 @@ fn lsp_hover_unstable_enabled() {
506506
},
507507
"end":{
508508
"line":0,
509-
"character":21
509+
"character":27
510510
}
511511
}
512512
}))

runtime/js/40_plugins.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
"use strict";
3+
4+
((window) => {
5+
const core = window.Deno.core;
6+
7+
function openPlugin(filename) {
8+
const rid = core.opSync("op_open_plugin", filename);
9+
core.syncOpsCache();
10+
return rid;
11+
}
12+
13+
window.__bootstrap.plugins = {
14+
openPlugin,
15+
};
16+
})(this);

runtime/js/90_deno_ns.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
Signal: __bootstrap.signals.Signal,
110110
SignalStream: __bootstrap.signals.SignalStream,
111111
emit: __bootstrap.compilerApi.emit,
112+
openPlugin: __bootstrap.plugins.openPlugin,
112113
kill: __bootstrap.process.kill,
113114
setRaw: __bootstrap.tty.setRaw,
114115
consoleSize: __bootstrap.tty.consoleSize,

runtime/ops/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod fs_events;
55
pub mod io;
66
pub mod os;
77
pub mod permissions;
8+
pub mod plugin;
89
pub mod process;
910
pub mod runtime;
1011
pub mod signal;

runtime/ops/plugin.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
use crate::permissions::Permissions;
3+
use deno_core::error::AnyError;
4+
use deno_core::op_sync;
5+
use deno_core::Extension;
6+
use deno_core::OpState;
7+
use deno_core::Resource;
8+
use deno_core::ResourceId;
9+
use dlopen::symbor::Library;
10+
use log::debug;
11+
use std::borrow::Cow;
12+
use std::mem;
13+
use std::path::PathBuf;
14+
use std::rc::Rc;
15+
16+
/// A default `init` function for plugins which mimics the way the internal
17+
/// extensions are initalized. Plugins currently do not support all extension
18+
/// features and are most likely not going to in the future. Currently only
19+
/// `init_state` and `init_ops` are supported while `init_middleware` and `init_js`
20+
/// are not. Currently the `PluginResource` does not support being closed due to
21+
/// certain risks in unloading the dynamic library without unloading dependent
22+
/// functions and resources.
23+
pub type InitFn = fn() -> Extension;
24+
25+
pub fn init() -> Extension {
26+
Extension::builder()
27+
.ops(vec![("op_open_plugin", op_sync(op_open_plugin))])
28+
.build()
29+
}
30+
31+
pub fn op_open_plugin(
32+
state: &mut OpState,
33+
filename: String,
34+
_: (),
35+
) -> Result<ResourceId, AnyError> {
36+
let filename = PathBuf::from(&filename);
37+
38+
super::check_unstable(state, "Deno.openPlugin");
39+
let permissions = state.borrow_mut::<Permissions>();
40+
permissions.plugin.check()?;
41+
42+
debug!("Loading Plugin: {:#?}", filename);
43+
let plugin_lib = Library::open(filename).map(Rc::new)?;
44+
let plugin_resource = PluginResource::new(&plugin_lib);
45+
46+
// Forgets the plugin_lib value to prevent segfaults when the process exits
47+
mem::forget(plugin_lib);
48+
49+
let init = *unsafe { plugin_resource.0.symbol::<InitFn>("init") }?;
50+
let rid = state.resource_table.add(plugin_resource);
51+
let mut extension = init();
52+
53+
if !extension.init_js().is_empty() {
54+
panic!("Plugins do not support loading js");
55+
}
56+
57+
if extension.init_middleware().is_some() {
58+
panic!("Plugins do not support middleware");
59+
}
60+
61+
extension.init_state(state)?;
62+
let ops = extension.init_ops().unwrap_or_default();
63+
for (name, opfn) in ops {
64+
state.op_table.register_op(name, opfn);
65+
}
66+
67+
Ok(rid)
68+
}
69+
70+
struct PluginResource(Rc<Library>);
71+
72+
impl Resource for PluginResource {
73+
fn name(&self) -> Cow<str> {
74+
"plugin".into()
75+
}
76+
77+
fn close(self: Rc<Self>) {
78+
unimplemented!();
79+
}
80+
}
81+
82+
impl PluginResource {
83+
fn new(lib: &Rc<Library>) -> Self {
84+
Self(lib.clone())
85+
}
86+
}

runtime/web_worker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ impl WebWorker {
335335
deno_net::init::<Permissions>(options.unstable),
336336
ops::os::init(),
337337
ops::permissions::init(),
338+
ops::plugin::init(),
338339
ops::process::init(),
339340
ops::signal::init(),
340341
ops::tty::init(),

runtime/worker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl MainWorker {
126126
deno_net::init::<Permissions>(options.unstable),
127127
ops::os::init(),
128128
ops::permissions::init(),
129+
ops::plugin::init(),
129130
ops::process::init(),
130131
ops::signal::init(),
131132
ops::tty::init(),

test_plugin/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
3+
[package]
4+
name = "test_plugin"
5+
version = "0.0.1"
6+
authors = ["the deno authors"]
7+
edition = "2018"
8+
publish = false
9+
10+
[lib]
11+
crate-type = ["cdylib"]
12+
13+
[dependencies]
14+
deno_core = { path = "../core" }
15+
futures = "0.3.15"
16+
serde = "1"
17+
18+
[dev-dependencies]
19+
test_util = { path = "../test_util" }

test_plugin/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# `test_plugin` crate
2+
3+
## To run this test manually
4+
5+
```
6+
cd test_plugin
7+
8+
../target/debug/deno run --unstable --allow-plugin tests/test.js debug
9+
```

test_plugin/src/lib.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
2+
3+
use std::borrow::Cow;
4+
use std::cell::RefCell;
5+
use std::rc::Rc;
6+
7+
use deno_core::error::bad_resource_id;
8+
use deno_core::error::AnyError;
9+
use deno_core::op_async;
10+
use deno_core::op_sync;
11+
use deno_core::Extension;
12+
use deno_core::OpState;
13+
use deno_core::Resource;
14+
use deno_core::ResourceId;
15+
use deno_core::ZeroCopyBuf;
16+
use serde::Deserialize;
17+
18+
#[no_mangle]
19+
pub fn init() -> Extension {
20+
Extension::builder()
21+
.ops(vec![
22+
("op_test_sync", op_sync(op_test_sync)),
23+
("op_test_async", op_async(op_test_async)),
24+
(
25+
"op_test_resource_table_add",
26+
op_sync(op_test_resource_table_add),
27+
),
28+
(
29+
"op_test_resource_table_get",
30+
op_sync(op_test_resource_table_get),
31+
),
32+
])
33+
.build()
34+
}
35+
36+
#[derive(Debug, Deserialize)]
37+
struct TestArgs {
38+
val: String,
39+
}
40+
41+
fn op_test_sync(
42+
_state: &mut OpState,
43+
args: TestArgs,
44+
zero_copy: Option<ZeroCopyBuf>,
45+
) -> Result<String, AnyError> {
46+
println!("Hello from sync plugin op.");
47+
48+
println!("args: {:?}", args);
49+
50+
if let Some(buf) = zero_copy {
51+
let buf_str = std::str::from_utf8(&buf[..])?;
52+
println!("zero_copy: {}", buf_str);
53+
}
54+
55+
Ok("test".to_string())
56+
}
57+
58+
async fn op_test_async(
59+
_state: Rc<RefCell<OpState>>,
60+
args: TestArgs,
61+
zero_copy: Option<ZeroCopyBuf>,
62+
) -> Result<String, AnyError> {
63+
println!("Hello from async plugin op.");
64+
65+
println!("args: {:?}", args);
66+
67+
if let Some(buf) = zero_copy {
68+
let buf_str = std::str::from_utf8(&buf[..])?;
69+
println!("zero_copy: {}", buf_str);
70+
}
71+
72+
let (tx, rx) = futures::channel::oneshot::channel::<Result<(), ()>>();
73+
std::thread::spawn(move || {
74+
std::thread::sleep(std::time::Duration::from_secs(1));
75+
tx.send(Ok(())).unwrap();
76+
});
77+
assert!(rx.await.is_ok());
78+
79+
Ok("test".to_string())
80+
}
81+
82+
struct TestResource(String);
83+
impl Resource for TestResource {
84+
fn name(&self) -> Cow<str> {
85+
"TestResource".into()
86+
}
87+
}
88+
89+
fn op_test_resource_table_add(
90+
state: &mut OpState,
91+
text: String,
92+
_: (),
93+
) -> Result<u32, AnyError> {
94+
println!("Hello from resource_table.add plugin op.");
95+
96+
Ok(state.resource_table.add(TestResource(text)))
97+
}
98+
99+
fn op_test_resource_table_get(
100+
state: &mut OpState,
101+
rid: ResourceId,
102+
_: (),
103+
) -> Result<String, AnyError> {
104+
println!("Hello from resource_table.get plugin op.");
105+
106+
Ok(
107+
state
108+
.resource_table
109+
.get::<TestResource>(rid)
110+
.ok_or_else(bad_resource_id)?
111+
.0
112+
.clone(),
113+
)
114+
}

0 commit comments

Comments
 (0)