worker.rs 10.3 KB
Newer Older
R
Ryan Dahl 已提交
1
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
K
Kitson Kelly 已提交
2
use crate::deno_error::DenoError;
3
use crate::state::ThreadSafeState;
4
use crate::tokio_util;
5
use deno;
6
use deno::ModuleSpecifier;
R
Ryan Dahl 已提交
7
use deno::StartupData;
8
use futures::Async;
R
Ryan Dahl 已提交
9
use futures::Future;
10 11
use std::sync::Arc;
use std::sync::Mutex;
B
Bartek Iwańczuk 已提交
12

13
/// Wraps deno::Isolate to provide source maps, ops for the CLI, and
14
/// high-level module loading
15
#[derive(Clone)]
16
pub struct Worker {
17
  isolate: Arc<Mutex<deno::Isolate>>,
18
  pub state: ThreadSafeState,
19 20
}

21 22 23 24
impl Worker {
  pub fn new(
    _name: String,
    startup_data: StartupData,
25
    state: ThreadSafeState,
26
  ) -> Worker {
27
    let state_ = state.clone();
28 29 30 31 32 33
    let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false)));
    {
      let mut i = isolate.lock().unwrap();
      i.set_dispatch(move |control_buf, zero_copy_buf| {
        state_.dispatch(control_buf, zero_copy_buf)
      });
34
    }
35
    Self { isolate, state }
36 37
  }

38
  /// Same as execute2() but the filename defaults to "<anonymous>".
K
Kitson Kelly 已提交
39
  pub fn execute(&mut self, js_source: &str) -> Result<(), DenoError> {
40 41 42 43 44 45
    self.execute2("<anonymous>", js_source)
  }

  /// Executes the provided JavaScript source code. The js_filename argument is
  /// provided only for debugging purposes.
  pub fn execute2(
46
    &mut self,
47 48
    js_filename: &str,
    js_source: &str,
K
Kitson Kelly 已提交
49
  ) -> Result<(), DenoError> {
50
    let mut isolate = self.isolate.lock().unwrap();
K
Kitson Kelly 已提交
51 52 53 54
    match isolate.execute(js_filename, js_source) {
      Ok(_) => Ok(()),
      Err(err) => Err(DenoError::from(err)),
    }
55 56
  }

57
  /// Executes the provided JavaScript module.
58
  pub fn execute_mod_async(
59
    &mut self,
60
    module_specifier: &ModuleSpecifier,
61
    is_prefetch: bool,
K
Kitson Kelly 已提交
62
  ) -> impl Future<Item = (), Error = DenoError> {
63 64 65
    let worker = self.clone();
    let worker_ = worker.clone();
    let loader = self.state.clone();
66
    let isolate = self.isolate.clone();
67
    let modules = self.state.modules.clone();
68 69 70 71 72 73
    let recursive_load = deno::RecursiveLoad::new(
      &module_specifier.to_string(),
      loader,
      isolate,
      modules,
    );
74 75 76
    recursive_load
      .and_then(move |id| -> Result<(), deno::JSErrorOr<DenoError>> {
        worker.state.progress.done();
77
        if is_prefetch {
78
          Ok(())
79
        } else {
80
          let mut isolate = worker.isolate.lock().unwrap();
81
          let result = isolate.mod_evaluate(id);
82
          if let Err(err) = result {
83
            Err(deno::JSErrorOr::JSError(err))
84
          } else {
85
            Ok(())
86
          }
87
        }
88 89
      }).map_err(move |err| {
        worker_.state.progress.done();
K
Kitson Kelly 已提交
90
        // Convert to DenoError AND apply_source_map.
91 92
        match err {
          deno::JSErrorOr::JSError(err) => {
K
Kitson Kelly 已提交
93
            worker_.apply_source_map(DenoError::from(err))
94
          }
K
Kitson Kelly 已提交
95
          deno::JSErrorOr::Other(err) => err,
96 97
        }
      })
98 99
  }

100
  /// Executes the provided JavaScript module.
101
  pub fn execute_mod(
102
    &mut self,
103
    module_specifier: &ModuleSpecifier,
104
    is_prefetch: bool,
K
Kitson Kelly 已提交
105
  ) -> Result<(), DenoError> {
106
    tokio_util::block_on(self.execute_mod_async(module_specifier, is_prefetch))
107 108
  }

109
  /// Applies source map to the error.
K
Kitson Kelly 已提交
110 111
  fn apply_source_map(&self, err: DenoError) -> DenoError {
    err.apply_source_map(&self.state.dir)
112 113
  }
}
114

