summaryrefslogtreecommitdiff
path: root/src/isolate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/isolate.rs')
-rw-r--r--src/isolate.rs799
1 files changed, 113 insertions, 686 deletions
diff --git a/src/isolate.rs b/src/isolate.rs
index 8a77777d2..7f3c0d919 100644
--- a/src/isolate.rs
+++ b/src/isolate.rs
@@ -1,353 +1,82 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-// Do not use FlatBuffers in this module.
-// TODO Currently this module uses Tokio, but it would be nice if they were
-// decoupled.
-
-#![allow(dead_code)]
-
+use crate::cli::Cli;
use crate::compiler::compile_sync;
use crate::compiler::ModuleMetaData;
-use crate::deno_dir;
use crate::errors::DenoError;
-use crate::errors::DenoResult;
use crate::errors::RustOrJsError;
-use crate::flags;
-use crate::global_timer::GlobalTimer;
-use crate::isolate_init::IsolateInit;
-use crate::js_errors::apply_source_map;
-use crate::libdeno;
-use crate::modules::Modules;
+use crate::isolate_state::IsolateState;
+use crate::js_errors;
use crate::msg;
-use crate::permissions::DenoPermissions;
-use crate::tokio_util;
+use deno_core;
+use deno_core::deno_mod;
use deno_core::JSError;
-use futures::sync::mpsc as async_mpsc;
+use futures::Async;
use futures::Future;
-use libc::c_char;
-use libc::c_void;
-use std;
-use std::cell::Cell;
-use std::cell::RefCell;
-use std::env;
-use std::ffi::CStr;
-use std::ffi::CString;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::mpsc;
use std::sync::Arc;
-use std::sync::Mutex;
-use std::sync::{Once, ONCE_INIT};
-use tokio;
-
-// Buf represents a byte array returned from a "Op".
-// The message might be empty (which will be translated into a null object on
-// the javascript side) or it is a heap allocated opaque sequence of bytes.
-// Usually a flatbuffer message.
-pub type Buf = Box<[u8]>;
-
-// JS promises in Deno map onto a specific Future
-// which yields either a DenoError or a byte array.
-pub type Op = dyn Future<Item = Buf, Error = DenoError> + Send;
-
-// Returns (is_sync, op)
-pub type Dispatch = fn(
- isolate: &Isolate,
- buf: libdeno::deno_buf,
- zero_copy_buf: libdeno::deno_buf,
-) -> (bool, Box<Op>);
-
-pub struct Isolate {
- libdeno_isolate: *const libdeno::isolate,
- dispatch: Dispatch,
- rx: mpsc::Receiver<(usize, Buf)>,
- tx: mpsc::Sender<(usize, Buf)>,
- ntasks: Cell<i32>,
- pub modules: RefCell<Modules>,
- pub state: Arc<IsolateState>,
- pub permissions: Arc<DenoPermissions>,
-}
-
-pub type WorkerSender = async_mpsc::Sender<Buf>;
-pub type WorkerReceiver = async_mpsc::Receiver<Buf>;
-pub type WorkerChannels = (WorkerSender, WorkerReceiver);
-
-// Isolate cannot be passed between threads but IsolateState can.
-// IsolateState satisfies Send and Sync.
-// So any state that needs to be accessed outside the main V8 thread should be
-// inside IsolateState.
-#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
-pub struct IsolateState {
- pub dir: deno_dir::DenoDir,
- pub argv: Vec<String>,
- pub flags: flags::DenoFlags,
- pub metrics: Metrics,
- pub worker_channels: Option<Mutex<WorkerChannels>>,
- pub global_timer: Mutex<GlobalTimer>,
-}
-
-impl IsolateState {
- pub fn new(
- flags: flags::DenoFlags,
- argv_rest: Vec<String>,
- worker_channels: Option<WorkerChannels>,
- ) -> Self {
- let custom_root = env::var("DENO_DIR").map(|s| s.into()).ok();
-
- Self {
- dir: deno_dir::DenoDir::new(flags.reload, flags.recompile, custom_root)
- .unwrap(),
- argv: argv_rest,
- flags,
- metrics: Metrics::default(),
- worker_channels: worker_channels.map(Mutex::new),
- global_timer: Mutex::new(GlobalTimer::new()),
- }
- }
- pub fn main_module(&self) -> Option<String> {
- if self.argv.len() <= 1 {
- None
- } else {
- let specifier = self.argv[1].clone();
- let referrer = ".";
- match self.dir.resolve_module_url(&specifier, referrer) {
- Ok(url) => Some(url.to_string()),
- Err(e) => {
- debug!("Potentially swallowed error {}", e);
- None
- }
- }
- }
- }
+type CoreIsolate = deno_core::Isolate<Cli>;
- #[cfg(test)]
- pub fn mock() -> Arc<IsolateState> {
- let argv = vec![String::from("./deno"), String::from("hello.js")];
- // For debugging: argv.push_back(String::from("-D"));
- let (flags, rest_argv, _) = flags::set_flags(argv).unwrap();
- Arc::new(IsolateState::new(flags, rest_argv, None))
- }
-
- fn metrics_op_dispatched(
- &self,
- bytes_sent_control: usize,
- bytes_sent_data: usize,
- ) {
- self.metrics.ops_dispatched.fetch_add(1, Ordering::SeqCst);
- self
- .metrics
- .bytes_sent_control
- .fetch_add(bytes_sent_control, Ordering::SeqCst);
- self
- .metrics
- .bytes_sent_data
- .fetch_add(bytes_sent_data, Ordering::SeqCst);
- }
-
- fn metrics_op_completed(&self, bytes_received: usize) {
- self.metrics.ops_completed.fetch_add(1, Ordering::SeqCst);
- self
- .metrics
- .bytes_received
- .fetch_add(bytes_received, Ordering::SeqCst);
- }
-}
-
-// AtomicU64 is currently unstable
-#[derive(Default)]
-pub struct Metrics {
- pub ops_dispatched: AtomicUsize,
- pub ops_completed: AtomicUsize,
- pub bytes_sent_control: AtomicUsize,
- pub bytes_sent_data: AtomicUsize,
- pub bytes_received: AtomicUsize,
- pub resolve_count: AtomicUsize,
+/// Wraps deno_core::Isolate to provide source maps, ops for the CLI, and
+/// high-level module loading
+pub struct Isolate {
+ inner: CoreIsolate,
+ state: Arc<IsolateState>,
}
-static DENO_INIT: Once = ONCE_INIT;
-
impl Isolate {
- pub fn new(
- init: IsolateInit,
- state: Arc<IsolateState>,
- dispatch: Dispatch,
- permissions: DenoPermissions,
- ) -> Self {
- DENO_INIT.call_once(|| {
- unsafe { libdeno::deno_init() };
- });
- let config = libdeno::deno_config {
- will_snapshot: 0,
- load_snapshot: match init.snapshot {
- Some(s) => s,
- None => libdeno::deno_buf::empty(),
- },
- shared: libdeno::deno_buf::empty(), // TODO Use for message passing.
- recv_cb: pre_dispatch,
- };
- let libdeno_isolate = unsafe { libdeno::deno_new(config) };
- // This channel handles sending async messages back to the runtime.
- let (tx, rx) = mpsc::channel::<(usize, Buf)>();
-
- let new_isolate = Self {
- libdeno_isolate,
- dispatch,
- rx,
- tx,
- ntasks: Cell::new(0),
- modules: RefCell::new(Modules::new()),
+ pub fn new(cli: Cli) -> Isolate {
+ let state = cli.state.clone();
+ Self {
+ inner: CoreIsolate::new(cli),
state,
- permissions: Arc::new(permissions),
- };
-
- // Run init script if present.
- match init.init_script {
- Some(init_script) => new_isolate
- .execute2(init_script.filename.as_str(), init_script.source.as_str())
- .unwrap(),
- None => {}
- };
-
- new_isolate
- }
-
- #[inline]
- pub fn as_raw_ptr(&self) -> *const c_void {
- self as *const _ as *const c_void
- }
-
- #[inline]
- pub unsafe fn from_raw_ptr<'a>(ptr: *const c_void) -> &'a Self {
- let ptr = ptr as *const _;
- &*ptr
- }
-
- #[inline]
- pub fn check_read(&self, filename: &str) -> DenoResult<()> {
- self.permissions.check_read(filename)
- }
-
- #[inline]
- pub fn check_write(&self, filename: &str) -> DenoResult<()> {
- self.permissions.check_write(filename)
- }
-
- #[inline]
- pub fn check_env(&self) -> DenoResult<()> {
- self.permissions.check_env()
- }
-
- #[inline]
- pub fn check_net(&self, filename: &str) -> DenoResult<()> {
- self.permissions.check_net(filename)
- }
-
- #[inline]
- pub fn check_run(&self) -> DenoResult<()> {
- self.permissions.check_run()
- }
-
- pub fn last_exception(&self) -> Option<JSError> {
- let ptr = unsafe { libdeno::deno_last_exception(self.libdeno_isolate) };
- if ptr.is_null() {
- None
- } else {
- let cstr = unsafe { CStr::from_ptr(ptr) };
- let v8_exception = cstr.to_str().unwrap();
- debug!("v8_exception\n{}\n", v8_exception);
- let js_error = JSError::from_v8_exception(v8_exception).unwrap();
- let js_error_mapped = apply_source_map(&js_error, &self.state.dir);
- Some(js_error_mapped)
}
}
/// Same as execute2() but the filename defaults to "<anonymous>".
- pub fn execute(&self, js_source: &str) -> Result<(), JSError> {
+ pub fn execute(&mut self, js_source: &str) -> Result<(), JSError> {
self.execute2("<anonymous>", js_source)
}
/// Executes the provided JavaScript source code. The js_filename argument is
/// provided only for debugging purposes.
pub fn execute2(
- &self,
+ &mut self,
js_filename: &str,
js_source: &str,
) -> Result<(), JSError> {
- let filename = CString::new(js_filename).unwrap();
- let source = CString::new(js_source).unwrap();
- unsafe {
- libdeno::deno_execute(
- self.libdeno_isolate,
- self.as_raw_ptr(),
- filename.as_ptr(),
- source.as_ptr(),
- )
- };
- if let Some(err) = self.last_exception() {
- return Err(err);
- }
- Ok(())
- }
-
- pub fn mod_new(
- &mut self,
- main: bool,
- name: String,
- source: String,
- ) -> Result<libdeno::deno_mod, JSError> {
- let name_ = CString::new(name.clone()).unwrap();
- let name_ptr = name_.as_ptr() as *const c_char;
-
- let source_ = CString::new(source.clone()).unwrap();
- let source_ptr = source_.as_ptr() as *const c_char;
-
- let id = unsafe {
- libdeno::deno_mod_new(self.libdeno_isolate, main, name_ptr, source_ptr)
- };
- if let Some(js_error) = self.last_exception() {
- assert_eq!(id, 0);
- return Err(js_error);
- }
-
- self.modules.borrow_mut().register(id, &name);
-
- Ok(id)
+ self.inner.execute(js_filename, js_source)
}
// TODO(ry) make this return a future.
- pub fn mod_load_deps(
- &mut self,
- id: libdeno::deno_mod,
- ) -> Result<(), RustOrJsError> {
+ fn mod_load_deps(&self, id: deno_mod) -> Result<(), RustOrJsError> {
// basically iterate over the imports, start loading them.
- let referrer_name =
- { self.modules.borrow_mut().get_name(id).unwrap().clone() };
- let len =
- unsafe { libdeno::deno_mod_imports_len(self.libdeno_isolate, id) };
-
- for i in 0..len {
- let specifier_ptr =
- unsafe { libdeno::deno_mod_imports_get(self.libdeno_isolate, id, i) };
- let specifier_c: &CStr = unsafe { CStr::from_ptr(specifier_ptr) };
- let specifier: &str = specifier_c.to_str().unwrap();
+ let referrer_name = {
+ let g = self.state.modules.lock().unwrap();
+ g.get_name(id).unwrap().clone()
+ };
+ for specifier in self.inner.mod_get_imports(id) {
let (name, _local_filename) = self
.state
.dir
- .resolve_module(specifier, &referrer_name)
+ .resolve_module(&specifier, &referrer_name)
.map_err(DenoError::from)
.map_err(RustOrJsError::from)?;
- debug!("mod_load_deps {} {}", i, name);
+ debug!("mod_load_deps {}", name);
- if !self.modules.borrow_mut().is_registered(&name) {
+ if !self.state.modules.lock().unwrap().is_registered(&name) {
let out = fetch_module_meta_data_and_maybe_compile(
&self.state,
- specifier,
+ &specifier,
&referrer_name,
)?;
- let child_id =
- self.mod_new(false, out.module_name.clone(), out.js_source())?;
+ let child_id = self.mod_new_and_register(
+ false,
+ &out.module_name.clone(),
+ &out.js_source(),
+ )?;
self.mod_load_deps(child_id)?;
}
@@ -356,140 +85,77 @@ impl Isolate {
Ok(())
}
- pub fn mod_instantiate(&self, id: libdeno::deno_mod) -> Result<(), JSError> {
- unsafe {
- libdeno::deno_mod_instantiate(
- self.libdeno_isolate,
- self.as_raw_ptr(),
- id,
- resolve_cb,
- )
- };
- if let Some(js_error) = self.last_exception() {
- return Err(js_error);
- }
-
- Ok(())
- }
-
- pub fn mod_evaluate(&self, id: libdeno::deno_mod) -> Result<(), JSError> {
- unsafe {
- libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id)
- };
- if let Some(js_error) = self.last_exception() {
- return Err(js_error);
- }
- Ok(())
- }
-
/// Executes the provided JavaScript module.
pub fn execute_mod(
&mut self,
js_filename: &str,
is_prefetch: bool,
) -> Result<(), RustOrJsError> {
- let out =
- fetch_module_meta_data_and_maybe_compile(&self.state, js_filename, ".")
- .map_err(RustOrJsError::from)?;
+ // TODO move isolate_state::execute_mod impl here.
+ self
+ .execute_mod_inner(js_filename, is_prefetch)
+ .map_err(|err| match err {
+ RustOrJsError::Js(err) => RustOrJsError::Js(self.apply_source_map(err)),
+ x => x,
+ })
+ }
+
+ /// High-level way to execute modules.
+ /// This will issue HTTP requests and file system calls.
+ /// Blocks. TODO(ry) Don't block.
+ fn execute_mod_inner(
+ &mut self,
+ url: &str,
+ is_prefetch: bool,
+ ) -> Result<(), RustOrJsError> {
+ let out = fetch_module_meta_data_and_maybe_compile(&self.state, url, ".")
+ .map_err(RustOrJsError::from)?;
let id = self
- .mod_new(true, out.module_name.clone(), out.js_source())
+ .mod_new_and_register(true, &out.module_name.clone(), &out.js_source())
.map_err(RustOrJsError::from)?;
self.mod_load_deps(id)?;
- self.mod_instantiate(id).map_err(RustOrJsError::from)?;
+ self
+ .inner
+ .mod_instantiate(id)
+ .map_err(RustOrJsError::from)?;
if !is_prefetch {
- self.mod_evaluate(id).map_err(RustOrJsError::from)?;
- }
- Ok(())
- }
-
- pub fn respond(&self, zero_copy_id: usize, buf: Buf) {
- self.state.metrics_op_completed(buf.len());
-
- // This will be cleaned up in the future.
- if zero_copy_id > 0 {
- unsafe {
- libdeno::deno_zero_copy_release(self.libdeno_isolate, zero_copy_id)
- }
- }
-
- // deno_respond will memcpy the buf into V8's heap,
- // so borrowing a reference here is sufficient.
- unsafe {
- libdeno::deno_respond(
- self.libdeno_isolate,
- self.as_raw_ptr(),
- buf.as_ref().into(),
- )
- }
- }
-
- fn complete_op(&self, zero_copy_id: usize, buf: Buf) {
- // Receiving a message on rx exactly corresponds to an async task
- // completing.
- self.ntasks_decrement();
- // Call into JS with the buf.
- self.respond(zero_copy_id, buf);
- }
-
- fn timeout(&self) {
- let dummy_buf = libdeno::deno_buf::empty();
- unsafe {
- libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), dummy_buf)
- }
- }
-
- fn check_promise_errors(&self) {
- unsafe {
- libdeno::deno_check_promise_errors(self.libdeno_isolate);
- }
- }
-
- // TODO Use Park abstraction? Note at time of writing Tokio default runtime
- // does not have new_with_park().
- pub fn event_loop(&self) -> Result<(), JSError> {
- // Main thread event loop.
- while !self.is_idle() {
- match self.rx.recv() {
- Ok((zero_copy_id, buf)) => self.complete_op(zero_copy_id, buf),
- Err(e) => panic!("Isolate.rx.recv() failed: {:?}", e),
- }
- self.check_promise_errors();
- if let Some(err) = self.last_exception() {
- return Err(err);
- }
- }
- // Check on done
- self.check_promise_errors();
- if let Some(err) = self.last_exception() {
- return Err(err);
+ self.inner.mod_evaluate(id).map_err(RustOrJsError::from)?;
}
Ok(())
}
- #[inline]
- fn ntasks_increment(&self) {
- assert!(self.ntasks.get() >= 0);
- self.ntasks.set(self.ntasks.get() + 1);
+ /// Wraps Isolate::mod_new but registers with modules.
+ fn mod_new_and_register(
+ &self,
+ main: bool,
+ name: &str,
+ source: &str,
+ ) -> Result<deno_mod, JSError> {
+ let id = self.inner.mod_new(main, name, source)?;
+ self.state.modules.lock().unwrap().register(id, &name);
+ Ok(id)
}
- #[inline]
- fn ntasks_decrement(&self) {
- self.ntasks.set(self.ntasks.get() - 1);
- assert!(self.ntasks.get() >= 0);
+ pub fn print_file_info(&self, module: &str) {
+ let m = self.state.modules.lock().unwrap();
+ m.print_file_info(&self.state.dir, module.to_string());
}
- #[inline]
- fn is_idle(&self) -> bool {
- self.ntasks.get() == 0
+ /// Applies source map to the error.
+ fn apply_source_map(&self, err: JSError) -> JSError {
+ js_errors::apply_source_map(&err, &self.state.dir)
}
}
-impl Drop for Isolate {
- fn drop(&mut self) {
- unsafe { libdeno::deno_delete(self.libdeno_isolate) }
+impl Future for Isolate {
+ type Item = ();
+ type Error = JSError;
+
+ fn poll(&mut self) -> Result<Async<()>, Self::Error> {
+ self.inner.poll().map_err(|err| self.apply_source_map(err))
}
}
@@ -511,308 +177,69 @@ fn fetch_module_meta_data_and_maybe_compile(
Ok(out)
}
-extern "C" fn resolve_cb(
- user_data: *mut c_void,
- specifier_ptr: *const c_char,
- referrer: libdeno::deno_mod,
-) -> libdeno::deno_mod {
- let isolate = unsafe { Isolate::from_raw_ptr(user_data) };
- let specifier_c: &CStr = unsafe { CStr::from_ptr(specifier_ptr) };
- let specifier: &str = specifier_c.to_str().unwrap();
- isolate
- .state
- .metrics
- .resolve_count
- .fetch_add(1, Ordering::Relaxed);
- isolate.modules.borrow_mut().resolve_cb(
- &isolate.state.dir,
- specifier,
- referrer,
- )
-}
-
-// Dereferences the C pointer into the Rust Isolate object.
-extern "C" fn pre_dispatch(
- user_data: *mut c_void,
- control_buf: libdeno::deno_buf,
- zero_copy_buf: libdeno::deno_buf,
-) {
- // for metrics
- let bytes_sent_control = control_buf.len();
- let bytes_sent_zero_copy = zero_copy_buf.len();
-
- let zero_copy_id = zero_copy_buf.zero_copy_id;
-
- // We should ensure that there is no other `&mut Isolate` exists.
- // And also, it should be in the same thread with other `&Isolate`s.
- let isolate = unsafe { Isolate::from_raw_ptr(user_data) };
- let dispatch = isolate.dispatch;
- let (is_sync, op) = dispatch(isolate, control_buf, zero_copy_buf);
-
- isolate
- .state
- .metrics_op_dispatched(bytes_sent_control, bytes_sent_zero_copy);
-
- if is_sync {
- // Execute op synchronously.
- let buf = tokio_util::block_on(op).unwrap();
- let buf_size = buf.len();
-
- if buf_size == 0 {
- // FIXME
- isolate.state.metrics_op_completed(buf.len());
- } else {
- // Set the synchronous response, the value returned from isolate.send().
- isolate.respond(zero_copy_id, buf);
- }
- } else {
- // Execute op asynchronously.
- let tx = isolate.tx.clone();
-
- // TODO Ideally Tokio would could tell us how many tasks are executing, but
- // it cannot currently. Therefore we track top-level promises/tasks
- // manually.
- isolate.ntasks_increment();
-
- let task = op
- .and_then(move |buf| {
- let sender = tx; // tx is moved to new thread
- sender.send((zero_copy_id, buf)).expect("tx.send error");
- Ok(())
- }).map_err(|_| ());
- tokio::spawn(task);
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
- use futures;
-
- #[test]
- fn test_dispatch_sync() {
- let state = IsolateState::mock();
- let init = IsolateInit {
- snapshot: None,
- init_script: None,
- };
- let isolate =
- Isolate::new(init, state, dispatch_sync, DenoPermissions::default());
- tokio_util::init(|| {
- isolate
- .execute(
- r#"
- const m = new Uint8Array([4, 5, 6]);
- let n = libdeno.send(m);
- if (!(n.byteLength === 3 &&
- n[0] === 1 &&
- n[1] === 2 &&
- n[2] === 3)) {
- throw Error("assert error");
- }
- "#,
- ).expect("execute error");
- isolate.event_loop().ok();
- });
- }
-
- fn dispatch_sync(
- _isolate: &Isolate,
- control: libdeno::deno_buf,
- data: libdeno::deno_buf,
- ) -> (bool, Box<Op>) {
- assert_eq!(control[0], 4);
- assert_eq!(control[1], 5);
- assert_eq!(control[2], 6);
- assert_eq!(data.len(), 0);
- // Send back some sync response.
- let vec: Vec<u8> = vec![1, 2, 3];
- let control = vec.into_boxed_slice();
- let op = Box::new(futures::future::ok(control));
- (true, op)
- }
-
- #[test]
- fn test_metrics_sync() {
- let state = IsolateState::mock();
- let init = IsolateInit {
- snapshot: None,
- init_script: None,
- };
- let isolate = Isolate::new(
- init,
- state,
- metrics_dispatch_sync,
- DenoPermissions::default(),
- );
- tokio_util::init(|| {
- // Verify that metrics have been properly initialized.
- {
- let metrics = &isolate.state.metrics;
- assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_sent_control.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_sent_data.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_received.load(Ordering::SeqCst), 0);
- }
-
- isolate
- .execute(
- r#"
- const control = new Uint8Array([4, 5, 6]);
- const data = new Uint8Array([42, 43, 44, 45, 46]);
- libdeno.send(control, data);
- "#,
- ).expect("execute error");;
- isolate.event_loop().unwrap();
- let metrics = &isolate.state.metrics;
- assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 1);
- assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 1);
- assert_eq!(metrics.bytes_sent_control.load(Ordering::SeqCst), 3);
- assert_eq!(metrics.bytes_sent_data.load(Ordering::SeqCst), 5);
- assert_eq!(metrics.bytes_received.load(Ordering::SeqCst), 4);
- });
- }
-
- #[test]
- fn test_metrics_async() {
- let state = IsolateState::mock();
- let init = IsolateInit {
- snapshot: None,
- init_script: None,
- };
- let isolate = Isolate::new(
- init,
- state,
- metrics_dispatch_async,
- DenoPermissions::default(),
- );
- tokio_util::init(|| {
- // Verify that metrics have been properly initialized.
- {
- let metrics = &isolate.state.metrics;
- assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_sent_control.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_sent_data.load(Ordering::SeqCst), 0);
- assert_eq!(metrics.bytes_received.load(Ordering::SeqCst), 0);
- }
-
- isolate
- .execute(
- r#"
- const control = new Uint8Array([4, 5, 6]);
- const data = new Uint8Array([42, 43, 44, 45, 46]);
- let r = libdeno.send(control, data);
- libdeno.recv(() => {});
- if (r != null) throw Error("expected null");
- "#,
- ).expect("execute error");
-
- // Make sure relevant metrics are updated before task is executed.
- {
- let metrics = &isolate.state.metrics;
- assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 1);
- assert_eq!(metrics.bytes_sent_control.load(Ordering::SeqCst), 3);
- assert_eq!(metrics.bytes_sent_data.load(Ordering::SeqCst), 5);
- // Note we cannot check ops_completed nor bytes_received because that
- // would be a race condition. It might be nice to have use a oneshot
- // with metrics_dispatch_async() to properly validate them.
- }
-
- isolate.event_loop().unwrap();
-
- // Make sure relevant metrics are updated after task is executed.
- {
- let metrics = &isolate.state.metrics;
- assert_eq!(metrics.ops_dispatched.load(Ordering::SeqCst), 1);
- assert_eq!(metrics.ops_completed.load(Ordering::SeqCst), 1);
- assert_eq!(metrics.bytes_sent_control.load(Ordering::SeqCst), 3);
- assert_eq!(metrics.bytes_sent_data.load(Ordering::SeqCst), 5);
- assert_eq!(metrics.bytes_received.load(Ordering::SeqCst), 4);
- }
- });
- }
-
- fn metrics_dispatch_sync(
- _isolate: &Isolate,
- _control: libdeno::deno_buf,
- _data: libdeno::deno_buf,
- ) -> (bool, Box<Op>) {
- // Send back some sync response
- let vec: Box<[u8]> = vec![1, 2, 3, 4].into_boxed_slice();
- let op = Box::new(futures::future::ok(vec));
- (true, op)
- }
-
- fn metrics_dispatch_async(
- _isolate: &Isolate,
- _control: libdeno::deno_buf,
- _data: libdeno::deno_buf,
- ) -> (bool, Box<Op>) {
- // Send back some sync response
- let vec: Box<[u8]> = vec![1, 2, 3, 4].into_boxed_slice();
- let op = Box::new(futures::future::ok(vec));
- (false, op)
- }
-
- #[test]
- fn thread_safety() {
- fn is_thread_safe<T: Sync + Send>() {}
- is_thread_safe::<IsolateState>();
- }
+ use crate::flags;
+ use crate::isolate_init::IsolateInit;
+ use crate::permissions::DenoPermissions;
+ use crate::tokio_util;
+ use futures::future::lazy;
+ use std::sync::atomic::Ordering;
#[test]
fn execute_mod() {
let filename = std::env::current_dir()
.unwrap()
.join("tests/esm_imports_a.js");
- let filename = filename.to_str().unwrap();
+ let filename = filename.to_str().unwrap().to_string();
- let argv = vec![String::from("./deno"), String::from(filename)];
+ let argv = vec![String::from("./deno"), filename.clone()];
let (flags, rest_argv, _) = flags::set_flags(argv).unwrap();
let state = Arc::new(IsolateState::new(flags, rest_argv, None));
+ let state_ = state.clone();
let init = IsolateInit {
snapshot: None,
init_script: None,
};
- let mut isolate =
- Isolate::new(init, state, dispatch_sync, DenoPermissions::default());
- tokio_util::init(|| {
- isolate
- .execute_mod(filename, false)
- .expect("execute_mod error");
- isolate.event_loop().ok();
- });
-
- let metrics = &isolate.state.metrics;
+ tokio_util::run(lazy(move || {
+ let cli = Cli::new(init, state.clone(), DenoPermissions::default());
+ let mut isolate = Isolate::new(cli);
+ if let Err(err) = isolate.execute_mod(&filename, false) {
+ eprintln!("execute_mod err {:?}", err);
+ }
+ tokio_util::panic_on_error(isolate)
+ }));
+
+ let metrics = &state_.metrics;
assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 1);
}
#[test]
fn execute_mod_circular() {
let filename = std::env::current_dir().unwrap().join("tests/circular1.js");
- let filename = filename.to_str().unwrap();
+ let filename = filename.to_str().unwrap().to_string();
- let argv = vec![String::from("./deno"), String::from(filename)];
+ let argv = vec![String::from("./deno"), filename.clone()];
let (flags, rest_argv, _) = flags::set_flags(argv).unwrap();
let state = Arc::new(IsolateState::new(flags, rest_argv, None));
+ let state_ = state.clone();
let init = IsolateInit {
snapshot: None,
init_script: None,
};
- let mut isolate =
- Isolate::new(init, state, dispatch_sync, DenoPermissions::default());
- tokio_util::init(|| {
- isolate
- .execute_mod(filename, false)
- .expect("execute_mod error");
- isolate.event_loop().ok();
- });
-
- let metrics = &isolate.state.metrics;
+ tokio_util::run(lazy(move || {
+ let cli = Cli::new(init, state.clone(), DenoPermissions::default());
+ let mut isolate = Isolate::new(cli);
+ if let Err(err) = isolate.execute_mod(&filename, false) {
+ eprintln!("execute_mod err {:?}", err);
+ }
+ tokio_util::panic_on_error(isolate)
+ }));
+
+ let metrics = &state_.metrics;
assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 2);
}
}