// Copyright 2018 the Deno authors. All rights reserved. MIT license. // Do not add dependenies to modules.rs. it should remain decoupled from the // isolate to keep the Isolate struct from becoming too bloating for users who // do not need asynchronous module loading. use crate::js_errors::JSError; use crate::libdeno; use crate::libdeno::deno_buf; use crate::libdeno::deno_dyn_import_id; use crate::libdeno::deno_mod; use crate::libdeno::deno_pinned_buf; use crate::libdeno::PinnedBuf; use crate::libdeno::Snapshot1; use crate::libdeno::Snapshot2; use crate::shared_queue::SharedQueue; use crate::shared_queue::RECOMMENDED_SIZE; use futures::stream::{FuturesUnordered, Stream}; use futures::task; use futures::Async::*; use futures::Future; use futures::Poll; use libc::c_char; use libc::c_void; use std::ffi::CStr; use std::ffi::CString; use std::ptr::null; use std::sync::{Arc, Mutex, Once, ONCE_INIT}; pub type Buf = Box<[u8]>; pub type OpAsyncFuture = Box + Send>; pub enum Op { Sync(Buf), Async(OpAsyncFuture), } /// Stores a script used to initalize a Isolate pub struct Script<'a> { pub source: &'a str, pub filename: &'a str, } /// Represents data used to initialize isolate at startup /// either a binary snapshot or a javascript source file /// in the form of the StartupScript struct. pub enum StartupData<'a> { Script(Script<'a>), Snapshot(&'a [u8]), LibdenoSnapshot(Snapshot1<'a>), None, } type DispatchFn = Fn(&[u8], Option) -> Op; pub type DynImportFuture = Box + Send>; type DynImportFn = Fn(&str, &str) -> DynImportFuture; /// Wraps DynImportFuture to include the deno_dyn_import_id, so that it doesn't /// need to be exposed. struct DynImport { id: deno_dyn_import_id, inner: DynImportFuture, } impl Future for DynImport { type Item = (deno_dyn_import_id, deno_mod); type Error = (); fn poll(&mut self) -> Poll { match self.inner.poll() { Ok(Ready(mod_id)) => Ok(Ready((self.id, mod_id))), Ok(NotReady) => Ok(NotReady), // Note that mod_id 0 indicates error. Err(()) => Ok(Ready((self.id, 0))), } } } #[derive(Default)] pub struct Config { dispatch: Option>, dyn_import: Option>, pub will_snapshot: bool, } impl Config { /// Defines the how Deno.core.dispatch() acts. /// Called whenever Deno.core.dispatch() is called in JavaScript. zero_copy_buf /// corresponds to the second argument of Deno.core.dispatch(). pub fn dispatch(&mut self, f: F) where F: Fn(&[u8], Option) -> Op + Send + Sync + 'static, { self.dispatch = Some(Arc::new(f)); } pub fn dyn_import(&mut self, f: F) where F: Fn(&str, &str) -> DynImportFuture + Send + Sync + 'static, { self.dyn_import = Some(Arc::new(f)); } } /// A single execution context of JavaScript. Corresponds roughly to the "Web /// Worker" concept in the DOM. An Isolate is a Future that can be used with /// Tokio. The Isolate future complete when there is an error or when all /// pending ops have completed. /// /// Ops are created in JavaScript by calling Deno.core.dispatch(), and in Rust /// by implementing deno::Dispatch::dispatch. An async Op corresponds exactly to /// a Promise in JavaScript. pub struct Isolate { libdeno_isolate: *const libdeno::isolate, shared_libdeno_isolate: Arc>>, config: Config, needs_init: bool, shared: SharedQueue, pending_ops: FuturesUnordered, pending_dyn_imports: FuturesUnordered, have_unpolled_ops: bool, } unsafe impl Send for Isolate {} impl Drop for Isolate { fn drop(&mut self) { // remove shared_libdeno_isolate reference *self.shared_libdeno_isolate.lock().unwrap() = None; unsafe { libdeno::deno_delete(self.libdeno_isolate) } } } static DENO_INIT: Once = ONCE_INIT; impl Isolate { /// startup_data defines the snapshot or script used at startup to initalize /// the isolate. // TODO(ry) move startup_data into Config. Ideally without introducing a // generic lifetime into the Isolate struct... pub fn new(startup_data: StartupData, config: Config) -> Self { DENO_INIT.call_once(|| { unsafe { libdeno::deno_init() }; }); let shared = SharedQueue::new(RECOMMENDED_SIZE); let needs_init = true; let mut startup_script: Option