115
impl Future for Worker {
116
  type Item = ();
K
Kitson Kelly 已提交
117
  type Error = DenoError;
118 119

  fn poll(&mut self) -> Result<Async<()>, Self::Error> {
120
    let mut isolate = self.isolate.lock().unwrap();
K
Kitson Kelly 已提交
121 122 123
    isolate
      .poll()
      .map_err(|err| self.apply_source_map(DenoError::from(err)))
124 125
  }
}
126

R
Ryan Dahl 已提交
127 128 129
#[cfg(test)]
mod tests {
  use super::*;
K
Kitson Kelly 已提交
130
  use crate::deno_error::err_check;
131
  use crate::flags;
A
andy finch 已提交
132
  use crate::ops::op_selector_std;
R
Ryan Dahl 已提交
133
  use crate::progress::Progress;
134 135
  use crate::resources;
  use crate::startup_data;
136
  use crate::state::ThreadSafeState;
137
  use crate::tokio_util;
138 139
  use futures::future::lazy;
  use std::sync::atomic::Ordering;
140 141

  #[test]
142
  fn execute_mod_esm_imports_a() {
143 144 145
    let module_specifier =
      ModuleSpecifier::resolve_root("tests/esm_imports_a.js").unwrap();
    let argv = vec![String::from("./deno"), module_specifier.to_string()];
R
Ryan Dahl 已提交
146 147 148 149 150 151
    let state = ThreadSafeState::new(
      flags::DenoFlags::default(),
      argv,
      op_selector_std,
      Progress::new(),
    );
152 153
    let state_ = state.clone();
    tokio_util::run(lazy(move || {
154 155
      let mut worker =
        Worker::new("TEST".to_string(), StartupData::None, state);
156
      let result = worker.execute_mod(&module_specifier, false);
157 158 159
      if let Err(err) = result {
        eprintln!("execute_mod err {:?}", err);
      }
160
      tokio_util::panic_on_error(worker)
161 162 163
    }));

    let metrics = &state_.metrics;
164
    assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 2);
R
Ryan Dahl 已提交
165 166
    // Check that we didn't start the compiler.
    assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 0);
167 168 169 170
  }

  #[test]
  fn execute_mod_circular() {
171 172 173
    let module_specifier =
      ModuleSpecifier::resolve_root("tests/circular1.js").unwrap();
    let argv = vec![String::from("./deno"), module_specifier.to_string()];
R
Ryan Dahl 已提交
174 175 176 177 178 179
    let state = ThreadSafeState::new(
      flags::DenoFlags::default(),
      argv,
      op_selector_std,
      Progress::new(),
    );
180 181
    let state_ = state.clone();
    tokio_util::run(lazy(move || {
182 183
      let mut worker =
        Worker::new("TEST".to_string(), StartupData::None, state);
184
      let result = worker.execute_mod(&module_specifier, false);
185 186 187
      if let Err(err) = result {
        eprintln!("execute_mod err {:?}", err);
      }
188
      tokio_util::panic_on_error(worker)
189 190 191
    }));

    let metrics = &state_.metrics;
192
    assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 2);
R
Ryan Dahl 已提交
193 194
    // Check that we didn't start the compiler.
    assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 0);
195
  }
196

R
Ryan Dahl 已提交
197 198
  #[test]
  fn execute_006_url_imports() {
199 200 201
    let module_specifier =
      ModuleSpecifier::resolve_root("tests/006_url_imports.ts").unwrap();
    let argv = vec![String::from("deno"), module_specifier.to_string()];
R
Ryan Dahl 已提交
202 203 204 205 206 207 208 209 210 211 212
    let mut flags = flags::DenoFlags::default();
    flags.reload = true;
    let state =
      ThreadSafeState::new(flags, argv, op_selector_std, Progress::new());
    let state_ = state.clone();
    tokio_util::run(lazy(move || {
      let mut worker = Worker::new(
        "TEST".to_string(),
        startup_data::deno_isolate_init(),
        state,
      );
K
Kitson Kelly 已提交
213
      err_check(worker.execute("denoMain()"));
214
      let result = worker.execute_mod(&module_specifier, false);
215 216 217
      if let Err(err) = result {
        eprintln!("execute_mod err {:?}", err);
      }
R
Ryan Dahl 已提交
218 219 220 221 222
      tokio_util::panic_on_error(worker)
    }));

    let metrics = &state_.metrics;
    assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 3);
