// 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_mod; 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_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 Op = dyn Future + Send; struct PendingOp { op: Box, zero_copy_id: usize, // non-zero if associated zero-copy buffer. } struct OpResult { buf: Buf, zero_copy_id: usize, } impl Future for PendingOp { type Item = OpResult; type Error = (); fn poll(&mut self) -> Poll { // Ops should not error. If an op experiences an error it needs to // encode that error into a buf, so it can be returned to JS. Ok(match self.op.poll().expect("ops should not error") { NotReady => NotReady, Ready(buf) => Ready(OpResult { buf, zero_copy_id: self.zero_copy_id, }), }) } } /// 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, } #[derive(Default)] pub struct Config { dispatch: Option (bool, Box) + Send + Sync>>, 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], deno_buf) -> (bool, Box) + Send + Sync + 'static, { self.dispatch = 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 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, 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