summaryrefslogtreecommitdiff
path: root/cli/ops/plugin.rs
blob: fc3d5201dbf5f0af80bfda668fc5fe73e5b4ffef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.

use crate::metrics::metrics_op;
use deno_core::plugin_api;
use deno_core::BufVec;
use deno_core::ErrBox;
use deno_core::JsRuntime;
use deno_core::Op;
use deno_core::OpAsyncFuture;
use deno_core::OpId;
use deno_core::OpState;
use deno_core::ZeroCopyBuf;
use dlopen::symbor::Library;
use futures::prelude::*;
use serde_derive::Deserialize;
use serde_json::Value;
use std::cell::RefCell;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use std::task::Context;
use std::task::Poll;

pub fn init(rt: &mut JsRuntime) {
  super::reg_json_sync(rt, "op_open_plugin", op_open_plugin);
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct OpenPluginArgs {
  filename: String,
}

pub fn op_open_plugin(
  state: &mut OpState,
  args: Value,
  _zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, ErrBox> {
  let args: OpenPluginArgs = serde_json::from_value(args)?;
  let filename = PathBuf::from(&args.filename);

  let cli_state = super::cli_state(state);
  cli_state.check_unstable("Deno.openPlugin");
  cli_state.check_plugin(&filename)?;

  debug!("Loading Plugin: {:#?}", filename);
  let plugin_lib = Library::open(filename).map(Rc::new)?;
  let plugin_resource = PluginResource::new(&plugin_lib);

  let rid;
  let deno_plugin_init;
  {
    rid = state
      .resource_table
      .add("plugin", Box::new(plugin_resource));
    deno_plugin_init = *unsafe {
      state
        .resource_table
        .get::<PluginResource>(rid)
        .unwrap()
        .lib
        .symbol::<plugin_api::InitFn>("deno_plugin_init")
        .unwrap()
    };
  }

  let mut interface = PluginInterface::new(state, &plugin_lib);
  deno_plugin_init(&mut interface);

  Ok(json!(rid))
}

struct PluginResource {
  lib: Rc<Library>,
}

impl PluginResource {
  fn new(lib: &Rc<Library>) -> Self {
    Self { lib: lib.clone() }
  }
}

struct PluginInterface<'a> {
  state: &'a mut OpState,
  plugin_lib: &'a Rc<Library>,
}

impl<'a> PluginInterface<'a> {
  fn new(state: &'a mut OpState, plugin_lib: &'a Rc<Library>) -> Self {
    Self { state, plugin_lib }
  }
}

impl<'a> plugin_api::Interface for PluginInterface<'a> {
  /// Does the same as `core::Isolate::register_op()`, but additionally makes
  /// the registered op dispatcher, as well as the op futures created by it,
  /// keep reference to the plugin `Library` object, so that the plugin doesn't
  /// get unloaded before all its op registrations and the futures created by
  /// them are dropped.
  fn register_op(
    &mut self,
    name: &str,
    dispatch_op_fn: plugin_api::DispatchOpFn,
  ) -> OpId {
    let plugin_lib = self.plugin_lib.clone();
    let plugin_op_fn = move |state_rc: Rc<RefCell<OpState>>,
                             mut zero_copy: BufVec| {
      let mut state = state_rc.borrow_mut();
      let mut interface = PluginInterface::new(&mut state, &plugin_lib);
      let op = dispatch_op_fn(&mut interface, &mut zero_copy);
      match op {
        sync_op @ Op::Sync(..) => sync_op,
        Op::Async(fut) => Op::Async(PluginOpAsyncFuture::new(&plugin_lib, fut)),
        Op::AsyncUnref(fut) => {
          Op::AsyncUnref(PluginOpAsyncFuture::new(&plugin_lib, fut))
        }
        _ => unreachable!(),
      }
    };
    self
      .state
      .op_table
      .register_op(name, metrics_op(Box::new(plugin_op_fn)))
  }
}

struct PluginOpAsyncFuture {
  fut: Option<OpAsyncFuture>,
  _plugin_lib: Rc<Library>,
}

impl PluginOpAsyncFuture {
  fn new(plugin_lib: &Rc<Library>, fut: OpAsyncFuture) -> Pin<Box<Self>> {
    let wrapped_fut = Self {
      fut: Some(fut),
      _plugin_lib: plugin_lib.clone(),
    };
    Box::pin(wrapped_fut)
  }
}

impl Future for PluginOpAsyncFuture {
  type Output = <OpAsyncFuture as Future>::Output;
  fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
    self.fut.as_mut().unwrap().poll_unpin(ctx)
  }
}

impl Drop for PluginOpAsyncFuture {
  fn drop(&mut self) {
    self.fut.take();
  }
}