R
Ryan Dahl 已提交
223 224
    // Check that we've only invoked the compiler once.
    assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 1);
R
Ryan Dahl 已提交
225 226
  }

227
  fn create_test_worker() -> Worker {
K
Kitson Kelly 已提交
228 229 230 231
    let state = ThreadSafeState::mock(vec![
      String::from("./deno"),
      String::from("hello.js"),
    ]);
232
    let mut worker =
233
      Worker::new("TEST".to_string(), startup_data::deno_isolate_init(), state);
K
Kitson Kelly 已提交
234 235
    err_check(worker.execute("denoMain()"));
    err_check(worker.execute("workerMain()"));
236 237 238 239 240 241 242 243 244 245 246
    worker
  }

  #[test]
  fn test_worker_messages() {
    tokio_util::init(|| {
      let mut worker = create_test_worker();
      let source = r#"
        onmessage = function(e) {
          console.log("msg from main script", e.data);
          if (e.data == "exit") {
247
            delete window.onmessage;
248 249 250 251 252 253 254 255
            return;
          } else {
            console.assert(e.data === "hi");
          }
          postMessage([1, 2, 3]);
          console.log("after postMessage");
        }
        "#;
K
Kitson Kelly 已提交
256
      err_check(worker.execute(source));
257 258 259 260 261 262 263

      let resource = worker.state.resource.clone();
      let resource_ = resource.clone();

      tokio::spawn(lazy(move || {
        worker.then(move |r| -> Result<(), ()> {
          resource_.close();
K
Kitson Kelly 已提交
264
          err_check(r);
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
          Ok(())
        })
      }));

      let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes();

      let r = resources::post_message_to_worker(resource.rid, msg).wait();
      assert!(r.is_ok());

      let maybe_msg = resources::get_message_from_worker(resource.rid)
        .wait()
        .unwrap();
      assert!(maybe_msg.is_some());
      // Check if message received is [1, 2, 3] in json
      assert_eq!(*maybe_msg.unwrap(), *b"[1,2,3]");

      let msg = json!("exit")
        .to_string()
        .into_boxed_str()
        .into_boxed_bytes();
      let r = resources::post_message_to_worker(resource.rid, msg).wait();
      assert!(r.is_ok());
    })
  }

  #[test]
  fn removed_from_resource_table_on_close() {
    tokio_util::init(|| {
      let mut worker = create_test_worker();
K
Kitson Kelly 已提交
294
      err_check(
295
        worker.execute("onmessage = () => { delete window.onmessage; }"),
296 297 298 299 300
      );

      let resource = worker.state.resource.clone();
      let rid = resource.rid;

301 302
      let worker_future = worker
        .then(move |r| -> Result<(), ()> {
303 304
          resource.close();
          println!("workers.rs after resource close");
K
Kitson Kelly 已提交
305
          err_check(r);
306
          Ok(())
307 308 309 310
        }).shared();

      let worker_future_ = worker_future.clone();
      tokio::spawn(lazy(move || worker_future_.then(|_| Ok(()))));
311 312 313 314 315 316 317 318

      assert_eq!(resources::get_type(rid), Some("worker".to_string()));

      let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes();
      let r = resources::post_message_to_worker(rid, msg).wait();
      assert!(r.is_ok());
      debug!("rid {:?}", rid);

319
      worker_future.wait().unwrap();
320 321 322
      assert_eq!(resources::get_type(rid), None);
    })
  }
323 324 325

  #[test]
  fn execute_mod_resolve_error() {
R
Ryan Dahl 已提交
326
    tokio_util::init(|| {
K
Kitson Kelly 已提交
327
      // "foo" is not a valid module specifier so this should return an error.
328
      let mut worker = create_test_worker();
329 330 331
      let module_specifier =
        ModuleSpecifier::resolve_root("does-not-exist").unwrap();
      let result = worker.execute_mod_async(&module_specifier, false).wait();
R
Ryan Dahl 已提交
332 333
      assert!(result.is_err());
    })
334 335 336 337
  }

  #[test]
  fn execute_mod_002_hello() {
R
Ryan Dahl 已提交
338 339 340
    tokio_util::init(|| {
      // This assumes cwd is project root (an assumption made throughout the
      // tests).
341
      let mut worker = create_test_worker();
342 343 344
      let module_specifier =
        ModuleSpecifier::resolve_root("./tests/002_hello.ts").unwrap();
      let result = worker.execute_mod_async(&module_specifier, false).wait();
R
Ryan Dahl 已提交
345 346
      assert!(result.is_ok());
    })
347
  }
348
}