diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ansi.rs | 70 | ||||
-rw-r--r-- | src/cli.rs | 90 | ||||
-rw-r--r-- | src/compiler.rs | 151 | ||||
-rw-r--r-- | src/deno_dir.rs | 1369 | ||||
-rw-r--r-- | src/errors.rs | 207 | ||||
-rw-r--r-- | src/flags.rs | 291 | ||||
-rw-r--r-- | src/fs.rs | 110 | ||||
-rw-r--r-- | src/global_timer.rs | 49 | ||||
-rw-r--r-- | src/http_body.rs | 112 | ||||
-rw-r--r-- | src/http_util.rs | 166 | ||||
-rw-r--r-- | src/isolate.rs | 236 | ||||
-rw-r--r-- | src/isolate_state.rs | 110 | ||||
-rw-r--r-- | src/js_errors.rs | 424 | ||||
-rw-r--r-- | src/main.rs | 140 | ||||
-rw-r--r-- | src/modules.rs | 204 | ||||
-rw-r--r-- | src/msg.fbs | 524 | ||||
-rw-r--r-- | src/msg.rs | 26 | ||||
-rw-r--r-- | src/msg_util.rs | 127 | ||||
-rw-r--r-- | src/ops.rs | 2020 | ||||
-rw-r--r-- | src/permissions.rs | 343 | ||||
-rw-r--r-- | src/repl.rs | 114 | ||||
-rw-r--r-- | src/resolve_addr.rs | 156 | ||||
-rw-r--r-- | src/resources.rs | 494 | ||||
-rw-r--r-- | src/startup_data.rs | 57 | ||||
-rw-r--r-- | src/tokio_util.rs | 118 | ||||
-rw-r--r-- | src/tokio_write.rs | 62 | ||||
-rw-r--r-- | src/version.rs | 6 | ||||
-rw-r--r-- | src/workers.rs | 181 |
28 files changed, 0 insertions, 7957 deletions
diff --git a/src/ansi.rs b/src/ansi.rs deleted file mode 100644 index 95b5e0694..000000000 --- a/src/ansi.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use ansi_term::Color::Fixed; -use ansi_term::Color::Red; -use ansi_term::Style; -use regex::Regex; -use std::env; -use std::fmt; - -lazy_static! { - // STRIP_ANSI_RE and strip_ansi_codes are lifted from the "console" crate. - // Copyright 2017 Armin Ronacher <armin.ronacher@active-4.com>. MIT License. - static ref STRIP_ANSI_RE: Regex = Regex::new( - r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" - ).unwrap(); - static ref NO_COLOR: bool = { - env::var_os("NO_COLOR").is_some() - }; -} - -/// Helper function to strip ansi codes. -#[cfg(test)] -pub fn strip_ansi_codes(s: &str) -> std::borrow::Cow<str> { - STRIP_ANSI_RE.replace_all(s, "") -} - -pub fn use_color() -> bool { - !(*NO_COLOR) -} - -pub fn red_bold(s: String) -> impl fmt::Display { - let mut style = Style::new(); - if use_color() { - style = style.bold().fg(Red); - } - style.paint(s) -} - -pub fn italic_bold(s: String) -> impl fmt::Display { - let mut style = Style::new(); - if use_color() { - style = style.italic().bold(); - } - style.paint(s) -} - -pub fn yellow(s: String) -> impl fmt::Display { - let mut style = Style::new(); - if use_color() { - // matches TypeScript's ForegroundColorEscapeSequences.Yellow - style = style.fg(Fixed(11)); - } - style.paint(s) -} - -pub fn cyan(s: String) -> impl fmt::Display { - let mut style = Style::new(); - if use_color() { - // matches TypeScript's ForegroundColorEscapeSequences.Cyan - style = style.fg(Fixed(14)); - } - style.paint(s) -} - -pub fn bold(s: String) -> impl fmt::Display { - let mut style = Style::new(); - if use_color() { - style = style.bold(); - } - style.paint(s) -} diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 42b2b29f8..000000000 --- a/src/cli.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -#![allow(unused_variables)] -#![allow(dead_code)] - -use crate::errors::DenoResult; -use crate::isolate_state::IsolateState; -use crate::ops; -use crate::permissions::DenoPermissions; -use deno_core::deno_buf; -use deno_core::deno_mod; -use deno_core::Behavior; -use deno_core::Op; -use deno_core::StartupData; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -// 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]>; - -/// Implements deno_core::Behavior for the main Deno command-line. -pub struct Cli { - startup_data: Option<StartupData>, - pub state: Arc<IsolateState>, - pub permissions: Arc<DenoPermissions>, // TODO(ry) move to IsolateState -} - -impl Cli { - pub fn new( - startup_data: Option<StartupData>, - state: Arc<IsolateState>, - permissions: DenoPermissions, - ) -> Self { - Self { - startup_data, - state, - permissions: Arc::new(permissions), - } - } - - #[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() - } -} - -impl Behavior for Cli { - fn startup_data(&mut self) -> Option<StartupData> { - self.startup_data.take() - } - - fn resolve(&mut self, specifier: &str, referrer: deno_mod) -> deno_mod { - self - .state - .metrics - .resolve_count - .fetch_add(1, Ordering::Relaxed); - let mut modules = self.state.modules.lock().unwrap(); - modules.resolve_cb(&self.state.dir, specifier, referrer) - } - - fn dispatch( - &mut self, - control: &[u8], - zero_copy: deno_buf, - ) -> (bool, Box<Op>) { - ops::dispatch(self, control, zero_copy) - } -} diff --git a/src/compiler.rs b/src/compiler.rs deleted file mode 100644 index 2d6e4a4b7..000000000 --- a/src/compiler.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::cli::Buf; -use crate::isolate_state::IsolateState; -use crate::msg; -use crate::permissions::{DenoPermissions, PermissionAccessor}; -use crate::resources; -use crate::resources::Resource; -use crate::resources::ResourceId; -use crate::startup_data; -use crate::workers; -use futures::Future; -use serde_json; -use std::str; -use std::sync::Mutex; - -lazy_static! { - static ref C_RID: Mutex<Option<ResourceId>> = Mutex::new(None); -} - -// This corresponds to JS ModuleMetaData. -// TODO Rename one or the other so they correspond. -#[derive(Debug)] -pub struct ModuleMetaData { - pub module_name: String, - pub filename: String, - pub media_type: msg::MediaType, - pub source_code: Vec<u8>, - pub maybe_output_code_filename: Option<String>, - pub maybe_output_code: Option<Vec<u8>>, - pub maybe_source_map_filename: Option<String>, - pub maybe_source_map: Option<Vec<u8>>, -} - -impl ModuleMetaData { - pub fn js_source(&self) -> String { - if self.media_type == msg::MediaType::Json { - return format!( - "export default {};", - str::from_utf8(&self.source_code).unwrap() - ); - } - match self.maybe_output_code { - None => str::from_utf8(&self.source_code).unwrap().to_string(), - Some(ref output_code) => str::from_utf8(output_code).unwrap().to_string(), - } - } -} - -fn lazy_start(parent_state: &IsolateState) -> Resource { - let mut cell = C_RID.lock().unwrap(); - let startup_data = startup_data::compiler_isolate_init(); - let permissions = DenoPermissions { - allow_read: PermissionAccessor::from(true), - allow_write: PermissionAccessor::from(true), - allow_net: PermissionAccessor::from(true), - ..Default::default() - }; - - let rid = cell.get_or_insert_with(|| { - let resource = workers::spawn( - Some(startup_data), - parent_state, - "compilerMain()".to_string(), - permissions, - ); - resource.rid - }); - Resource { rid: *rid } -} - -fn req(specifier: &str, referrer: &str) -> Buf { - json!({ - "specifier": specifier, - "referrer": referrer, - }).to_string() - .into_boxed_str() - .into_boxed_bytes() -} - -pub fn compile_sync( - parent_state: &IsolateState, - specifier: &str, - referrer: &str, - module_meta_data: &ModuleMetaData, -) -> ModuleMetaData { - let req_msg = req(specifier, referrer); - - let compiler = lazy_start(parent_state); - - let send_future = resources::worker_post_message(compiler.rid, req_msg); - send_future.wait().unwrap(); - - let recv_future = resources::worker_recv_message(compiler.rid); - let result = recv_future.wait().unwrap(); - assert!(result.is_some()); - let res_msg = result.unwrap(); - - let res_json = std::str::from_utf8(&res_msg).unwrap(); - match serde_json::from_str::<serde_json::Value>(res_json) { - Ok(serde_json::Value::Object(map)) => ModuleMetaData { - module_name: module_meta_data.module_name.clone(), - filename: module_meta_data.filename.clone(), - media_type: module_meta_data.media_type, - source_code: module_meta_data.source_code.clone(), - maybe_output_code: match map["outputCode"].as_str() { - Some(str) => Some(str.as_bytes().to_owned()), - _ => None, - }, - maybe_output_code_filename: None, - maybe_source_map: match map["sourceMap"].as_str() { - Some(str) => Some(str.as_bytes().to_owned()), - _ => None, - }, - maybe_source_map_filename: None, - }, - _ => panic!("error decoding compiler response"), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compile_sync() { - let cwd = std::env::current_dir().unwrap(); - let cwd_string = cwd.to_str().unwrap().to_owned(); - - let specifier = "./tests/002_hello.ts"; - let referrer = cwd_string + "/"; - - let mut out = ModuleMetaData { - module_name: "xxx".to_owned(), - filename: "/tests/002_hello.ts".to_owned(), - media_type: msg::MediaType::TypeScript, - source_code: "console.log(\"Hello World\");".as_bytes().to_owned(), - maybe_output_code_filename: None, - maybe_output_code: None, - maybe_source_map_filename: None, - maybe_source_map: None, - }; - - out = compile_sync(&IsolateState::mock(), specifier, &referrer, &mut out); - assert!( - out - .maybe_output_code - .unwrap() - .starts_with("console.log(\"Hello World\");".as_bytes()) - ); - } -} diff --git a/src/deno_dir.rs b/src/deno_dir.rs deleted file mode 100644 index 829af5aa2..000000000 --- a/src/deno_dir.rs +++ /dev/null @@ -1,1369 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::compiler::ModuleMetaData; -use crate::errors; -use crate::errors::DenoError; -use crate::errors::DenoResult; -use crate::errors::ErrorKind; -use crate::fs as deno_fs; -use crate::http_util; -use crate::js_errors::SourceMapGetter; -use crate::msg; -use crate::version; - -use dirs; -use ring; -use std; -use std::fmt::Write; -use std::fs; -use std::path::Path; -use std::path::PathBuf; -use std::result::Result; -use std::str; -use url; -use url::Url; - -/// Gets corresponding MediaType given extension -fn extmap(ext: &str) -> msg::MediaType { - match ext { - "ts" => msg::MediaType::TypeScript, - "js" => msg::MediaType::JavaScript, - "json" => msg::MediaType::Json, - _ => msg::MediaType::Unknown, - } -} - -pub struct DenoDir { - // Example: /Users/rld/.deno/ - pub root: PathBuf, - // In the Go code this was called SrcDir. - // This is where we cache http resources. Example: - // /Users/rld/.deno/deps/github.com/ry/blah.js - pub gen: PathBuf, - // In the Go code this was called CacheDir. - // This is where we cache compilation outputs. Example: - // /Users/rld/.deno/gen/f39a473452321cacd7c346a870efb0e3e1264b43.js - pub deps: PathBuf, - // This splits to http and https deps - pub deps_http: PathBuf, - pub deps_https: PathBuf, - // If remote resources should be reloaded. - reload: bool, - // recompile the typescript files. - // if true, not load cache files - // else, load cache files - recompile: bool, -} - -impl DenoDir { - // Must be called before using any function from this module. - // https://github.com/denoland/deno/blob/golang/deno_dir.go#L99-L111 - pub fn new( - reload: bool, - recompile: bool, - custom_root: Option<PathBuf>, - ) -> std::io::Result<Self> { - // Only setup once. - let home_dir = dirs::home_dir().expect("Could not get home directory."); - let fallback = home_dir.join(".deno"); - // We use the OS cache dir because all files deno writes are cache files - // Once that changes we need to start using different roots if DENO_DIR - // is not set, and keep a single one if it is. - let default = dirs::cache_dir() - .map(|d| d.join("deno")) - .unwrap_or(fallback); - - let root: PathBuf = custom_root.unwrap_or(default); - let gen = root.as_path().join("gen"); - let deps = root.as_path().join("deps"); - let deps_http = deps.join("http"); - let deps_https = deps.join("https"); - - let deno_dir = Self { - root, - gen, - deps, - deps_http, - deps_https, - reload, - recompile, - }; - - // TODO Lazily create these directories. - deno_fs::mkdir(deno_dir.gen.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps_http.as_ref(), 0o755, true)?; - deno_fs::mkdir(deno_dir.deps_https.as_ref(), 0o755, true)?; - - debug!("root {}", deno_dir.root.display()); - debug!("gen {}", deno_dir.gen.display()); - debug!("deps {}", deno_dir.deps.display()); - debug!("deps_http {}", deno_dir.deps_http.display()); - debug!("deps_https {}", deno_dir.deps_https.display()); - - Ok(deno_dir) - } - - // https://github.com/denoland/deno/blob/golang/deno_dir.go#L32-L35 - pub fn cache_path( - self: &Self, - filename: &str, - source_code: &[u8], - ) -> (PathBuf, PathBuf) { - let cache_key = source_code_hash(filename, source_code, version::DENO); - ( - self.gen.join(cache_key.to_string() + ".js"), - self.gen.join(cache_key.to_string() + ".js.map"), - ) - } - - fn load_cache( - self: &Self, - filename: &str, - source_code: &[u8], - ) -> Result<(Vec<u8>, Vec<u8>), std::io::Error> { - let (output_code, source_map) = self.cache_path(filename, source_code); - debug!( - "load_cache code: {} map: {}", - output_code.display(), - source_map.display() - ); - let read_output_code = fs::read(&output_code)?; - let read_source_map = fs::read(&source_map)?; - Ok((read_output_code, read_source_map)) - } - - pub fn code_cache( - self: &Self, - module_meta_data: &ModuleMetaData, - ) -> std::io::Result<()> { - let (cache_path, source_map_path) = self - .cache_path(&module_meta_data.filename, &module_meta_data.source_code); - // TODO(ry) This is a race condition w.r.t to exists() -- probably should - // create the file in exclusive mode. A worry is what might happen is there - // are two processes and one reads the cache file while the other is in the - // midst of writing it. - if cache_path.exists() && source_map_path.exists() { - Ok(()) - } else { - match &module_meta_data.maybe_output_code { - Some(output_code) => fs::write(cache_path, output_code), - _ => Ok(()), - }?; - match &module_meta_data.maybe_source_map { - Some(source_map) => fs::write(source_map_path, source_map), - _ => Ok(()), - }?; - Ok(()) - } - } - - // Prototype https://github.com/denoland/deno/blob/golang/deno_dir.go#L37-L73 - /// Fetch remote source code. - fn fetch_remote_source( - self: &Self, - module_name: &str, - filename: &str, - ) -> DenoResult<Option<ModuleMetaData>> { - let p = Path::new(&filename); - // We write a special ".mime" file into the `.deno/deps` directory along side the - // cached file, containing just the media type. - let media_type_filename = [&filename, ".mime"].concat(); - let mt = Path::new(&media_type_filename); - eprint!("Downloading {}...", &module_name); // no newline - let maybe_source = http_util::fetch_sync_string(&module_name); - if let Ok((source, content_type)) = maybe_source { - eprintln!(""); // next line - match p.parent() { - Some(ref parent) => fs::create_dir_all(parent), - None => Ok(()), - }?; - deno_fs::write_file(&p, &source, 0o666)?; - // Remove possibly existing stale .mime file - // may not exist. DON'T unwrap - let _ = std::fs::remove_file(&media_type_filename); - // Create .mime file only when content type different from extension - let resolved_content_type = map_content_type(&p, Some(&content_type)); - let ext = p - .extension() - .map(|x| x.to_str().unwrap_or("")) - .unwrap_or(""); - let media_type = extmap(&ext); - if media_type == msg::MediaType::Unknown - || media_type != resolved_content_type - { - deno_fs::write_file(&mt, content_type.as_bytes(), 0o666)? - } - return Ok(Some(ModuleMetaData { - module_name: module_name.to_string(), - filename: filename.to_string(), - media_type: map_content_type(&p, Some(&content_type)), - source_code: source.as_bytes().to_owned(), - maybe_output_code_filename: None, - maybe_output_code: None, - maybe_source_map_filename: None, - maybe_source_map: None, - })); - } else { - eprintln!(" NOT FOUND"); - } - Ok(None) - } - - /// Fetch local or cached source code. - fn fetch_local_source( - self: &Self, - module_name: &str, - filename: &str, - ) -> DenoResult<Option<ModuleMetaData>> { - let p = Path::new(&filename); - let media_type_filename = [&filename, ".mime"].concat(); - let mt = Path::new(&media_type_filename); - let source_code = match fs::read(p) { - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound { - return Ok(None); - } else { - return Err(e.into()); - } - } - Ok(c) => c, - }; - // .mime file might not exists - // this is okay for local source: maybe_content_type_str will be None - let maybe_content_type_string = fs::read_to_string(&mt).ok(); - // Option<String> -> Option<&str> - let maybe_content_type_str = - maybe_content_type_string.as_ref().map(String::as_str); - Ok(Some(ModuleMetaData { - module_name: module_name.to_string(), - filename: filename.to_string(), - media_type: map_content_type(&p, maybe_content_type_str), - source_code, - maybe_output_code_filename: None, - maybe_output_code: None, - maybe_source_map_filename: None, - maybe_source_map: None, - })) - } - - // Prototype: https://github.com/denoland/deno/blob/golang/os.go#L122-L138 - fn get_source_code( - self: &Self, - module_name: &str, - filename: &str, - ) -> DenoResult<ModuleMetaData> { - let is_module_remote = is_remote(module_name); - // We try fetch local. Two cases: - // 1. This is a remote module, but no reload provided - // 2. This is a local module - if !is_module_remote || !self.reload { - debug!( - "fetch local or reload {} is_module_remote {}", - module_name, is_module_remote - ); - match self.fetch_local_source(&module_name, &filename)? { - Some(output) => { - debug!("found local source "); - return Ok(output); - } - None => { - debug!("fetch_local_source returned None"); - } - } - } - - // If not remote file, stop here! - if !is_module_remote { - debug!("not remote file stop here"); - return Err(DenoError::from(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("cannot find local file '{}'", filename), - ))); - } - - debug!("is remote but didn't find module"); - - // not cached/local, try remote - let maybe_remote_source = - self.fetch_remote_source(&module_name, &filename)?; - if let Some(output) = maybe_remote_source { - return Ok(output); - } - Err(DenoError::from(std::io::Error::new( - std::io::ErrorKind::NotFound, - format!("cannot find remote file '{}'", filename), - ))) - } - - pub fn fetch_module_meta_data( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<ModuleMetaData, errors::DenoError> { - debug!( - "fetch_module_meta_data. specifier {} referrer {}", - specifier, referrer - ); - - let (module_name, filename) = self.resolve_module(specifier, referrer)?; - - let result = self.get_source_code(module_name.as_str(), filename.as_str()); - let mut out = match result { - Ok(out) => out, - Err(err) => { - if err.kind() == ErrorKind::NotFound { - // For NotFound, change the message to something better. - return Err(errors::new( - ErrorKind::NotFound, - format!( - "Cannot resolve module \"{}\" from \"{}\"", - specifier, referrer - ), - )); - } else { - return Err(err); - } - } - }; - - if out.source_code.starts_with("#!".as_bytes()) { - out.source_code = filter_shebang(out.source_code); - } - - if out.media_type != msg::MediaType::TypeScript { - return Ok(out); - } - - let (output_code_filename, output_source_map_filename) = - self.cache_path(&out.filename, &out.source_code); - let mut maybe_output_code = None; - let mut maybe_source_map = None; - - if !self.recompile { - let result = self.load_cache(out.filename.as_str(), &out.source_code); - match result { - Err(err) => { - if err.kind() == std::io::ErrorKind::NotFound { - return Ok(out); - } else { - return Err(err.into()); - } - } - Ok((output_code, source_map)) => { - maybe_output_code = Some(output_code); - maybe_source_map = Some(source_map); - } - } - } - - Ok(ModuleMetaData { - module_name: out.module_name, - filename: out.filename, - media_type: out.media_type, - source_code: out.source_code, - maybe_output_code_filename: output_code_filename - .to_str() - .map(String::from), - maybe_output_code, - maybe_source_map_filename: output_source_map_filename - .to_str() - .map(String::from), - maybe_source_map, - }) - } - - // Prototype: https://github.com/denoland/deno/blob/golang/os.go#L56-L68 - fn src_file_to_url(self: &Self, filename: &str) -> String { - let filename_path = Path::new(filename); - if filename_path.starts_with(&self.deps) { - let (rest, prefix) = if filename_path.starts_with(&self.deps_https) { - let rest = filename_path.strip_prefix(&self.deps_https).unwrap(); - let prefix = "https://".to_string(); - (rest, prefix) - } else if filename_path.starts_with(&self.deps_http) { - let rest = filename_path.strip_prefix(&self.deps_http).unwrap(); - let prefix = "http://".to_string(); - (rest, prefix) - } else { - // TODO(kevinkassimo): change this to support other protocols than http - unimplemented!() - }; - // Windows doesn't support ":" in filenames, so we represent port using a - // special string. - // TODO(ry) This current implementation will break on a URL that has - // the default port but contains "_PORT" in the path. - let rest = rest.to_str().unwrap().replacen("_PORT", ":", 1); - prefix + &rest - } else { - String::from(filename) - } - } - - /// Returns (module name, local filename) - pub fn resolve_module_url( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<Url, url::ParseError> { - let specifier = self.src_file_to_url(specifier); - let mut referrer = self.src_file_to_url(referrer); - - debug!( - "resolve_module specifier {} referrer {}", - specifier, referrer - ); - - if referrer.starts_with('.') { - let cwd = std::env::current_dir().unwrap(); - let referrer_path = cwd.join(referrer); - referrer = referrer_path.to_str().unwrap().to_string() + "/"; - } - - let j = if is_remote(&specifier) || Path::new(&specifier).is_absolute() { - parse_local_or_remote(&specifier)? - } else if referrer.ends_with('/') { - let r = Url::from_directory_path(&referrer); - // TODO(ry) Properly handle error. - if r.is_err() { - error!("Url::from_directory_path error {}", referrer); - } - let base = r.unwrap(); - base.join(specifier.as_ref())? - } else { - let base = parse_local_or_remote(&referrer)?; - base.join(specifier.as_ref())? - }; - Ok(j) - } - - /// Returns (module name, local filename) - pub fn resolve_module( - self: &Self, - specifier: &str, - referrer: &str, - ) -> Result<(String, String), url::ParseError> { - let j = self.resolve_module_url(specifier, referrer)?; - - let module_name = j.to_string(); - let filename; - match j.scheme() { - "file" => { - filename = deno_fs::normalize_path(j.to_file_path().unwrap().as_ref()); - } - "https" => { - filename = deno_fs::normalize_path( - get_cache_filename(self.deps_https.as_path(), &j).as_ref(), - ) - } - "http" => { - filename = deno_fs::normalize_path( - get_cache_filename(self.deps_http.as_path(), &j).as_ref(), - ) - } - // TODO(kevinkassimo): change this to support other protocols than http - _ => unimplemented!(), - } - - debug!("module_name: {}, filename: {}", module_name, filename); - Ok((module_name, filename)) - } -} - -impl SourceMapGetter for DenoDir { - fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> { - match self.fetch_module_meta_data(script_name, ".") { - Err(_e) => None, - Ok(out) => match out.maybe_source_map { - None => None, - Some(source_map) => Some(source_map), - }, - } - } -} - -fn get_cache_filename(basedir: &Path, url: &Url) -> PathBuf { - let host = url.host_str().unwrap(); - let host_port = match url.port() { - // Windows doesn't support ":" in filenames, so we represent port using a - // special string. - Some(port) => format!("{}_PORT{}", host, port), - None => host.to_string(), - }; - - let mut out = basedir.to_path_buf(); - out.push(host_port); - for path_seg in url.path_segments().unwrap() { - out.push(path_seg); - } - out -} - -fn source_code_hash( - filename: &str, - source_code: &[u8], - version: &str, -) -> String { - let mut ctx = ring::digest::Context::new(&ring::digest::SHA1); - ctx.update(version.as_bytes()); - ctx.update(filename.as_bytes()); - ctx.update(source_code); - let digest = ctx.finish(); - let mut out = String::new(); - // TODO There must be a better way to do this... - for byte in digest.as_ref() { - write!(&mut out, "{:02x}", byte).unwrap(); - } - out -} - -fn is_remote(module_name: &str) -> bool { - module_name.starts_with("http://") || module_name.starts_with("https://") -} - -fn parse_local_or_remote(p: &str) -> Result<url::Url, url::ParseError> { - if is_remote(p) || p.starts_with("file:") { - Url::parse(p) - } else { - Url::from_file_path(p).map_err(|_err| url::ParseError::IdnaError) - } -} - -fn map_file_extension(path: &Path) -> msg::MediaType { - match path.extension() { - None => msg::MediaType::Unknown, - Some(os_str) => match os_str.to_str() { - Some("ts") => msg::MediaType::TypeScript, - Some("js") => msg::MediaType::JavaScript, - Some("json") => msg::MediaType::Json, - _ => msg::MediaType::Unknown, - }, - } -} - -// convert a ContentType string into a enumerated MediaType -fn map_content_type(path: &Path, content_type: Option<&str>) -> msg::MediaType { - match content_type { - Some(content_type) => { - // sometimes there is additional data after the media type in - // Content-Type so we have to do a bit of manipulation so we are only - // dealing with the actual media type - let ct_vector: Vec<&str> = content_type.split(';').collect(); - let ct: &str = ct_vector.first().unwrap(); - match ct.to_lowercase().as_ref() { - "application/typescript" - | "text/typescript" - | "video/vnd.dlna.mpeg-tts" - | "video/mp2t" - | "application/x-typescript" => msg::MediaType::TypeScript, - "application/javascript" - | "text/javascript" - | "application/ecmascript" - | "text/ecmascript" - | "application/x-javascript" => msg::MediaType::JavaScript, - "application/json" | "text/json" => msg::MediaType::Json, - "text/plain" => map_file_extension(path), - _ => { - debug!("unknown content type: {}", content_type); - msg::MediaType::Unknown - } - } - } - None => map_file_extension(path), - } -} - -fn filter_shebang(bytes: Vec<u8>) -> Vec<u8> { - let string = str::from_utf8(&bytes).unwrap(); - if let Some(i) = string.find('\n') { - let (_, rest) = string.split_at(i); - rest.as_bytes().to_owned() - } else { - Vec::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tokio_util; - use tempfile::TempDir; - - fn test_setup(reload: bool, recompile: bool) -> (TempDir, DenoDir) { - let temp_dir = TempDir::new().expect("tempdir fail"); - let deno_dir = - DenoDir::new(reload, recompile, Some(temp_dir.path().to_path_buf())) - .expect("setup fail"); - (temp_dir, deno_dir) - } - - // The `add_root` macro prepends "C:" to a string if on windows; on posix - // systems it returns the input string untouched. This is necessary because - // `Url::from_file_path()` fails if the input path isn't an absolute path. - macro_rules! add_root { - ($path:expr) => { - if cfg!(target_os = "windows") { - concat!("C:", $path) - } else { - $path - } - }; - } - - macro_rules! file_url { - ($path:expr) => { - if cfg!(target_os = "windows") { - concat!("file:///C:", $path) - } else { - concat!("file://", $path) - } - }; - } - - #[test] - fn test_get_cache_filename() { - let url = Url::parse("http://example.com:1234/path/to/file.ts").unwrap(); - let basedir = Path::new("/cache/dir/"); - let cache_file = get_cache_filename(&basedir, &url); - assert_eq!( - cache_file, - Path::new("/cache/dir/example.com_PORT1234/path/to/file.ts") - ); - } - - #[test] - fn test_cache_path() { - let (temp_dir, deno_dir) = test_setup(false, false); - let filename = "hello.js"; - let source_code = "1+2".as_bytes(); - let hash = source_code_hash(filename, source_code, version::DENO); - assert_eq!( - ( - temp_dir.path().join(format!("gen/{}.js", hash)), - temp_dir.path().join(format!("gen/{}.js.map", hash)) - ), - deno_dir.cache_path(filename, source_code) - ); - } - - #[test] - fn test_code_cache() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let filename = "hello.js"; - let source_code = "1+2".as_bytes(); - let output_code = "1+2 // output code".as_bytes(); - let source_map = "{}".as_bytes(); - let hash = source_code_hash(filename, source_code, version::DENO); - let (cache_path, source_map_path) = - deno_dir.cache_path(filename, source_code); - assert!(cache_path.ends_with(format!("gen/{}.js", hash))); - assert!(source_map_path.ends_with(format!("gen/{}.js.map", hash))); - - let out = ModuleMetaData { - filename: filename.to_owned(), - source_code: source_code.to_owned(), - module_name: "hello.js".to_owned(), - media_type: msg::MediaType::TypeScript, - maybe_output_code: Some(output_code.to_owned()), - maybe_output_code_filename: None, - maybe_source_map: Some(source_map.to_owned()), - maybe_source_map_filename: None, - }; - - let r = deno_dir.code_cache(&out); - r.expect("code_cache error"); - assert!(cache_path.exists()); - assert_eq!(output_code.to_owned(), fs::read(&cache_path).unwrap()); - } - - #[test] - fn test_source_code_hash() { - assert_eq!( - "7e44de2ed9e0065da09d835b76b8d70be503d276", - source_code_hash("hello.ts", "1+2".as_bytes(), "0.2.11") - ); - // Different source_code should result in different hash. - assert_eq!( - "57033366cf9db1ef93deca258cdbcd9ef5f4bde1", - source_code_hash("hello.ts", "1".as_bytes(), "0.2.11") - ); - // Different filename should result in different hash. - assert_eq!( - "19657f90b5b0540f87679e2fb362e7bd62b644b0", - source_code_hash("hi.ts", "1+2".as_bytes(), "0.2.11") - ); - // Different version should result in different hash. - assert_eq!( - "e2b4b7162975a02bf2770f16836eb21d5bcb8be1", - source_code_hash("hi.ts", "1+2".as_bytes(), "0.2.0") - ); - } - - #[test] - fn test_get_source_code_1() { - let (temp_dir, deno_dir) = test_setup(false, false); - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let module_name = "http://localhost:4545/tests/subdir/mod2.ts"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mod2.ts") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - - let result = deno_dir.get_source_code(module_name, &filename); - println!("module_name {} filename {}", module_name, filename); - assert!(result.is_ok()); - let r = result.unwrap(); - assert_eq!( - r.source_code, - "export { printHello } from \"./print_hello.ts\";\n".as_bytes() - ); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // Should not create .mime file due to matching ext - assert!(fs::read_to_string(&mime_file_name).is_err()); - - // Modify .mime - let _ = fs::write(&mime_file_name, "text/javascript"); - let result2 = deno_dir.get_source_code(module_name, &filename); - assert!(result2.is_ok()); - let r2 = result2.unwrap(); - assert_eq!( - r2.source_code, - "export { printHello } from \"./print_hello.ts\";\n".as_bytes() - ); - // If get_source_code does not call remote, this should be JavaScript - // as we modified before! (we do not overwrite .mime due to no http fetch) - assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/javascript" - ); - - // Force self.reload - let deno_dir = - DenoDir::new(true, false, Some(temp_dir.path().to_path_buf())) - .expect("setup fail"); - let result3 = deno_dir.get_source_code(module_name, &filename); - assert!(result3.is_ok()); - let r3 = result3.unwrap(); - let expected3 = - "export { printHello } from \"./print_hello.ts\";\n".as_bytes(); - assert_eq!(r3.source_code, expected3); - // Now the old .mime file should have gone! Resolved back to TypeScript - assert_eq!(&(r3.media_type), &msg::MediaType::TypeScript); - assert!(fs::read_to_string(&mime_file_name).is_err()); - }); - } - - #[test] - fn test_get_source_code_2() { - let (temp_dir, deno_dir) = test_setup(false, false); - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let module_name = "http://localhost:4545/tests/subdir/mismatch_ext.ts"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mismatch_ext.ts") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - - let result = deno_dir.get_source_code(module_name, &filename); - println!("module_name {} filename {}", module_name, filename); - assert!(result.is_ok()); - let r = result.unwrap(); - let expected = "export const loaded = true;\n".as_bytes(); - assert_eq!(r.source_code, expected); - // Mismatch ext with content type, create .mime - assert_eq!(&(r.media_type), &msg::MediaType::JavaScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/javascript" - ); - - // Modify .mime - let _ = fs::write(&mime_file_name, "text/typescript"); - let result2 = deno_dir.get_source_code(module_name, &filename); - assert!(result2.is_ok()); - let r2 = result2.unwrap(); - let expected2 = "export const loaded = true;\n".as_bytes(); - assert_eq!(r2.source_code, expected2); - // If get_source_code does not call remote, this should be TypeScript - // as we modified before! (we do not overwrite .mime due to no http fetch) - assert_eq!(&(r2.media_type), &msg::MediaType::TypeScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/typescript" - ); - - // Force self.reload - let deno_dir = - DenoDir::new(true, false, Some(temp_dir.path().to_path_buf())) - .expect("setup fail"); - let result3 = deno_dir.get_source_code(module_name, &filename); - assert!(result3.is_ok()); - let r3 = result3.unwrap(); - let expected3 = "export const loaded = true;\n".as_bytes(); - assert_eq!(r3.source_code, expected3); - // Now the old .mime file should be overwritten back to JavaScript! - // (due to http fetch) - assert_eq!(&(r3.media_type), &msg::MediaType::JavaScript); - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/javascript" - ); - }); - } - - #[test] - fn test_fetch_source_1() { - use crate::tokio_util; - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let (_temp_dir, deno_dir) = test_setup(false, false); - let module_name = - "http://localhost:4545/tests/subdir/mt_video_mp2t.t3.ts"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mt_video_mp2t.t3.ts") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - - let result = deno_dir.fetch_remote_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // matching ext, no .mime file created - assert!(fs::read_to_string(&mime_file_name).is_err()); - - // Modify .mime, make sure read from local - let _ = fs::write(&mime_file_name, "text/javascript"); - let result2 = deno_dir.fetch_local_source(module_name, &filename); - assert!(result2.is_ok()); - let r2 = result2.unwrap().unwrap(); - assert_eq!(r2.source_code, "export const loaded = true;\n".as_bytes()); - // Not MediaType::TypeScript due to .mime modification - assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript); - }); - } - - #[test] - fn test_fetch_source_2() { - use crate::tokio_util; - // http_util::fetch_sync_string requires tokio - tokio_util::init(|| { - let (_temp_dir, deno_dir) = test_setup(false, false); - let module_name = "http://localhost:4545/tests/subdir/no_ext"; - let filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/no_ext") - .as_ref(), - ); - let mime_file_name = format!("{}.mime", &filename); - let result = deno_dir.fetch_remote_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - // no ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name).unwrap(), - "text/typescript" - ); - - let module_name_2 = "http://localhost:4545/tests/subdir/mismatch_ext.ts"; - let filename_2 = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mismatch_ext.ts") - .as_ref(), - ); - let mime_file_name_2 = format!("{}.mime", &filename_2); - let result_2 = deno_dir.fetch_remote_source(module_name_2, &filename_2); - assert!(result_2.is_ok()); - let r2 = result_2.unwrap().unwrap(); - assert_eq!(r2.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r2.media_type), &msg::MediaType::JavaScript); - // mismatch ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name_2).unwrap(), - "text/javascript" - ); - - // test unknown extension - let module_name_3 = "http://localhost:4545/tests/subdir/unknown_ext.deno"; - let filename_3 = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/unknown_ext.deno") - .as_ref(), - ); - let mime_file_name_3 = format!("{}.mime", &filename_3); - let result_3 = deno_dir.fetch_remote_source(module_name_3, &filename_3); - assert!(result_3.is_ok()); - let r3 = result_3.unwrap().unwrap(); - assert_eq!(r3.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r3.media_type), &msg::MediaType::TypeScript); - // unknown ext, should create .mime file - assert_eq!( - fs::read_to_string(&mime_file_name_3).unwrap(), - "text/typescript" - ); - }); - } - - #[test] - fn test_fetch_source_3() { - // only local, no http_util::fetch_sync_string called - let (_temp_dir, deno_dir) = test_setup(false, false); - let cwd = std::env::current_dir().unwrap(); - let cwd_string = cwd.to_str().unwrap(); - let module_name = "http://example.com/mt_text_typescript.t1.ts"; // not used - let filename = - format!("{}/tests/subdir/mt_text_typescript.t1.ts", &cwd_string); - - let result = deno_dir.fetch_local_source(module_name, &filename); - assert!(result.is_ok()); - let r = result.unwrap().unwrap(); - assert_eq!(r.source_code, "export const loaded = true;\n".as_bytes()); - assert_eq!(&(r.media_type), &msg::MediaType::TypeScript); - } - - #[test] - fn test_fetch_module_meta_data() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let cwd = std::env::current_dir().unwrap(); - let cwd_string = String::from(cwd.to_str().unwrap()) + "/"; - - // Test failure case. - let specifier = "hello.ts"; - let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - assert!(r.is_err()); - - // Assuming cwd is the deno repo root. - let specifier = "./js/main.ts"; - let referrer = cwd_string.as_str(); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - assert!(r.is_ok()); - //let fetch_module_meta_data_output = r.unwrap(); - //println!("fetch_module_meta_data_output {:?}", fetch_module_meta_data_output); - } - - #[test] - fn test_fetch_module_meta_data_1() { - /*recompile ts file*/ - let (_temp_dir, deno_dir) = test_setup(false, true); - - let cwd = std::env::current_dir().unwrap(); - let cwd_string = String::from(cwd.to_str().unwrap()) + "/"; - - // Test failure case. - let specifier = "hello.ts"; - let referrer = add_root!("/baddir/badfile.ts"); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - assert!(r.is_err()); - - // Assuming cwd is the deno repo root. - let specifier = "./js/main.ts"; - let referrer = cwd_string.as_str(); - let r = deno_dir.fetch_module_meta_data(specifier, referrer); - assert!(r.is_ok()); - } - - #[test] - fn test_src_file_to_url_1() { - let (_temp_dir, deno_dir) = test_setup(false, false); - assert_eq!("hello", deno_dir.src_file_to_url("hello")); - assert_eq!("/hello", deno_dir.src_file_to_url("/hello")); - let x = deno_dir.deps_http.join("hello/world.txt"); - assert_eq!( - "http://hello/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_2() { - let (_temp_dir, deno_dir) = test_setup(false, false); - assert_eq!("hello", deno_dir.src_file_to_url("hello")); - assert_eq!("/hello", deno_dir.src_file_to_url("/hello")); - let x = deno_dir.deps_https.join("hello/world.txt"); - assert_eq!( - "https://hello/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_3() { - let (_temp_dir, deno_dir) = test_setup(false, false); - let x = deno_dir.deps_http.join("localhost_PORT4545/world.txt"); - assert_eq!( - "http://localhost:4545/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - #[test] - fn test_src_file_to_url_4() { - let (_temp_dir, deno_dir) = test_setup(false, false); - let x = deno_dir.deps_https.join("localhost_PORT4545/world.txt"); - assert_eq!( - "https://localhost:4545/world.txt", - deno_dir.src_file_to_url(x.to_str().unwrap()) - ); - } - - // https://github.com/denoland/deno/blob/golang/os_test.go#L16-L87 - #[test] - fn test_resolve_module_1() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let test_cases = [ - ( - "./subdir/print_hello.ts", - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/006_url_imports.ts"), - file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"), - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/subdir/print_hello.ts"), - ), - ( - "testdata/001_hello.js", - add_root!("/Users/rld/go/src/github.com/denoland/deno/"), - file_url!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"), - add_root!("/Users/rld/go/src/github.com/denoland/deno/testdata/001_hello.js"), - ), - ( - add_root!("/Users/rld/src/deno/hello.js"), - ".", - file_url!("/Users/rld/src/deno/hello.js"), - add_root!("/Users/rld/src/deno/hello.js"), - ), - ( - add_root!("/this/module/got/imported.js"), - add_root!("/that/module/did/it.js"), - file_url!("/this/module/got/imported.js"), - add_root!("/this/module/got/imported.js"), - ), - ]; - for &test in test_cases.iter() { - let specifier = String::from(test.0); - let referrer = String::from(test.1); - let (module_name, filename) = - deno_dir.resolve_module(&specifier, &referrer).unwrap(); - assert_eq!(module_name, test.2); - assert_eq!(filename, test.3); - } - } - - #[test] - fn test_resolve_module_2() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http://localhost:4545/testdata/subdir/print_hello.ts"; - let referrer = add_root!("/deno/testdata/006_url_imports.ts"); - - let expected_module_name = - "http://localhost:4545/testdata/subdir/print_hello.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/testdata/subdir/print_hello.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_3() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier_ = - deno_dir.deps_http.join("unpkg.com/liltest@0.0.5/index.ts"); - let specifier = specifier_.to_str().unwrap(); - let referrer = "."; - - let expected_module_name = "http://unpkg.com/liltest@0.0.5/index.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("unpkg.com/liltest@0.0.5/index.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_4() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "./util"; - let referrer_ = deno_dir.deps_http.join("unpkg.com/liltest@0.0.5/index.ts"); - let referrer = referrer_.to_str().unwrap(); - - // http containing files -> load relative import with http - let expected_module_name = "http://unpkg.com/liltest@0.0.5/util"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("unpkg.com/liltest@0.0.5/util") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_5() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "./util"; - let referrer_ = - deno_dir.deps_https.join("unpkg.com/liltest@0.0.5/index.ts"); - let referrer = referrer_.to_str().unwrap(); - - // https containing files -> load relative import with https - let expected_module_name = "https://unpkg.com/liltest@0.0.5/util"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_https - .join("unpkg.com/liltest@0.0.5/util") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_6() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http://localhost:4545/tests/subdir/mod2.ts"; - let referrer = add_root!("/deno/tests/006_url_imports.ts"); - let expected_module_name = "http://localhost:4545/tests/subdir/mod2.ts"; - let expected_filename = deno_fs::normalize_path( - deno_dir - .deps_http - .join("localhost_PORT4545/tests/subdir/mod2.ts") - .as_ref(), - ); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_7() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "http_test.ts"; - let referrer = add_root!("/Users/rld/src/deno_net/"); - let expected_module_name = - file_url!("/Users/rld/src/deno_net/http_test.ts"); - let expected_filename = add_root!("/Users/rld/src/deno_net/http_test.ts"); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, referrer).unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_referrer_dot() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "tests/001_hello.js"; - - let cwd = std::env::current_dir().unwrap(); - let expected_path = cwd.join(specifier); - let expected_module_name = - Url::from_file_path(&expected_path).unwrap().to_string(); - let expected_filename = deno_fs::normalize_path(&expected_path); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, ".").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "./").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_resolve_module_referrer_dotdot() { - let (_temp_dir, deno_dir) = test_setup(false, false); - - let specifier = "tests/001_hello.js"; - - let cwd = std::env::current_dir().unwrap(); - let expected_path = cwd.join("..").join(specifier); - let expected_module_name = - Url::from_file_path(&expected_path).unwrap().to_string(); - let expected_filename = deno_fs::normalize_path(&expected_path); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "..").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - - let (module_name, filename) = - deno_dir.resolve_module(specifier, "../").unwrap(); - assert_eq!(module_name, expected_module_name); - assert_eq!(filename, expected_filename); - } - - #[test] - fn test_map_file_extension() { - assert_eq!( - map_file_extension(Path::new("foo/bar.ts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.d.ts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.js")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.json")), - msg::MediaType::Json - ); - assert_eq!( - map_file_extension(Path::new("foo/bar.txt")), - msg::MediaType::Unknown - ); - assert_eq!( - map_file_extension(Path::new("foo/bar")), - msg::MediaType::Unknown - ); - } - - #[test] - fn test_map_content_type() { - // Extension only - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), None), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.d.ts"), None), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.js"), None), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.json"), None), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar.txt"), None), - msg::MediaType::Unknown - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), None), - msg::MediaType::Unknown - ); - - // Media Type - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("video/vnd.dlna.mpeg-tts")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("video/mp2t")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/x-typescript")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/ecmascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/ecmascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/x-javascript")), - msg::MediaType::JavaScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("application/json")), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar"), Some("text/json")), - msg::MediaType::Json - ); - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), Some("text/plain")), - msg::MediaType::TypeScript - ); - assert_eq!( - map_content_type(Path::new("foo/bar.ts"), Some("foo/bar")), - msg::MediaType::Unknown - ); - } - - #[test] - fn test_filter_shebang() { - assert_eq!(filter_shebang("#!".as_bytes().to_owned()), "".as_bytes()); - assert_eq!( - filter_shebang("#!\n\n".as_bytes().to_owned()), - "\n\n".as_bytes() - ); - let code = "#!/usr/bin/env deno\nconsole.log('hello');\n" - .as_bytes() - .to_owned(); - assert_eq!(filter_shebang(code), "\nconsole.log('hello');\n".as_bytes()); - } -} diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 65118d070..000000000 --- a/src/errors.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::js_errors::JSErrorColor; -pub use crate::msg::ErrorKind; -use crate::resolve_addr::ResolveAddrError; -use deno_core::JSError; -use hyper; -use std; -use std::fmt; -use std::io; -use url; - -pub type DenoResult<T> = std::result::Result<T, DenoError>; - -#[derive(Debug)] -pub struct DenoError { - repr: Repr, -} - -#[derive(Debug)] -enum Repr { - Simple(ErrorKind, String), - IoErr(io::Error), - UrlErr(url::ParseError), - HyperErr(hyper::Error), -} - -pub fn new(kind: ErrorKind, msg: String) -> DenoError { - DenoError { - repr: Repr::Simple(kind, msg), - } -} - -impl DenoError { - pub fn kind(&self) -> ErrorKind { - match self.repr { - Repr::Simple(kind, ref _msg) => kind, - // Repr::Simple(kind) => kind, - Repr::IoErr(ref err) => { - use std::io::ErrorKind::*; - match err.kind() { - NotFound => ErrorKind::NotFound, - PermissionDenied => ErrorKind::PermissionDenied, - ConnectionRefused => ErrorKind::ConnectionRefused, - ConnectionReset => ErrorKind::ConnectionReset, - ConnectionAborted => ErrorKind::ConnectionAborted, - NotConnected => ErrorKind::NotConnected, - AddrInUse => ErrorKind::AddrInUse, - AddrNotAvailable => ErrorKind::AddrNotAvailable, - BrokenPipe => ErrorKind::BrokenPipe, - AlreadyExists => ErrorKind::AlreadyExists, - WouldBlock => ErrorKind::WouldBlock, - InvalidInput => ErrorKind::InvalidInput, - InvalidData => ErrorKind::InvalidData, - TimedOut => ErrorKind::TimedOut, - Interrupted => ErrorKind::Interrupted, - WriteZero => ErrorKind::WriteZero, - Other => ErrorKind::Other, - UnexpectedEof => ErrorKind::UnexpectedEof, - _ => unreachable!(), - } - } - Repr::UrlErr(ref err) => { - use url::ParseError::*; - match err { - EmptyHost => ErrorKind::EmptyHost, - IdnaError => ErrorKind::IdnaError, - InvalidPort => ErrorKind::InvalidPort, - InvalidIpv4Address => ErrorKind::InvalidIpv4Address, - InvalidIpv6Address => ErrorKind::InvalidIpv6Address, - InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter, - RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase, - RelativeUrlWithCannotBeABaseBase => { - ErrorKind::RelativeUrlWithCannotBeABaseBase - } - SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl, - Overflow => ErrorKind::Overflow, - } - } - Repr::HyperErr(ref err) => { - // For some reason hyper::errors::Kind is private. - if err.is_parse() { - ErrorKind::HttpParse - } else if err.is_user() { - ErrorKind::HttpUser - } else if err.is_canceled() { - ErrorKind::HttpCanceled - } else if err.is_closed() { - ErrorKind::HttpClosed - } else { - ErrorKind::HttpOther - } - } - } - } -} - -impl fmt::Display for DenoError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr { - Repr::Simple(_kind, ref err_str) => f.pad(err_str), - Repr::IoErr(ref err) => err.fmt(f), - Repr::UrlErr(ref err) => err.fmt(f), - Repr::HyperErr(ref err) => err.fmt(f), - } - } -} - -impl std::error::Error for DenoError { - fn description(&self) -> &str { - match self.repr { - Repr::Simple(_kind, ref msg) => msg.as_str(), - Repr::IoErr(ref err) => err.description(), - Repr::UrlErr(ref err) => err.description(), - Repr::HyperErr(ref err) => err.description(), - } - } - - fn cause(&self) -> Option<&dyn std::error::Error> { - match self.repr { - Repr::Simple(_kind, ref _msg) => None, - Repr::IoErr(ref err) => Some(err), - Repr::UrlErr(ref err) => Some(err), - Repr::HyperErr(ref err) => Some(err), - } - } -} - -impl From<io::Error> for DenoError { - #[inline] - fn from(err: io::Error) -> Self { - Self { - repr: Repr::IoErr(err), - } - } -} - -impl From<url::ParseError> for DenoError { - #[inline] - fn from(err: url::ParseError) -> Self { - Self { - repr: Repr::UrlErr(err), - } - } -} - -impl From<hyper::Error> for DenoError { - #[inline] - fn from(err: hyper::Error) -> Self { - Self { - repr: Repr::HyperErr(err), - } - } -} - -impl From<ResolveAddrError> for DenoError { - fn from(e: ResolveAddrError) -> Self { - match e { - ResolveAddrError::Syntax => Self { - repr: Repr::Simple( - ErrorKind::InvalidInput, - "invalid address syntax".to_string(), - ), - }, - ResolveAddrError::Resolution(io_err) => Self { - repr: Repr::IoErr(io_err), - }, - } - } -} - -pub fn bad_resource() -> DenoError { - new(ErrorKind::BadResource, String::from("bad resource id")) -} - -pub fn permission_denied() -> DenoError { - new( - ErrorKind::PermissionDenied, - String::from("permission denied"), - ) -} - -#[derive(Debug)] -pub enum RustOrJsError { - Rust(DenoError), - Js(JSError), -} - -impl From<DenoError> for RustOrJsError { - fn from(e: DenoError) -> Self { - RustOrJsError::Rust(e) - } -} - -impl From<JSError> for RustOrJsError { - fn from(e: JSError) -> Self { - RustOrJsError::Js(e) - } -} - -impl fmt::Display for RustOrJsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RustOrJsError::Rust(e) => e.fmt(f), - RustOrJsError::Js(e) => JSErrorColor(e).fmt(f), - } - } -} diff --git a/src/flags.rs b/src/flags.rs deleted file mode 100644 index d6a63a9fb..000000000 --- a/src/flags.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use deno_core::v8_set_flags; -use getopts; -use getopts::Options; - -// Creates vector of strings, Vec<String> -#[cfg(test)] -macro_rules! svec { - ($($x:expr),*) => (vec![$($x.to_string()),*]); -} - -#[cfg_attr(feature = "cargo-clippy", allow(stutter))] -#[derive(Clone, Debug, PartialEq, Default)] -pub struct DenoFlags { - pub help: bool, - pub log_debug: bool, - pub version: bool, - pub reload: bool, - pub recompile: bool, - pub allow_read: bool, - pub allow_write: bool, - pub allow_net: bool, - pub allow_env: bool, - pub allow_run: bool, - pub no_prompts: bool, - pub types: bool, - pub prefetch: bool, - pub info: bool, - pub fmt: bool, -} - -pub fn get_usage(opts: &Options) -> String { - format!( - "Usage: deno script.ts {} -Environment variables: - DENO_DIR Set deno's base directory - NO_COLOR Set to disable color", - opts.usage("") - ) -} - -/// Checks provided arguments for known options and sets appropriate Deno flags -/// for them. Unknown options are returned for further use. -/// Note: -/// -/// 1. This assumes that privileged flags do not accept parameters deno --foo bar. -/// This assumption is currently valid. But if it were to change in the future, -/// this parsing technique would need to be modified. I think we want to keep the -/// privileged flags minimal - so having this restriction is maybe a good thing. -/// -/// 2. Misspelled flags will be forwarded to user code - e.g. --allow-ne would -/// not cause an error. I also think this is ok because missing any of the -/// privileged flags is not destructive. Userland flag parsing would catch these -/// errors. -fn set_recognized_flags( - opts: &Options, - flags: &mut DenoFlags, - args: Vec<String>, -) -> Result<Vec<String>, getopts::Fail> { - let mut rest = Vec::<String>::new(); - // getopts doesn't allow parsing unknown options so we check them - // one-by-one and handle unrecognized ones manually - // better solution welcome! - for arg in args { - let fake_args = vec![arg]; - match opts.parse(&fake_args) { - Err(getopts::Fail::UnrecognizedOption(_)) => { - rest.extend(fake_args); - } - Err(e) => { - return Err(e); - } - Ok(matches) => { - if matches.opt_present("help") { - flags.help = true; - } - if matches.opt_present("log-debug") { - flags.log_debug = true; - } - if matches.opt_present("version") { - flags.version = true; - } - if matches.opt_present("reload") { - flags.reload = true; - } - if matches.opt_present("recompile") { - flags.recompile = true; - } - if matches.opt_present("allow-read") { - flags.allow_read = true; - } - if matches.opt_present("allow-write") { - flags.allow_write = true; - } - if matches.opt_present("allow-net") { - flags.allow_net = true; - } - if matches.opt_present("allow-env") { - flags.allow_env = true; - } - if matches.opt_present("allow-run") { - flags.allow_run = true; - } - if matches.opt_present("allow-all") { - flags.allow_read = true; - flags.allow_env = true; - flags.allow_net = true; - flags.allow_run = true; - flags.allow_read = true; - flags.allow_write = true; - } - if matches.opt_present("no-prompt") { - flags.no_prompts = true; - } - if matches.opt_present("types") { - flags.types = true; - } - if matches.opt_present("prefetch") { - flags.prefetch = true; - } - if matches.opt_present("info") { - flags.info = true; - } - if matches.opt_present("fmt") { - flags.fmt = true; - } - - if !matches.free.is_empty() { - rest.extend(matches.free); - } - } - } - } - Ok(rest) -} - -#[cfg_attr(feature = "cargo-clippy", allow(stutter))] -pub fn set_flags( - args: Vec<String>, -) -> Result<(DenoFlags, Vec<String>, String), String> { - // TODO: all flags passed after "--" are swallowed by v8_set_flags - // eg. deno --allow-net ./test.ts -- --title foobar - // args === ["deno", "--allow-net" "./test.ts"] - let args = v8_set_flags(args); - - let mut opts = Options::new(); - // TODO(kevinkassimo): v8_set_flags intercepts '-help' with single '-' - // Resolve that and then uncomment line below (enabling Go style -long-flag) - // opts.long_only(true); - opts.optflag("", "allow-read", "Allow file system read access"); - opts.optflag("", "allow-write", "Allow file system write access"); - opts.optflag("", "allow-net", "Allow network access"); - opts.optflag("", "allow-env", "Allow environment access"); - opts.optflag("", "allow-run", "Allow running subprocesses"); - opts.optflag("A", "allow-all", "Allow all permissions"); - opts.optflag("", "no-prompt", "Do not use prompts"); - opts.optflag("", "recompile", "Force recompilation of TypeScript code"); - opts.optflag("h", "help", "Print this message"); - opts.optflag("D", "log-debug", "Log debug output"); - opts.optflag("v", "version", "Print the version"); - opts.optflag("r", "reload", "Reload cached remote resources"); - opts.optflag("", "v8-options", "Print V8 command line options"); - opts.optflag("", "types", "Print runtime TypeScript declarations"); - opts.optflag("", "prefetch", "Prefetch the dependencies"); - opts.optflag("", "info", "Show source file related info"); - opts.optflag("", "fmt", "Format code"); - - let mut flags = DenoFlags::default(); - - let rest = - set_recognized_flags(&opts, &mut flags, args).map_err(|e| e.to_string())?; - Ok((flags, rest, get_usage(&opts))) -} - -#[test] -fn test_set_flags_1() { - let (flags, rest, _) = set_flags(svec!["deno", "--version"]).unwrap(); - assert_eq!(rest, svec!["deno"]); - assert_eq!( - flags, - DenoFlags { - version: true, - ..DenoFlags::default() - } - ); -} - -#[test] -fn test_set_flags_2() { - let (flags, rest, _) = - set_flags(svec!["deno", "-r", "-D", "script.ts"]).unwrap(); - assert_eq!(rest, svec!["deno", "script.ts"]); - assert_eq!( - flags, - DenoFlags { - log_debug: true, - reload: true, - ..DenoFlags::default() - } - ); -} - -#[test] -fn test_set_flags_3() { - let (flags, rest, _) = - set_flags(svec!["deno", "-r", "script.ts", "--allow-write"]).unwrap(); - assert_eq!(rest, svec!["deno", "script.ts"]); - assert_eq!( - flags, - DenoFlags { - reload: true, - allow_write: true, - ..DenoFlags::default() - } - ); -} - -#[test] -fn test_set_flags_4() { - let (flags, rest, _) = - set_flags(svec!["deno", "-Dr", "script.ts", "--allow-write"]).unwrap(); - assert_eq!(rest, svec!["deno", "script.ts"]); - assert_eq!( - flags, - DenoFlags { - log_debug: true, - reload: true, - allow_write: true, - ..DenoFlags::default() - } - ); -} - -#[test] -fn test_set_flags_5() { - let (flags, rest, _) = set_flags(svec!["deno", "--types"]).unwrap(); - assert_eq!(rest, svec!["deno"]); - assert_eq!( - flags, - DenoFlags { - types: true, - ..DenoFlags::default() - } - ) -} - -#[test] -fn test_set_flags_6() { - let (flags, rest, _) = - set_flags(svec!["deno", "gist.ts", "--title", "X", "--allow-net"]).unwrap(); - assert_eq!(rest, svec!["deno", "gist.ts", "--title", "X"]); - assert_eq!( - flags, - DenoFlags { - allow_net: true, - ..DenoFlags::default() - } - ) -} - -#[test] -fn test_set_flags_7() { - let (flags, rest, _) = - set_flags(svec!["deno", "gist.ts", "--allow-all"]).unwrap(); - assert_eq!(rest, svec!["deno", "gist.ts"]); - assert_eq!( - flags, - DenoFlags { - allow_net: true, - allow_env: true, - allow_run: true, - allow_read: true, - allow_write: true, - ..DenoFlags::default() - } - ) -} - -#[test] -fn test_set_flags_8() { - let (flags, rest, _) = - set_flags(svec!["deno", "gist.ts", "--allow-read"]).unwrap(); - assert_eq!(rest, svec!["deno", "gist.ts"]); - assert_eq!( - flags, - DenoFlags { - allow_read: true, - ..DenoFlags::default() - } - ) -} diff --git a/src/fs.rs b/src/fs.rs deleted file mode 100644 index ff0da95e5..000000000 --- a/src/fs.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use std; -use std::fs::{create_dir, DirBuilder, File, OpenOptions}; -use std::io::ErrorKind; -use std::io::Write; -use std::path::{Path, PathBuf}; - -use rand; -use rand::Rng; - -#[cfg(any(unix))] -use std::os::unix::fs::DirBuilderExt; -#[cfg(any(unix))] -use std::os::unix::fs::PermissionsExt; - -pub fn write_file<T: AsRef<[u8]>>( - filename: &Path, - data: T, - perm: u32, -) -> std::io::Result<()> { - write_file_2(filename, data, true, perm, true, false) -} - -pub fn write_file_2<T: AsRef<[u8]>>( - filename: &Path, - data: T, - update_perm: bool, - perm: u32, - is_create: bool, - is_append: bool, -) -> std::io::Result<()> { - let mut file = OpenOptions::new() - .read(false) - .write(true) - .append(is_append) - .truncate(!is_append) - .create(is_create) - .open(filename)?; - - if update_perm { - set_permissions(&mut file, perm)?; - } - - file.write_all(data.as_ref()) -} - -#[cfg(any(unix))] -fn set_permissions(file: &mut File, perm: u32) -> std::io::Result<()> { - debug!("set file perm to {}", perm); - file.set_permissions(PermissionsExt::from_mode(perm & 0o777)) -} -#[cfg(not(any(unix)))] -fn set_permissions(_file: &mut File, _perm: u32) -> std::io::Result<()> { - // NOOP on windows - Ok(()) -} - -pub fn make_temp_dir( - dir: Option<&Path>, - prefix: Option<&str>, - suffix: Option<&str>, -) -> std::io::Result<PathBuf> { - let prefix_ = prefix.unwrap_or(""); - let suffix_ = suffix.unwrap_or(""); - let mut buf: PathBuf = match dir { - Some(ref p) => p.to_path_buf(), - None => std::env::temp_dir(), - }.join("_"); - let mut rng = rand::thread_rng(); - loop { - let unique = rng.gen::<u32>(); - buf.set_file_name(format!("{}{:08x}{}", prefix_, unique, suffix_)); - // TODO: on posix, set mode flags to 0o700. - let r = create_dir(buf.as_path()); - match r { - Err(ref e) if e.kind() == ErrorKind::AlreadyExists => continue, - Ok(_) => return Ok(buf), - Err(e) => return Err(e), - } - } -} - -pub fn mkdir(path: &Path, perm: u32, recursive: bool) -> std::io::Result<()> { - debug!("mkdir -p {}", path.display()); - let mut builder = DirBuilder::new(); - builder.recursive(recursive); - set_dir_permission(&mut builder, perm); - builder.create(path) -} - -#[cfg(any(unix))] -fn set_dir_permission(builder: &mut DirBuilder, perm: u32) { - debug!("set dir perm to {}", perm); - builder.mode(perm & 0o777); -} - -#[cfg(not(any(unix)))] -fn set_dir_permission(_builder: &mut DirBuilder, _perm: u32) { - // NOOP on windows -} - -pub fn normalize_path(path: &Path) -> String { - let s = String::from(path.to_str().unwrap()); - if cfg!(windows) { - // TODO This isn't correct. Probbly should iterate over components. - s.replace("\\", "/") - } else { - s - } -} diff --git a/src/global_timer.rs b/src/global_timer.rs deleted file mode 100644 index eef70ddc2..000000000 --- a/src/global_timer.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -//! This module helps deno implement timers. -//! -//! As an optimization, we want to avoid an expensive calls into rust for every -//! setTimeout in JavaScript. Thus in //js/timers.ts a data structure is -//! implemented that calls into Rust for only the smallest timeout. Thus we -//! only need to be able to start and cancel a single timer (or Delay, as Tokio -//! calls it) for an entire Isolate. This is what is implemented here. - -use crate::tokio_util::panic_on_error; -use futures::Future; -use std::time::Instant; -use tokio::sync::oneshot; -use tokio::timer::Delay; - -pub struct GlobalTimer { - tx: Option<oneshot::Sender<()>>, -} - -impl GlobalTimer { - pub fn new() -> Self { - Self { tx: None } - } - - pub fn cancel(&mut self) { - if let Some(tx) = self.tx.take() { - tx.send(()).ok(); - } - } - - pub fn new_timeout( - &mut self, - deadline: Instant, - ) -> impl Future<Item = (), Error = ()> { - if self.tx.is_some() { - self.cancel(); - } - assert!(self.tx.is_none()); - - let (tx, rx) = oneshot::channel(); - self.tx = Some(tx); - - let delay = panic_on_error(Delay::new(deadline)); - let rx = panic_on_error(rx); - - delay.select(rx).then(|_| Ok(())) - } -} diff --git a/src/http_body.rs b/src/http_body.rs deleted file mode 100644 index 235463ff1..000000000 --- a/src/http_body.rs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -use futures::Async; -use futures::Poll; -use hyper::body::Payload; -use hyper::Body; -use hyper::Chunk; -use std::cmp::min; -use std::io; -use std::io::Read; -use tokio::io::AsyncRead; - -/// Wraps `hyper::Body` so that it can be exposed as an `AsyncRead` and integrated -/// into resources more easily. -pub struct HttpBody { - body: Body, - chunk: Option<Chunk>, - pos: usize, -} - -impl HttpBody { - pub fn from(body: Body) -> Self { - Self { - body, - chunk: None, - pos: 0, - } - } -} - -impl Read for HttpBody { - fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { - unimplemented!(); - } -} - -impl AsyncRead for HttpBody { - fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, io::Error> { - if let Some(chunk) = self.chunk.take() { - debug!( - "HttpBody Fake Read buf {} chunk {} pos {}", - buf.len(), - chunk.len(), - self.pos - ); - let n = min(buf.len(), chunk.len() - self.pos); - { - let rest = &chunk[self.pos..]; - buf[..n].clone_from_slice(&rest[..n]); - } - self.pos += n; - if self.pos == chunk.len() { - self.pos = 0; - } else { - self.chunk = Some(chunk); - } - return Ok(Async::Ready(n)); - } else { - assert_eq!(self.pos, 0); - } - - let p = self.body.poll_data(); - match p { - Err(e) => Err( - // TODO Need to map hyper::Error into std::io::Error. - io::Error::new(io::ErrorKind::Other, e), - ), - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(maybe_chunk)) => match maybe_chunk { - None => Ok(Async::Ready(0)), - Some(chunk) => { - debug!( - "HttpBody Real Read buf {} chunk {} pos {}", - buf.len(), - chunk.len(), - self.pos - ); - let n = min(buf.len(), chunk.len()); - buf[..n].clone_from_slice(&chunk[..n]); - if buf.len() < chunk.len() { - self.pos = n; - self.chunk = Some(chunk); - } - Ok(Async::Ready(n)) - } - }, - } - } -} - -#[test] -fn test_body_async_read() { - use std::str::from_utf8; - let body = Body::from("hello world"); - let mut body = HttpBody::from(body); - - let buf = &mut [0, 0, 0, 0, 0]; - let r = body.poll_read(buf); - assert!(r.is_ok()); - assert_eq!(r.unwrap(), Async::Ready(5)); - assert_eq!(from_utf8(buf).unwrap(), "hello"); - - let r = body.poll_read(buf); - assert!(r.is_ok()); - assert_eq!(r.unwrap(), Async::Ready(5)); - assert_eq!(from_utf8(buf).unwrap(), " worl"); - - let r = body.poll_read(buf); - assert!(r.is_ok()); - assert_eq!(r.unwrap(), Async::Ready(1)); - assert_eq!(from_utf8(&buf[0..1]).unwrap(), "d"); -} diff --git a/src/http_util.rs b/src/http_util.rs deleted file mode 100644 index 8aadbe136..000000000 --- a/src/http_util.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::errors; -use crate::errors::{DenoError, DenoResult}; -use crate::tokio_util; - -use futures::future::{loop_fn, Loop}; -use futures::{future, Future, Stream}; -use hyper; -use hyper::client::{Client, HttpConnector}; -use hyper::header::CONTENT_TYPE; -use hyper::Uri; -use hyper_rustls; - -type Connector = hyper_rustls::HttpsConnector<HttpConnector>; - -lazy_static! { - static ref CONNECTOR: Connector = { - let num_dns_threads = 4; - Connector::new(num_dns_threads) - }; -} - -pub fn get_client() -> Client<Connector, hyper::Body> { - // TODO use Hyper's connection pool. - let c = CONNECTOR.clone(); - Client::builder().build(c) -} - -/// Construct the next uri based on base uri and location header fragment -/// See <https://tools.ietf.org/html/rfc3986#section-4.2> -fn resolve_uri_from_location(base_uri: &Uri, location: &str) -> Uri { - if location.starts_with("http://") || location.starts_with("https://") { - // absolute uri - location - .parse::<Uri>() - .expect("provided redirect url should be a valid url") - } else if location.starts_with("//") { - // "//" authority path-abempty - format!("{}:{}", base_uri.scheme_part().unwrap().as_str(), location) - .parse::<Uri>() - .expect("provided redirect url should be a valid url") - } else if location.starts_with('/') { - // path-absolute - let mut new_uri_parts = base_uri.clone().into_parts(); - new_uri_parts.path_and_query = Some(location.parse().unwrap()); - Uri::from_parts(new_uri_parts).unwrap() - } else { - // assuming path-noscheme | path-empty - let mut new_uri_parts = base_uri.clone().into_parts(); - new_uri_parts.path_and_query = - Some(format!("{}/{}", base_uri.path(), location).parse().unwrap()); - Uri::from_parts(new_uri_parts).unwrap() - } -} - -// The CodeFetch message is used to load HTTP javascript resources and expects a -// synchronous response, this utility method supports that. -pub fn fetch_sync_string(module_name: &str) -> DenoResult<(String, String)> { - let url = module_name.parse::<Uri>().unwrap(); - let client = get_client(); - // TODO(kevinkassimo): consider set a max redirection counter - // to avoid bouncing between 2 or more urls - let fetch_future = loop_fn((client, url), |(client, url)| { - client - .get(url.clone()) - .map_err(DenoError::from) - .and_then(move |response| { - if response.status().is_redirection() { - let location_string = response - .headers() - .get("location") - .expect("url redirection should provide 'location' header") - .to_str() - .unwrap() - .to_string(); - debug!("Redirecting to {}...", &location_string); - let new_url = resolve_uri_from_location(&url, &location_string); - return Ok(Loop::Continue((client, new_url))); - } - if !response.status().is_success() { - return Err(errors::new( - errors::ErrorKind::NotFound, - "module not found".to_string(), - )); - } - Ok(Loop::Break(response)) - }) - }).and_then(|response| { - let content_type = response - .headers() - .get(CONTENT_TYPE) - .map(|content_type| content_type.to_str().unwrap().to_string()); - let body = response - .into_body() - .concat2() - .map(|body| String::from_utf8(body.to_vec()).unwrap()) - .map_err(DenoError::from); - body.join(future::ok(content_type)) - }).and_then(|(body_string, maybe_content_type)| { - future::ok((body_string, maybe_content_type.unwrap())) - }); - - tokio_util::block_on(fetch_future) -} - -#[test] -fn test_fetch_sync_string() { - // Relies on external http server. See tools/http_server.py - tokio_util::init(|| { - let (p, m) = - fetch_sync_string("http://127.0.0.1:4545/package.json").unwrap(); - println!("package.json len {}", p.len()); - assert!(p.len() > 1); - assert!(m == "application/json") - }); -} - -#[test] -fn test_fetch_sync_string_with_redirect() { - // Relies on external http server. See tools/http_server.py - tokio_util::init(|| { - let (p, m) = - fetch_sync_string("http://127.0.0.1:4546/package.json").unwrap(); - println!("package.json len {}", p.len()); - assert!(p.len() > 1); - assert!(m == "application/json") - }); -} - -#[test] -fn test_resolve_uri_from_location_full_1() { - let url = "http://deno.land".parse::<Uri>().unwrap(); - let new_uri = resolve_uri_from_location(&url, "http://golang.org"); - assert_eq!(new_uri.host().unwrap(), "golang.org"); -} - -#[test] -fn test_resolve_uri_from_location_full_2() { - let url = "https://deno.land".parse::<Uri>().unwrap(); - let new_uri = resolve_uri_from_location(&url, "https://golang.org"); - assert_eq!(new_uri.host().unwrap(), "golang.org"); -} - -#[test] -fn test_resolve_uri_from_location_relative_1() { - let url = "http://deno.land/x".parse::<Uri>().unwrap(); - let new_uri = resolve_uri_from_location(&url, "//rust-lang.org/en-US"); - assert_eq!(new_uri.host().unwrap(), "rust-lang.org"); - assert_eq!(new_uri.path(), "/en-US"); -} - -#[test] -fn test_resolve_uri_from_location_relative_2() { - let url = "http://deno.land/x".parse::<Uri>().unwrap(); - let new_uri = resolve_uri_from_location(&url, "/y"); - assert_eq!(new_uri.host().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/y"); -} - -#[test] -fn test_resolve_uri_from_location_relative_3() { - let url = "http://deno.land/x".parse::<Uri>().unwrap(); - let new_uri = resolve_uri_from_location(&url, "z"); - assert_eq!(new_uri.host().unwrap(), "deno.land"); - assert_eq!(new_uri.path(), "/x/z"); -} diff --git a/src/isolate.rs b/src/isolate.rs deleted file mode 100644 index 379203dd3..000000000 --- a/src/isolate.rs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::cli::Cli; -use crate::compiler::compile_sync; -use crate::compiler::ModuleMetaData; -use crate::errors::DenoError; -use crate::errors::RustOrJsError; -use crate::isolate_state::IsolateState; -use crate::js_errors; -use crate::msg; -use deno_core; -use deno_core::deno_mod; -use deno_core::JSError; -use futures::Async; -use futures::Future; -use std::sync::Arc; - -type CoreIsolate = deno_core::Isolate<Cli>; - -/// 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>, -} - -impl Isolate { - pub fn new(cli: Cli) -> Isolate { - let state = cli.state.clone(); - Self { - inner: CoreIsolate::new(cli), - state, - } - } - - /// Same as execute2() but the filename defaults to "<anonymous>". - 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( - &mut self, - js_filename: &str, - js_source: &str, - ) -> Result<(), JSError> { - self.inner.execute(js_filename, js_source) - } - - // TODO(ry) make this return a future. - fn mod_load_deps(&self, id: deno_mod) -> Result<(), RustOrJsError> { - // basically iterate over the imports, start loading them. - - 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) - .map_err(DenoError::from) - .map_err(RustOrJsError::from)?; - - debug!("mod_load_deps {}", name); - - if !self.state.modules.lock().unwrap().is_registered(&name) { - let out = fetch_module_meta_data_and_maybe_compile( - &self.state, - &specifier, - &referrer_name, - )?; - let child_id = self.mod_new_and_register( - false, - &out.module_name.clone(), - &out.js_source(), - )?; - - self.mod_load_deps(child_id)?; - } - } - - Ok(()) - } - - /// Executes the provided JavaScript module. - pub fn execute_mod( - &mut self, - js_filename: &str, - is_prefetch: bool, - ) -> Result<(), RustOrJsError> { - // 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_and_register(true, &out.module_name.clone(), &out.js_source()) - .map_err(RustOrJsError::from)?; - - self.mod_load_deps(id)?; - - self - .inner - .mod_instantiate(id) - .map_err(RustOrJsError::from)?; - if !is_prefetch { - self.inner.mod_evaluate(id).map_err(RustOrJsError::from)?; - } - Ok(()) - } - - /// 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) - } - - 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()); - } - - /// Applies source map to the error. - fn apply_source_map(&self, err: JSError) -> JSError { - js_errors::apply_source_map(&err, &self.state.dir) - } -} - -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)) - } -} - -fn fetch_module_meta_data_and_maybe_compile( - state: &Arc<IsolateState>, - specifier: &str, - referrer: &str, -) -> Result<ModuleMetaData, DenoError> { - let mut out = state.dir.fetch_module_meta_data(specifier, referrer)?; - if (out.media_type == msg::MediaType::TypeScript - && out.maybe_output_code.is_none()) - || state.flags.recompile - { - debug!(">>>>> compile_sync START"); - out = compile_sync(state, specifier, &referrer, &out); - debug!(">>>>> compile_sync END"); - state.dir.code_cache(&out)?; - } - Ok(out) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::flags; - 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().to_string(); - - 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(); - tokio_util::run(lazy(move || { - let cli = Cli::new(None, 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().to_string(); - - 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(); - tokio_util::run(lazy(move || { - let cli = Cli::new(None, 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); - } -} diff --git a/src/isolate_state.rs b/src/isolate_state.rs deleted file mode 100644 index 4cc010389..000000000 --- a/src/isolate_state.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::cli::Buf; -use crate::deno_dir; -use crate::flags; -use crate::global_timer::GlobalTimer; -use crate::modules::Modules; -use futures::sync::mpsc as async_mpsc; -use std; -use std::env; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Mutex; - -pub type WorkerSender = async_mpsc::Sender<Buf>; -pub type WorkerReceiver = async_mpsc::Receiver<Buf>; -pub type WorkerChannels = (WorkerSender, WorkerReceiver); - -// 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, -} - -// 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 modules: Mutex<Modules>, - 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(), - modules: Mutex::new(Modules::new()), - 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 - } - } - } - } - - #[cfg(test)] - pub fn mock() -> 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(); - IsolateState::new(flags, rest_argv, None) - } - - pub 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); - } - - pub 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); - } -} diff --git a/src/js_errors.rs b/src/js_errors.rs deleted file mode 100644 index 90c9f2007..000000000 --- a/src/js_errors.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -//! This mod adds source maps and ANSI color display to deno_core::JSError. -use crate::ansi; -use deno_core::JSError; -use deno_core::StackFrame; -use source_map_mappings::parse_mappings; -use source_map_mappings::Bias; -use source_map_mappings::Mappings; -use std::collections::HashMap; -use std::fmt; -use std::str; - -/// Wrapper around JSError which provides color to_string. -pub struct JSErrorColor<'a>(pub &'a JSError); - -struct StackFrameColor<'a>(&'a StackFrame); - -pub trait SourceMapGetter { - /// Returns the raw source map file. - fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>>; -} - -/// Cached filename lookups. The key can be None if a previous lookup failed to -/// find a SourceMap. -type CachedMaps = HashMap<String, Option<SourceMap>>; - -struct SourceMap { - mappings: Mappings, - sources: Vec<String>, -} - -impl<'a> fmt::Display for StackFrameColor<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let frame = self.0; - // Note when we print to string, we change from 0-indexed to 1-indexed. - let function_name = ansi::italic_bold(frame.function_name.clone()); - let script_line_column = - format_script_line_column(&frame.script_name, frame.line, frame.column); - - if !frame.function_name.is_empty() { - write!(f, " at {} ({})", function_name, script_line_column) - } else if frame.is_eval { - write!(f, " at eval ({})", script_line_column) - } else { - write!(f, " at {}", script_line_column) - } - } -} - -fn format_script_line_column( - script_name: &str, - line: i64, - column: i64, -) -> String { - // TODO match this style with how typescript displays errors. - let line = ansi::yellow((1 + line).to_string()); - let column = ansi::yellow((1 + column).to_string()); - let script_name = ansi::cyan(script_name.to_string()); - format!("{}:{}:{}", script_name, line, column) -} - -impl<'a> fmt::Display for JSErrorColor<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let e = self.0; - if e.script_resource_name.is_some() { - let script_resource_name = e.script_resource_name.as_ref().unwrap(); - // Avoid showing internal code from gen/bundle/main.js - if script_resource_name != "gen/bundle/main.js" - && script_resource_name != "gen/bundle/compiler.js" - { - if e.line_number.is_some() && e.start_column.is_some() { - assert!(e.line_number.is_some()); - assert!(e.start_column.is_some()); - let script_line_column = format_script_line_column( - script_resource_name, - e.line_number.unwrap() - 1, - e.start_column.unwrap() - 1, - ); - write!(f, "{}", script_line_column)?; - } - if e.source_line.is_some() { - write!(f, "\n{}\n", e.source_line.as_ref().unwrap())?; - let mut s = String::new(); - for i in 0..e.end_column.unwrap() { - if i >= e.start_column.unwrap() { - s.push('^'); - } else { - s.push(' '); - } - } - writeln!(f, "{}", ansi::red_bold(s))?; - } - } - } - - write!(f, "{}", ansi::bold(e.message.clone()))?; - - for frame in &e.frames { - write!(f, "\n{}", StackFrameColor(&frame).to_string())?; - } - Ok(()) - } -} - -impl SourceMap { - fn from_json(json_str: &str) -> Option<Self> { - // Ugly. Maybe use serde_derive. - match serde_json::from_str::<serde_json::Value>(json_str) { - Ok(serde_json::Value::Object(map)) => match map["mappings"].as_str() { - None => None, - Some(mappings_str) => { - match parse_mappings::<()>(mappings_str.as_bytes()) { - Err(_) => None, - Ok(mappings) => { - if !map["sources"].is_array() { - return None; - } - let sources_val = map["sources"].as_array().unwrap(); - let mut sources = Vec::<String>::new(); - - for source_val in sources_val { - match source_val.as_str() { - None => return None, - Some(source) => { - sources.push(source.to_string()); - } - } - } - - Some(SourceMap { sources, mappings }) - } - } - } - }, - _ => None, - } - } -} - -fn frame_apply_source_map( - frame: &StackFrame, - mappings_map: &mut CachedMaps, - getter: &dyn SourceMapGetter, -) -> StackFrame { - let maybe_sm = get_mappings(frame.script_name.as_ref(), mappings_map, getter); - let frame_pos = ( - frame.script_name.to_owned(), - frame.line as i64, - frame.column as i64, - ); - let (script_name, line, column) = match maybe_sm { - None => frame_pos, - Some(sm) => match sm.mappings.original_location_for( - frame.line as u32, - frame.column as u32, - Bias::default(), - ) { - None => frame_pos, - Some(mapping) => match &mapping.original { - None => frame_pos, - Some(original) => { - let orig_source = sm.sources[original.source as usize].clone(); - ( - orig_source, - i64::from(original.original_line), - i64::from(original.original_column), - ) - } - }, - }, - }; - - StackFrame { - script_name, - function_name: frame.function_name.clone(), - line, - column, - is_eval: frame.is_eval, - is_constructor: frame.is_constructor, - is_wasm: frame.is_wasm, - } -} - -pub fn apply_source_map( - js_error: &JSError, - getter: &dyn SourceMapGetter, -) -> JSError { - let mut mappings_map: CachedMaps = HashMap::new(); - let mut frames = Vec::<StackFrame>::new(); - for frame in &js_error.frames { - let f = frame_apply_source_map(&frame, &mut mappings_map, getter); - frames.push(f); - } - JSError { - message: js_error.message.clone(), - frames, - error_level: js_error.error_level, - source_line: js_error.source_line.clone(), - // TODO the following need to be source mapped: - script_resource_name: js_error.script_resource_name.clone(), - line_number: js_error.line_number, - start_position: js_error.start_position, - end_position: js_error.end_position, - start_column: js_error.start_column, - end_column: js_error.end_column, - } -} - -// The bundle does not get built for 'cargo check', so we don't embed the -// bundle source map. -#[cfg(feature = "check-only")] -fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> { - None -} - -#[cfg(not(feature = "check-only"))] -fn builtin_source_map(script_name: &str) -> Option<Vec<u8>> { - match script_name { - "gen/bundle/main.js" => Some( - include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/main.js.map")) - .to_vec(), - ), - "gen/bundle/compiler.js" => Some( - include_bytes!(concat!( - env!("GN_OUT_DIR"), - "/gen/bundle/compiler.js.map" - )).to_vec(), - ), - _ => None, - } -} - -fn parse_map_string( - script_name: &str, - getter: &dyn SourceMapGetter, -) -> Option<SourceMap> { - builtin_source_map(script_name) - .or_else(|| getter.get_source_map(script_name)) - .and_then(|raw_source_map| { - SourceMap::from_json(str::from_utf8(&raw_source_map).unwrap()) - }) -} - -fn get_mappings<'a>( - script_name: &str, - mappings_map: &'a mut CachedMaps, - getter: &dyn SourceMapGetter, -) -> &'a Option<SourceMap> { - mappings_map - .entry(script_name.to_string()) - .or_insert_with(|| parse_map_string(script_name, getter)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::ansi::strip_ansi_codes; - - fn error1() -> JSError { - JSError { - message: "Error: foo bar".to_string(), - source_line: None, - script_resource_name: None, - line_number: None, - start_position: None, - end_position: None, - error_level: None, - start_column: None, - end_column: None, - frames: vec![ - StackFrame { - line: 4, - column: 16, - script_name: "foo_bar.ts".to_string(), - function_name: "foo".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - StackFrame { - line: 5, - column: 20, - script_name: "bar_baz.ts".to_string(), - function_name: "qat".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - StackFrame { - line: 1, - column: 1, - script_name: "deno_main.js".to_string(), - function_name: "".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - ], - } - } - - struct MockSourceMapGetter {} - - impl SourceMapGetter for MockSourceMapGetter { - fn get_source_map(&self, script_name: &str) -> Option<Vec<u8>> { - let s = match script_name { - "foo_bar.ts" => r#"{"sources": ["foo_bar.ts"], "mappings":";;;IAIA,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#, - "bar_baz.ts" => r#"{"sources": ["bar_baz.ts"], "mappings":";;;IAEA,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,GAAG,GAAG,sDAAa,OAAO,2BAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,EAAE,CAAC;IAEQ,QAAA,GAAG,GAAG,KAAK,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}"#, - _ => return None, - }; - Some(s.as_bytes().to_owned()) - } - } - - #[test] - fn js_error_to_string() { - let e = error1(); - assert_eq!("Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2", strip_ansi_codes(&e.to_string())); - } - - #[test] - fn js_error_apply_source_map_1() { - let e = error1(); - let getter = MockSourceMapGetter {}; - let actual = apply_source_map(&e, &getter); - let expected = JSError { - message: "Error: foo bar".to_string(), - source_line: None, - script_resource_name: None, - line_number: None, - start_position: None, - end_position: None, - error_level: None, - start_column: None, - end_column: None, - frames: vec![ - StackFrame { - line: 5, - column: 12, - script_name: "foo_bar.ts".to_string(), - function_name: "foo".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - StackFrame { - line: 4, - column: 14, - script_name: "bar_baz.ts".to_string(), - function_name: "qat".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - StackFrame { - line: 1, - column: 1, - script_name: "deno_main.js".to_string(), - function_name: "".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }, - ], - }; - assert_eq!(actual, expected); - } - - #[test] - fn js_error_apply_source_map_2() { - let e = JSError { - message: "TypeError: baz".to_string(), - source_line: None, - script_resource_name: None, - line_number: None, - start_position: None, - end_position: None, - error_level: None, - start_column: None, - end_column: None, - frames: vec![StackFrame { - line: 11, - column: 12, - script_name: "gen/bundle/main.js".to_string(), - function_name: "setLogDebug".to_string(), - is_eval: false, - is_constructor: false, - is_wasm: false, - }], - }; - let getter = MockSourceMapGetter {}; - let actual = apply_source_map(&e, &getter); - assert_eq!(actual.message, "TypeError: baz"); - // Because this is accessing the live bundle, this test might be more fragile - assert_eq!(actual.frames.len(), 1); - assert!(actual.frames[0].script_name.ends_with("js/util.ts")); - } - - #[test] - fn source_map_from_json() { - let json = r#"{"version":3,"file":"error_001.js","sourceRoot":"","sources":["file:///Users/rld/src/deno/tests/error_001.ts"],"names":[],"mappings":"AAAA,SAAS,GAAG;IACV,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,GAAG;IACV,GAAG,EAAE,CAAC;AACR,CAAC;AAED,GAAG,EAAE,CAAC"}"#; - let sm = SourceMap::from_json(json).unwrap(); - assert_eq!(sm.sources.len(), 1); - assert_eq!( - sm.sources[0], - "file:///Users/rld/src/deno/tests/error_001.ts" - ); - let mapping = sm - .mappings - .original_location_for(1, 10, Bias::default()) - .unwrap(); - assert_eq!(mapping.generated_line, 1); - assert_eq!(mapping.generated_column, 10); - assert_eq!( - mapping.original, - Some(source_map_mappings::OriginalLocation { - source: 0, - original_line: 1, - original_column: 8, - name: None - }) - ); - } -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 4657a3a4d..000000000 --- a/src/main.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; -#[macro_use] -extern crate futures; -#[macro_use] -extern crate serde_json; - -mod ansi; -pub mod cli; -pub mod compiler; -pub mod deno_dir; -pub mod errors; -pub mod flags; -mod fs; -mod global_timer; -mod http_body; -mod http_util; -pub mod isolate; -pub mod isolate_state; -pub mod js_errors; -pub mod modules; -pub mod msg; -pub mod msg_util; -pub mod ops; -pub mod permissions; -mod repl; -pub mod resolve_addr; -pub mod resources; -mod startup_data; -mod tokio_util; -mod tokio_write; -pub mod version; -pub mod workers; - -use crate::cli::Cli; -use crate::errors::RustOrJsError; -use crate::isolate::Isolate; -use crate::isolate_state::IsolateState; -use futures::lazy; -use futures::Future; -use log::{LevelFilter, Metadata, Record}; -use std::env; -use std::sync::Arc; - -static LOGGER: Logger = Logger; - -struct Logger; - -impl log::Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= log::max_level() - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - println!("{} RS - {}", record.level(), record.args()); - } - } - fn flush(&self) {} -} - -fn print_err_and_exit(err: RustOrJsError) { - eprintln!("{}", err.to_string()); - std::process::exit(1); -} - -fn js_check<E>(r: Result<(), E>) -where - E: Into<RustOrJsError>, -{ - if let Err(err) = r { - print_err_and_exit(err.into()); - } -} - -fn main() { - #[cfg(windows)] - ansi_term::enable_ansi_support().ok(); // For Windows 10 - - log::set_logger(&LOGGER).unwrap(); - let args = env::args().collect(); - let (mut flags, mut rest_argv, usage_string) = flags::set_flags(args) - .unwrap_or_else(|err| { - eprintln!("{}", err); - std::process::exit(1) - }); - - if flags.help { - println!("{}", &usage_string); - std::process::exit(0); - } - - log::set_max_level(if flags.log_debug { - LevelFilter::Debug - } else { - LevelFilter::Warn - }); - - if flags.fmt { - rest_argv.insert(1, "https://deno.land/std/prettier/main.ts".to_string()); - flags.allow_read = true; - flags.allow_write = true; - } - - let should_prefetch = flags.prefetch || flags.info; - let should_display_info = flags.info; - - let state = Arc::new(IsolateState::new(flags, rest_argv, None)); - let state_ = state.clone(); - let startup_data = startup_data::deno_isolate_init(); - let permissions = permissions::DenoPermissions::from_flags(&state.flags); - let cli = Cli::new(Some(startup_data), state_, permissions); - let mut isolate = Isolate::new(cli); - - let main_future = lazy(move || { - // Setup runtime. - js_check(isolate.execute("denoMain()")); - - // Execute main module. - if let Some(main_module) = state.main_module() { - debug!("main_module {}", main_module); - js_check(isolate.execute_mod(&main_module, should_prefetch)); - if should_display_info { - // Display file info and exit. Do not run file - isolate.print_file_info(&main_module); - std::process::exit(0); - } - } - - isolate.then(|result| { - js_check(result); - Ok(()) - }) - }); - - tokio_util::run(main_future); -} diff --git a/src/modules.rs b/src/modules.rs deleted file mode 100644 index 908c31b6d..000000000 --- a/src/modules.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::ansi; -use crate::deno_dir::DenoDir; -use crate::msg; -use deno_core::deno_mod; -use std::collections::HashMap; -use std::collections::HashSet; -use std::fmt; - -pub struct ModuleInfo { - name: String, - children: Vec<deno_mod>, -} - -/// A collection of JS modules. -#[derive(Default)] -pub struct Modules { - pub info: HashMap<deno_mod, ModuleInfo>, - pub by_name: HashMap<String, deno_mod>, -} - -impl Modules { - pub fn new() -> Modules { - Self { - info: HashMap::new(), - by_name: HashMap::new(), - } - } - - pub fn get_id(&self, name: &str) -> Option<deno_mod> { - self.by_name.get(name).cloned() - } - - pub fn get_children(&self, id: deno_mod) -> Option<&Vec<deno_mod>> { - self.info.get(&id).map(|i| &i.children) - } - - pub fn get_name(&self, id: deno_mod) -> Option<&String> { - self.info.get(&id).map(|i| &i.name) - } - - pub fn is_registered(&self, name: &str) -> bool { - self.by_name.get(name).is_some() - } - - pub fn register(&mut self, id: deno_mod, name: &str) { - let name = String::from(name); - debug!("register {}", name); - self.by_name.insert(name.clone(), id); - self.info.insert( - id, - ModuleInfo { - name, - children: Vec::new(), - }, - ); - } - - pub fn resolve_cb( - &mut self, - deno_dir: &DenoDir, - specifier: &str, - referrer: deno_mod, - ) -> deno_mod { - debug!("resolve_cb {}", specifier); - - let maybe_info = self.info.get_mut(&referrer); - if maybe_info.is_none() { - debug!("cant find referrer {}", referrer); - return 0; - } - let info = maybe_info.unwrap(); - let referrer_name = &info.name; - let r = deno_dir.resolve_module(specifier, referrer_name); - if let Err(err) = r { - debug!("potentially swallowed err: {}", err); - return 0; - } - let (name, _local_filename) = r.unwrap(); - - if let Some(id) = self.by_name.get(&name) { - let child_id = *id; - info.children.push(child_id); - return child_id; - } else { - return 0; - } - } - - pub fn print_file_info(&self, deno_dir: &DenoDir, filename: String) { - let maybe_out = deno_dir.fetch_module_meta_data(&filename, "."); - if maybe_out.is_err() { - println!("{}", maybe_out.unwrap_err()); - return; - } - let out = maybe_out.unwrap(); - - println!("{} {}", ansi::bold("local:".to_string()), &(out.filename)); - println!( - "{} {}", - ansi::bold("type:".to_string()), - msg::enum_name_media_type(out.media_type) - ); - if out.maybe_output_code_filename.is_some() { - println!( - "{} {}", - ansi::bold("compiled:".to_string()), - out.maybe_output_code_filename.as_ref().unwrap(), - ); - } - if out.maybe_source_map_filename.is_some() { - println!( - "{} {}", - ansi::bold("map:".to_string()), - out.maybe_source_map_filename.as_ref().unwrap() - ); - } - - let deps = Deps::new(self, &out.module_name); - println!("{}{}", ansi::bold("deps:\n".to_string()), deps.name); - if let Some(ref depsdeps) = deps.deps { - for d in depsdeps { - println!("{}", d); - } - } - } -} - -pub struct Deps { - pub name: String, - pub deps: Option<Vec<Deps>>, - prefix: String, - is_last: bool, -} - -impl Deps { - pub fn new(modules: &Modules, module_name: &str) -> Deps { - let mut seen = HashSet::new(); - let id = modules.get_id(module_name).unwrap(); - Self::helper(&mut seen, "".to_string(), true, modules, id) - } - - fn helper( - seen: &mut HashSet<deno_mod>, - prefix: String, - is_last: bool, - modules: &Modules, - id: deno_mod, - ) -> Deps { - let name = modules.get_name(id).unwrap().to_string(); - if seen.contains(&id) { - Deps { - name, - prefix, - deps: None, - is_last, - } - } else { - seen.insert(id); - let child_ids = modules.get_children(id).unwrap(); - let child_count = child_ids.iter().count(); - let deps = child_ids - .iter() - .enumerate() - .map(|(index, dep_id)| { - let new_is_last = index == child_count - 1; - let mut new_prefix = prefix.clone(); - new_prefix.push(if is_last { ' ' } else { '│' }); - new_prefix.push(' '); - Self::helper(seen, new_prefix, new_is_last, modules, *dep_id) - }).collect(); - Deps { - name, - prefix, - deps: Some(deps), - is_last, - } - } - } -} - -impl fmt::Display for Deps { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut has_children = false; - if let Some(ref deps) = self.deps { - has_children = !deps.is_empty(); - } - write!( - f, - "{}{}─{} {}", - self.prefix, - if self.is_last { "└" } else { "├" }, - if has_children { "┬" } else { "─" }, - self.name - )?; - - if let Some(ref deps) = self.deps { - for d in deps { - write!(f, "\n{}", d)?; - } - } - Ok(()) - } -} diff --git a/src/msg.fbs b/src/msg.fbs deleted file mode 100644 index 243034cfb..000000000 --- a/src/msg.fbs +++ /dev/null @@ -1,524 +0,0 @@ -union Any { - Accept, - Chdir, - Chmod, - Close, - CopyFile, - Cwd, - CwdRes, - Dial, - Environ, - EnvironRes, - Exit, - Fetch, - FetchModuleMetaData, - FetchModuleMetaDataRes, - FetchRes, - FormatError, - FormatErrorRes, - GlobalTimer, - GlobalTimerRes, - GlobalTimerStop, - IsTTY, - IsTTYRes, - Listen, - ListenRes, - MakeTempDir, - MakeTempDirRes, - Metrics, - MetricsRes, - Mkdir, - NewConn, - Now, - NowRes, - Open, - OpenRes, - PermissionRevoke, - Permissions, - PermissionsRes, - Read, - ReadDir, - ReadDirRes, - ReadFile, - ReadFileRes, - ReadRes, - Readlink, - ReadlinkRes, - Remove, - Rename, - ReplReadline, - ReplReadlineRes, - ReplStart, - ReplStartRes, - Resources, - ResourcesRes, - Run, - RunRes, - RunStatus, - RunStatusRes, - Seek, - SetEnv, - Shutdown, - Start, - StartRes, - Stat, - StatRes, - Symlink, - Truncate, - WorkerGetMessage, - WorkerGetMessageRes, - WorkerPostMessage, - Write, - WriteFile, - WriteRes, -} - -enum ErrorKind: byte { - NoError = 0, - - // io errors - - NotFound, - PermissionDenied, - ConnectionRefused, - ConnectionReset, - ConnectionAborted, - NotConnected, - AddrInUse, - AddrNotAvailable, - BrokenPipe, - AlreadyExists, - WouldBlock, - InvalidInput, - InvalidData, - TimedOut, - Interrupted, - WriteZero, - Other, - UnexpectedEof, - BadResource, - CommandFailed, - - // url errors - - EmptyHost, - IdnaError, - InvalidPort, - InvalidIpv4Address, - InvalidIpv6Address, - InvalidDomainCharacter, - RelativeUrlWithoutBase, - RelativeUrlWithCannotBeABaseBase, - SetHostOnCannotBeABaseUrl, - Overflow, - - // hyper errors - - HttpUser, - HttpClosed, - HttpCanceled, - HttpParse, - HttpOther, - TooLarge, - - // custom errors - InvalidUri, - InvalidSeekMode, -} - -table Cwd {} - -table CwdRes { - cwd: string; -} - -enum MediaType: byte { - JavaScript = 0, - TypeScript, - Json, - Unknown -} - -table Base { - cmd_id: uint32; - sync: bool = false; - error_kind: ErrorKind = NoError; - error: string; - inner: Any; -} - -table Start { - unused: int8; -} - -table StartRes { - cwd: string; - pid: uint32; - argv: [string]; - exec_path: string; - main_module: string; // Absolute URL. - debug_flag: bool; - deps_flag: bool; - types_flag: bool; - version_flag: bool; - deno_version: string; - v8_version: string; - no_color: bool; -} - -table FormatError { - error: string; -} - -table FormatErrorRes { - error: string; -} - -table WorkerGetMessage { - unused: int8; -} - -table WorkerGetMessageRes { - data: [ubyte]; -} - -table WorkerPostMessage { - // data passed thru the zero-copy data parameter. -} - -table FetchModuleMetaData { - specifier: string; - referrer: string; -} - -table FetchModuleMetaDataRes { - // If it's a non-http module, moduleName and filename will be the same. - // For http modules, moduleName is its resolved http URL, and filename - // is the location of the locally downloaded source code. - module_name: string; - filename: string; - media_type: MediaType; - data: [ubyte]; -} - -table Chdir { - directory: string; -} - -table GlobalTimer { - timeout: int; -} - -table GlobalTimerRes { } - -table GlobalTimerStop { } - -table Exit { - code: int; -} - -table Environ {} - -table SetEnv { - key: string; - value: string; -} - -table EnvironRes { - map: [KeyValue]; -} - -table KeyValue { - key: string; - value: string; -} - -table Permissions {} - -table PermissionRevoke { - permission: string; -} - -table PermissionsRes { - run: bool; - read: bool; - write: bool; - net: bool; - env: bool; -} - -// Note this represents The WHOLE header of an http message, not just the key -// value pairs. That means it includes method and url for Requests and status -// for responses. This is why it is singular "Header" instead of "Headers". -table HttpHeader { - is_request: bool; - // Request only: - method: string; - url: string; - // Response only: - status: uint16; - // Both: - fields: [KeyValue]; -} - -table Fetch { - header: HttpHeader; -} - -table FetchRes { - header: HttpHeader; - body_rid: uint32; -} - -table MakeTempDir { - dir: string; - prefix: string; - suffix: string; -} - -table MakeTempDirRes { - path: string; -} - -table Mkdir { - path: string; - recursive: bool; - mode: uint; // Specified by https://godoc.org/os#FileMode -} - -table Chmod { - path: string; - mode: uint; // Specified by https://godoc.org/os#FileMode -} - -table Remove { - path: string; - recursive: bool; -} - -table ReadFile { - filename: string; -} - -table ReadFileRes { - data: [ubyte]; -} - -table ReadDir { - path: string; -} - -table ReadDirRes { - entries: [StatRes]; -} - -table WriteFile { - filename: string; - data: [ubyte]; - update_perm: bool; - perm: uint; - // perm specified by https://godoc.org/os#FileMode - is_create: bool; - is_append: bool; -} - -table CopyFile { - from: string; - to: string; -} - -table Rename { - oldpath: string; - newpath: string; -} - -table Readlink { - name: string; -} - -table ReadlinkRes { - path: string; -} - -table ReplStart { - history_file: string; - // TODO add config -} - -table ReplStartRes { - rid: uint32; -} - -table ReplReadline { - rid: uint32; - prompt: string; -} - -table ReplReadlineRes { - line: string; -} - -table Resources {} - -table Resource { - rid: uint32; - repr: string; -} - -table ResourcesRes { - resources: [Resource]; -} - -table Symlink { - oldname: string; - newname: string; -} - -table Stat { - filename: string; - lstat: bool; -} - -table StatRes { - is_file: bool; - is_symlink: bool; - len: ulong; - modified:ulong; - accessed:ulong; - created:ulong; - mode: uint; - has_mode: bool; // false on windows - name: string; - path: string; -} - -table Truncate { - name: string; - len: uint; -} - -table Open { - filename: string; - perm: uint; - mode: string; -} - -table OpenRes { - rid: uint32; -} - -table Read { - rid: uint32; - // (ptr, len) is passed as second parameter to libdeno.send(). -} - -table ReadRes { - nread: uint; - eof: bool; -} - -table Write { - rid: uint32; -} - -table WriteRes { - nbyte: uint; -} - -table Close { - rid: uint32; -} - -table Shutdown { - rid: uint32; - how: uint; -} - -table Listen { - network: string; - address: string; -} - -table ListenRes { - rid: uint32; -} - -table Accept { - rid: uint32; -} - -table Dial { - network: string; - address: string; -} - -// Response to Accept and Dial. -table NewConn { - rid: uint32; - remote_addr: string; - local_addr: string; -} - -table Metrics {} - -table MetricsRes { - ops_dispatched: uint64; - ops_completed: uint64; - bytes_sent_control: uint64; - bytes_sent_data: uint64; - bytes_received: uint64; -} - -enum ProcessStdio: byte { Inherit, Piped, Null } - -table Run { - args: [string]; - cwd: string; - env: [KeyValue]; - stdin: ProcessStdio; - stdout: ProcessStdio; - stderr: ProcessStdio; -} - -table RunRes { - rid: uint32; - pid: uint32; - // The following stdio rids are only valid if "Piped" was specified for the - // corresponding stdio stream. The caller MUST issue a close op for all valid - // stdio streams. - stdin_rid: uint32; - stdout_rid: uint32; - stderr_rid: uint32; -} - -table RunStatus { - rid: uint32; -} - -table RunStatusRes { - got_signal: bool; - exit_code: int; - exit_signal: int; -} - -table Now {} - -table NowRes { - time: uint64; -} - -table IsTTY {} - -table IsTTYRes { - stdin: bool; - stdout: bool; - stderr: bool; -} - -table Seek { - rid: uint32; - offset: int; - whence: uint; -} - -root_type Base; diff --git a/src/msg.rs b/src/msg.rs deleted file mode 100644 index 080f39de8..000000000 --- a/src/msg.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -#![allow(unused_imports)] -#![allow(dead_code)] -#![cfg_attr( - feature = "cargo-clippy", - allow(clippy::all, clippy::pedantic) -)] -use crate::isolate_state; -use flatbuffers; -use std::sync::atomic::Ordering; - -// GN_OUT_DIR is set either by build.rs (for the Cargo build), or by -// build_extra/rust/run.py (for the GN+Ninja build). -include!(concat!(env!("GN_OUT_DIR"), "/gen/msg_generated.rs")); - -impl<'a> From<&'a isolate_state::Metrics> for MetricsResArgs { - fn from(m: &'a isolate_state::Metrics) -> Self { - MetricsResArgs { - ops_dispatched: m.ops_dispatched.load(Ordering::SeqCst) as u64, - ops_completed: m.ops_completed.load(Ordering::SeqCst) as u64, - bytes_sent_control: m.bytes_sent_control.load(Ordering::SeqCst) as u64, - bytes_sent_data: m.bytes_sent_data.load(Ordering::SeqCst) as u64, - bytes_received: m.bytes_received.load(Ordering::SeqCst) as u64, - } - } -} diff --git a/src/msg_util.rs b/src/msg_util.rs deleted file mode 100644 index 71bcc19d9..000000000 --- a/src/msg_util.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// Helpers for serialization. -use crate::errors; -use crate::errors::DenoResult; -use crate::msg; - -use flatbuffers; -use http::header::HeaderName; -use http::uri::Uri; -use http::Method; -use hyper::header::HeaderMap; -use hyper::header::HeaderValue; -use hyper::Body; -use hyper::Request; -use hyper::Response; -use std::str::FromStr; - -type Headers = HeaderMap<HeaderValue>; - -pub fn serialize_key_value<'bldr>( - builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, - key: &str, - value: &str, -) -> flatbuffers::WIPOffset<msg::KeyValue<'bldr>> { - let key = builder.create_string(&key); - let value = builder.create_string(&value); - msg::KeyValue::create( - builder, - &msg::KeyValueArgs { - key: Some(key), - value: Some(value), - }, - ) -} - -pub fn serialize_request_header<'bldr>( - builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, - r: &Request<Body>, -) -> flatbuffers::WIPOffset<msg::HttpHeader<'bldr>> { - let method = builder.create_string(r.method().as_str()); - let url = builder.create_string(r.uri().to_string().as_ref()); - - let mut fields = Vec::new(); - for (key, val) in r.headers().iter() { - let kv = serialize_key_value(builder, key.as_ref(), val.to_str().unwrap()); - fields.push(kv); - } - let fields = builder.create_vector(fields.as_ref()); - - msg::HttpHeader::create( - builder, - &msg::HttpHeaderArgs { - is_request: true, - method: Some(method), - url: Some(url), - fields: Some(fields), - ..Default::default() - }, - ) -} - -pub fn serialize_fields<'bldr>( - builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, - headers: &Headers, -) -> flatbuffers::WIPOffset< - flatbuffers::Vector< - 'bldr, - flatbuffers::ForwardsUOffset<msg::KeyValue<'bldr>>, - >, -> { - let mut fields = Vec::new(); - for (key, val) in headers.iter() { - let kv = serialize_key_value(builder, key.as_ref(), val.to_str().unwrap()); - fields.push(kv); - } - builder.create_vector(fields.as_ref()) -} - -// Not to be confused with serialize_response which has nothing to do with HTTP. -pub fn serialize_http_response<'bldr>( - builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, - r: &Response<Body>, -) -> flatbuffers::WIPOffset<msg::HttpHeader<'bldr>> { - let status = r.status().as_u16(); - let fields = serialize_fields(builder, r.headers()); - msg::HttpHeader::create( - builder, - &msg::HttpHeaderArgs { - is_request: false, - status, - fields: Some(fields), - ..Default::default() - }, - ) -} - -pub fn deserialize_request( - header_msg: msg::HttpHeader<'_>, - body: Body, -) -> DenoResult<Request<Body>> { - let mut r = Request::new(body); - - assert!(header_msg.is_request()); - - let u = header_msg.url().unwrap(); - let u = Uri::from_str(u) - .map_err(|e| errors::new(msg::ErrorKind::InvalidUri, e.to_string()))?; - *r.uri_mut() = u; - - if let Some(method) = header_msg.method() { - let method = Method::from_str(method).unwrap(); - *r.method_mut() = method; - } - - if let Some(fields) = header_msg.fields() { - let headers = r.headers_mut(); - for i in 0..fields.len() { - let kv = fields.get(i); - let key = kv.key().unwrap(); - let name = HeaderName::from_bytes(key.as_bytes()).unwrap(); - let value = kv.value().unwrap(); - let v = HeaderValue::from_str(value).unwrap(); - headers.insert(name, v); - } - } - Ok(r) -} diff --git a/src/ops.rs b/src/ops.rs deleted file mode 100644 index 254a21563..000000000 --- a/src/ops.rs +++ /dev/null @@ -1,2020 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use atty; -use crate::ansi; -use crate::cli::Buf; -use crate::cli::Cli; -use crate::errors; -use crate::errors::{permission_denied, DenoError, DenoResult, ErrorKind}; -use crate::fs as deno_fs; -use crate::http_util; -use crate::isolate_state::IsolateState; -use crate::js_errors::apply_source_map; -use crate::js_errors::JSErrorColor; -use crate::msg; -use crate::msg_util; -use crate::repl; -use crate::resolve_addr::resolve_addr; -use crate::resources; -use crate::resources::table_entries; -use crate::resources::Resource; -use crate::tokio_util; -use crate::tokio_write; -use crate::version; -use deno_core::deno_buf; -use deno_core::JSError; -use deno_core::Op; -use flatbuffers::FlatBufferBuilder; -use futures; -use futures::Async; -use futures::Poll; -use futures::Sink; -use futures::Stream; -use hyper; -use hyper::rt::Future; -use remove_dir_all::remove_dir_all; -use std; -use std::convert::From; -use std::fs; -use std::net::Shutdown; -use std::path::Path; -use std::path::PathBuf; -use std::process::Command; -use std::sync::Arc; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use tokio; -use tokio::net::TcpListener; -use tokio::net::TcpStream; -use tokio_process::CommandExt; -use tokio_threadpool; - -#[cfg(unix)] -use std::os::unix::fs::PermissionsExt; -#[cfg(unix)] -use std::os::unix::process::ExitStatusExt; - -type OpResult = DenoResult<Buf>; - -pub type OpWithError = dyn Future<Item = Buf, Error = DenoError> + Send; - -// TODO Ideally we wouldn't have to box the OpWithError being returned. -// The box is just to make it easier to get a prototype refactor working. -type OpCreator = - fn(cli: &Cli, base: &msg::Base<'_>, data: deno_buf) -> Box<OpWithError>; - -#[inline] -fn empty_buf() -> Buf { - Box::new([]) -} - -/// Processes raw messages from JavaScript. -/// This functions invoked every time libdeno.send() is called. -/// control corresponds to the first argument of libdeno.send(). -/// data corresponds to the second argument of libdeno.send(). -pub fn dispatch( - cli: &Cli, - control: &[u8], - zero_copy: deno_buf, -) -> (bool, Box<Op>) { - let bytes_sent_control = control.len(); - let bytes_sent_zero_copy = zero_copy.len(); - let base = msg::get_root_as_base(&control); - let is_sync = base.sync(); - let inner_type = base.inner_type(); - let cmd_id = base.cmd_id(); - - let op: Box<OpWithError> = { - // Handle regular ops. - let op_creator: OpCreator = match inner_type { - msg::Any::Accept => op_accept, - msg::Any::Chdir => op_chdir, - msg::Any::Chmod => op_chmod, - msg::Any::Close => op_close, - msg::Any::CopyFile => op_copy_file, - msg::Any::Cwd => op_cwd, - msg::Any::Dial => op_dial, - msg::Any::Environ => op_env, - msg::Any::Exit => op_exit, - msg::Any::Fetch => op_fetch, - msg::Any::FetchModuleMetaData => op_fetch_module_meta_data, - msg::Any::FormatError => op_format_error, - msg::Any::GlobalTimer => op_global_timer, - msg::Any::GlobalTimerStop => op_global_timer_stop, - msg::Any::IsTTY => op_is_tty, - msg::Any::Listen => op_listen, - msg::Any::MakeTempDir => op_make_temp_dir, - msg::Any::Metrics => op_metrics, - msg::Any::Mkdir => op_mkdir, - msg::Any::Now => op_now, - msg::Any::Open => op_open, - msg::Any::PermissionRevoke => op_revoke_permission, - msg::Any::Permissions => op_permissions, - msg::Any::Read => op_read, - msg::Any::ReadDir => op_read_dir, - msg::Any::ReadFile => op_read_file, - msg::Any::Readlink => op_read_link, - msg::Any::Remove => op_remove, - msg::Any::Rename => op_rename, - msg::Any::ReplReadline => op_repl_readline, - msg::Any::ReplStart => op_repl_start, - msg::Any::Resources => op_resources, - msg::Any::Run => op_run, - msg::Any::RunStatus => op_run_status, - msg::Any::Seek => op_seek, - msg::Any::SetEnv => op_set_env, - msg::Any::Shutdown => op_shutdown, - msg::Any::Start => op_start, - msg::Any::Stat => op_stat, - msg::Any::Symlink => op_symlink, - msg::Any::Truncate => op_truncate, - msg::Any::WorkerGetMessage => op_worker_get_message, - msg::Any::WorkerPostMessage => op_worker_post_message, - msg::Any::Write => op_write, - msg::Any::WriteFile => op_write_file, - _ => panic!(format!( - "Unhandled message {}", - msg::enum_name_any(inner_type) - )), - }; - op_creator(&cli, &base, zero_copy) - }; - - cli - .state - .metrics_op_dispatched(bytes_sent_control, bytes_sent_zero_copy); - let state = cli.state.clone(); - - let boxed_op = Box::new( - op.or_else(move |err: DenoError| -> Result<Buf, ()> { - debug!("op err {}", err); - // No matter whether we got an Err or Ok, we want a serialized message to - // send back. So transform the DenoError into a deno_buf. - let builder = &mut FlatBufferBuilder::new(); - let errmsg_offset = builder.create_string(&format!("{}", err)); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - error: Some(errmsg_offset), - error_kind: err.kind(), - ..Default::default() - }, - )) - }).and_then(move |buf: Buf| -> Result<Buf, ()> { - // Handle empty responses. For sync responses we just want - // to send null. For async we want to send a small message - // with the cmd_id. - let buf = if is_sync || buf.len() > 0 { - buf - } else { - let builder = &mut FlatBufferBuilder::new(); - serialize_response( - cmd_id, - builder, - msg::BaseArgs { - ..Default::default() - }, - ) - }; - state.metrics_op_completed(buf.len()); - Ok(buf) - }).map_err(|err| panic!("unexpected error {:?}", err)), - ); - - debug!( - "msg_from_js {} sync {}", - msg::enum_name_any(inner_type), - base.sync() - ); - (base.sync(), boxed_op) -} - -fn op_now( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let start = SystemTime::now(); - let since_the_epoch = start.duration_since(UNIX_EPOCH).unwrap(); - let time = since_the_epoch.as_secs() * 1000 - + u64::from(since_the_epoch.subsec_millis()); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::NowRes::create(builder, &msg::NowResArgs { time }); - ok_future(serialize_response( - base.cmd_id(), - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::NowRes, - ..Default::default() - }, - )) -} - -fn op_is_tty( - _cli: &Cli, - base: &msg::Base<'_>, - _data: deno_buf, -) -> Box<OpWithError> { - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::IsTTYRes::create( - builder, - &msg::IsTTYResArgs { - stdin: atty::is(atty::Stream::Stdin), - stdout: atty::is(atty::Stream::Stdout), - stderr: atty::is(atty::Stream::Stderr), - }, - ); - ok_future(serialize_response( - base.cmd_id(), - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::IsTTYRes, - ..Default::default() - }, - )) -} - -fn op_exit( - _cli: &Cli, - base: &msg::Base<'_>, - _data: deno_buf, -) -> Box<OpWithError> { - let inner = base.inner_as_exit().unwrap(); - std::process::exit(inner.code()) -} - -fn op_start( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let mut builder = FlatBufferBuilder::new(); - - let argv = cli - .state - .argv - .iter() - .map(|s| s.as_str()) - .collect::<Vec<_>>(); - let argv_off = builder.create_vector_of_strings(argv.as_slice()); - - let cwd_path = std::env::current_dir().unwrap(); - let cwd_off = - builder.create_string(deno_fs::normalize_path(cwd_path.as_ref()).as_ref()); - - let exec_path = - builder.create_string(std::env::current_exe().unwrap().to_str().unwrap()); - - let v8_version = version::v8(); - let v8_version_off = builder.create_string(v8_version); - - let deno_version = version::DENO; - let deno_version_off = builder.create_string(deno_version); - - let main_module = cli.state.main_module().map(|m| builder.create_string(&m)); - - let inner = msg::StartRes::create( - &mut builder, - &msg::StartResArgs { - cwd: Some(cwd_off), - pid: std::process::id(), - argv: Some(argv_off), - main_module, - debug_flag: cli.state.flags.log_debug, - types_flag: cli.state.flags.types, - version_flag: cli.state.flags.version, - v8_version: Some(v8_version_off), - deno_version: Some(deno_version_off), - no_color: !ansi::use_color(), - exec_path: Some(exec_path), - ..Default::default() - }, - ); - - ok_future(serialize_response( - base.cmd_id(), - &mut builder, - msg::BaseArgs { - inner_type: msg::Any::StartRes, - inner: Some(inner.as_union_value()), - ..Default::default() - }, - )) -} - -fn op_format_error( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_format_error().unwrap(); - let orig_error = String::from(inner.error().unwrap()); - - let js_error = JSError::from_v8_exception(&orig_error).unwrap(); - let js_error_mapped = apply_source_map(&js_error, &cli.state.dir); - let js_error_string = JSErrorColor(&js_error_mapped).to_string(); - - let mut builder = FlatBufferBuilder::new(); - let new_error = builder.create_string(&js_error_string); - - let inner = msg::FormatErrorRes::create( - &mut builder, - &msg::FormatErrorResArgs { - error: Some(new_error), - ..Default::default() - }, - ); - - ok_future(serialize_response( - base.cmd_id(), - &mut builder, - msg::BaseArgs { - inner_type: msg::Any::FormatErrorRes, - inner: Some(inner.as_union_value()), - ..Default::default() - }, - )) -} - -fn serialize_response( - cmd_id: u32, - builder: &mut FlatBufferBuilder<'_>, - mut args: msg::BaseArgs<'_>, -) -> Buf { - args.cmd_id = cmd_id; - let base = msg::Base::create(builder, &args); - msg::finish_base_buffer(builder, base); - let data = builder.finished_data(); - // println!("serialize_response {:x?}", data); - data.into() -} - -#[inline] -pub fn ok_future(buf: Buf) -> Box<OpWithError> { - Box::new(futures::future::ok(buf)) -} - -// Shout out to Earl Sweatshirt. -#[inline] -pub fn odd_future(err: DenoError) -> Box<OpWithError> { - Box::new(futures::future::err(err)) -} - -// https://github.com/denoland/deno/blob/golang/os.go#L100-L154 -fn op_fetch_module_meta_data( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_fetch_module_meta_data().unwrap(); - let cmd_id = base.cmd_id(); - let specifier = inner.specifier().unwrap(); - let referrer = inner.referrer().unwrap(); - - // Check for allow read since this operation could be used to read from the file system. - if !cli.permissions.allows_read() { - debug!("No read permission for fetch_module_meta_data"); - return odd_future(permission_denied()); - } - - // Check for allow write since this operation could be used to write to the file system. - if !cli.permissions.allows_write() { - debug!("No network permission for fetch_module_meta_data"); - return odd_future(permission_denied()); - } - - // Check for allow net since this operation could be used to make https/http requests. - if !cli.permissions.allows_net() { - debug!("No network permission for fetch_module_meta_data"); - return odd_future(permission_denied()); - } - - assert_eq!( - cli.state.dir.root.join("gen"), - cli.state.dir.gen, - "Sanity check" - ); - - Box::new(futures::future::result(|| -> OpResult { - let builder = &mut FlatBufferBuilder::new(); - let out = cli.state.dir.fetch_module_meta_data(specifier, referrer)?; - let data_off = builder.create_vector(out.source_code.as_slice()); - let msg_args = msg::FetchModuleMetaDataResArgs { - module_name: Some(builder.create_string(&out.module_name)), - filename: Some(builder.create_string(&out.filename)), - media_type: out.media_type, - data: Some(data_off), - }; - let inner = msg::FetchModuleMetaDataRes::create(builder, &msg_args); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchModuleMetaDataRes, - ..Default::default() - }, - )) - }())) -} - -fn op_chdir( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_chdir().unwrap(); - let directory = inner.directory().unwrap(); - Box::new(futures::future::result(|| -> OpResult { - std::env::set_current_dir(&directory)?; - Ok(empty_buf()) - }())) -} - -fn op_global_timer_stop( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert!(base.sync()); - assert_eq!(data.len(), 0); - let mut t = cli.state.global_timer.lock().unwrap(); - t.cancel(); - ok_future(empty_buf()) -} - -fn op_global_timer( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert!(!base.sync()); - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - let inner = base.inner_as_global_timer().unwrap(); - let val = inner.timeout(); - assert!(val >= 0); - - let mut t = cli.state.global_timer.lock().unwrap(); - let deadline = Instant::now() + Duration::from_millis(val as u64); - let f = t.new_timeout(deadline); - - Box::new(f.then(move |_| { - let builder = &mut FlatBufferBuilder::new(); - let inner = - msg::GlobalTimerRes::create(builder, &msg::GlobalTimerResArgs {}); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::GlobalTimerRes, - ..Default::default() - }, - )) - })) -} - -fn op_set_env( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_set_env().unwrap(); - let key = inner.key().unwrap(); - let value = inner.value().unwrap(); - if let Err(e) = cli.check_env() { - return odd_future(e); - } - std::env::set_var(key, value); - ok_future(empty_buf()) -} - -fn op_env(cli: &Cli, base: &msg::Base<'_>, data: deno_buf) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - - if let Err(e) = cli.check_env() { - return odd_future(e); - } - - let builder = &mut FlatBufferBuilder::new(); - let vars: Vec<_> = std::env::vars() - .map(|(key, value)| msg_util::serialize_key_value(builder, &key, &value)) - .collect(); - let tables = builder.create_vector(&vars); - let inner = msg::EnvironRes::create( - builder, - &msg::EnvironResArgs { map: Some(tables) }, - ); - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::EnvironRes, - ..Default::default() - }, - )) -} - -fn op_permissions( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::PermissionsRes::create( - builder, - &msg::PermissionsResArgs { - run: cli.permissions.allows_run(), - read: cli.permissions.allows_read(), - write: cli.permissions.allows_write(), - net: cli.permissions.allows_net(), - env: cli.permissions.allows_env(), - }, - ); - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::PermissionsRes, - ..Default::default() - }, - )) -} - -fn op_revoke_permission( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_permission_revoke().unwrap(); - let permission = inner.permission().unwrap(); - let result = match permission { - "run" => cli.permissions.revoke_run(), - "read" => cli.permissions.revoke_read(), - "write" => cli.permissions.revoke_write(), - "net" => cli.permissions.revoke_net(), - "env" => cli.permissions.revoke_env(), - _ => Ok(()), - }; - if let Err(e) = result { - return odd_future(e); - } - ok_future(empty_buf()) -} - -fn op_fetch( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - let inner = base.inner_as_fetch().unwrap(); - let cmd_id = base.cmd_id(); - - let header = inner.header().unwrap(); - assert!(header.is_request()); - let url = header.url().unwrap(); - - let body = if data.is_empty() { - hyper::Body::empty() - } else { - hyper::Body::from(Vec::from(&*data)) - }; - - let maybe_req = msg_util::deserialize_request(header, body); - if let Err(e) = maybe_req { - return odd_future(e); - } - let req = maybe_req.unwrap(); - - if let Err(e) = cli.check_net(url) { - return odd_future(e); - } - - let client = http_util::get_client(); - - debug!("Before fetch {}", url); - let future = - client - .request(req) - .map_err(DenoError::from) - .and_then(move |res| { - let builder = &mut FlatBufferBuilder::new(); - let header_off = msg_util::serialize_http_response(builder, &res); - let body = res.into_body(); - let body_resource = resources::add_hyper_body(body); - let inner = msg::FetchRes::create( - builder, - &msg::FetchResArgs { - header: Some(header_off), - body_rid: body_resource.rid, - }, - ); - - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchRes, - ..Default::default() - }, - )) - }); - Box::new(future) -} - -// This is just type conversion. Implement From trait? -// See https://github.com/tokio-rs/tokio/blob/ffd73a64e7ec497622b7f939e38017afe7124dc4/tokio-fs/src/lib.rs#L76-L85 -fn convert_blocking<F>(f: F) -> Poll<Buf, DenoError> -where - F: FnOnce() -> DenoResult<Buf>, -{ - use futures::Async::*; - match tokio_threadpool::blocking(f) { - Ok(Ready(Ok(v))) => Ok(v.into()), - Ok(Ready(Err(err))) => Err(err), - Ok(NotReady) => Ok(NotReady), - Err(err) => panic!("blocking error {}", err), - } -} - -fn blocking<F>(is_sync: bool, f: F) -> Box<OpWithError> -where - F: 'static + Send + FnOnce() -> DenoResult<Buf>, -{ - if is_sync { - Box::new(futures::future::result(f())) - } else { - Box::new(tokio_util::poll_fn(move || convert_blocking(f))) - } -} - -fn op_make_temp_dir( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let base = Box::new(*base); - let inner = base.inner_as_make_temp_dir().unwrap(); - let cmd_id = base.cmd_id(); - - // FIXME - if let Err(e) = cli.check_write("make_temp") { - return odd_future(e); - } - - let dir = inner.dir().map(PathBuf::from); - let prefix = inner.prefix().map(String::from); - let suffix = inner.suffix().map(String::from); - - blocking(base.sync(), move || -> OpResult { - // TODO(piscisaureus): use byte vector for paths, not a string. - // See https://github.com/denoland/deno/issues/627. - // We can't assume that paths are always valid utf8 strings. - let path = deno_fs::make_temp_dir( - // Converting Option<String> to Option<&str> - dir.as_ref().map(|x| &**x), - prefix.as_ref().map(|x| &**x), - suffix.as_ref().map(|x| &**x), - )?; - let builder = &mut FlatBufferBuilder::new(); - let path_off = builder.create_string(path.to_str().unwrap()); - let inner = msg::MakeTempDirRes::create( - builder, - &msg::MakeTempDirResArgs { - path: Some(path_off), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::MakeTempDirRes, - ..Default::default() - }, - )) - }) -} - -fn op_mkdir( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_mkdir().unwrap(); - let path = String::from(inner.path().unwrap()); - let recursive = inner.recursive(); - let mode = inner.mode(); - - if let Err(e) = cli.check_write(&path) { - return odd_future(e); - } - - blocking(base.sync(), move || { - debug!("op_mkdir {}", path); - deno_fs::mkdir(Path::new(&path), mode, recursive)?; - Ok(empty_buf()) - }) -} - -fn op_chmod( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_chmod().unwrap(); - let _mode = inner.mode(); - let path = String::from(inner.path().unwrap()); - - if let Err(e) = cli.check_write(&path) { - return odd_future(e); - } - - blocking(base.sync(), move || { - debug!("op_chmod {}", &path); - let path = PathBuf::from(&path); - // Still check file/dir exists on windows - let _metadata = fs::metadata(&path)?; - // Only work in unix - #[cfg(any(unix))] - { - // We need to use underscore to compile in Windows. - #[cfg_attr( - feature = "cargo-clippy", - allow(clippy::used_underscore_binding) - )] - let mut permissions = _metadata.permissions(); - #[cfg_attr( - feature = "cargo-clippy", - allow(clippy::used_underscore_binding) - )] - permissions.set_mode(_mode); - fs::set_permissions(&path, permissions)?; - } - Ok(empty_buf()) - }) -} - -fn op_open( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - let inner = base.inner_as_open().unwrap(); - let filename_str = inner.filename().unwrap(); - let filename = PathBuf::from(&filename_str); - let mode = inner.mode().unwrap(); - - let mut open_options = tokio::fs::OpenOptions::new(); - - match mode { - "r" => { - open_options.read(true); - } - "r+" => { - open_options.read(true).write(true); - } - "w" => { - open_options.create(true).write(true).truncate(true); - } - "w+" => { - open_options - .read(true) - .create(true) - .write(true) - .truncate(true); - } - "a" => { - open_options.create(true).append(true); - } - "a+" => { - open_options.read(true).create(true).append(true); - } - "x" => { - open_options.create_new(true).write(true); - } - "x+" => { - open_options.create_new(true).read(true).write(true); - } - &_ => { - panic!("Unknown file open mode."); - } - } - - match mode { - "r" => { - if let Err(e) = cli.check_read(&filename_str) { - return odd_future(e); - } - } - "w" | "a" | "x" => { - if let Err(e) = cli.check_write(&filename_str) { - return odd_future(e); - } - } - &_ => { - if let Err(e) = cli.check_read(&filename_str) { - return odd_future(e); - } - if let Err(e) = cli.check_write(&filename_str) { - return odd_future(e); - } - } - } - - let op = open_options - .open(filename) - .map_err(DenoError::from) - .and_then(move |fs_file| -> OpResult { - let resource = resources::add_fs_file(fs_file); - let builder = &mut FlatBufferBuilder::new(); - let inner = - msg::OpenRes::create(builder, &msg::OpenResArgs { rid: resource.rid }); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::OpenRes, - ..Default::default() - }, - )) - }); - Box::new(op) -} - -fn op_close( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_close().unwrap(); - let rid = inner.rid(); - match resources::lookup(rid) { - None => odd_future(errors::bad_resource()), - Some(resource) => { - resource.close(); - ok_future(empty_buf()) - } - } -} - -fn op_shutdown( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_shutdown().unwrap(); - let rid = inner.rid(); - let how = inner.how(); - match resources::lookup(rid) { - None => odd_future(errors::bad_resource()), - Some(mut resource) => { - let shutdown_mode = match how { - 0 => Shutdown::Read, - 1 => Shutdown::Write, - _ => unimplemented!(), - }; - blocking(base.sync(), move || { - // Use UFCS for disambiguation - Resource::shutdown(&mut resource, shutdown_mode)?; - Ok(empty_buf()) - }) - } - } -} - -fn op_read( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - let cmd_id = base.cmd_id(); - let inner = base.inner_as_read().unwrap(); - let rid = inner.rid(); - - match resources::lookup(rid) { - None => odd_future(errors::bad_resource()), - Some(resource) => { - let op = tokio::io::read(resource, data) - .map_err(DenoError::from) - .and_then(move |(_resource, _buf, nread)| { - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::ReadRes::create( - builder, - &msg::ReadResArgs { - nread: nread as u32, - eof: nread == 0, - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReadRes, - ..Default::default() - }, - )) - }); - Box::new(op) - } - } -} - -fn op_write( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - let cmd_id = base.cmd_id(); - let inner = base.inner_as_write().unwrap(); - let rid = inner.rid(); - - match resources::lookup(rid) { - None => odd_future(errors::bad_resource()), - Some(resource) => { - let op = tokio_write::write(resource, data) - .map_err(DenoError::from) - .and_then(move |(_resource, _buf, nwritten)| { - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::WriteRes::create( - builder, - &msg::WriteResArgs { - nbyte: nwritten as u32, - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::WriteRes, - ..Default::default() - }, - )) - }); - Box::new(op) - } - } -} - -fn op_seek( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let _cmd_id = base.cmd_id(); - let inner = base.inner_as_seek().unwrap(); - let rid = inner.rid(); - let offset = inner.offset(); - let whence = inner.whence(); - - match resources::lookup(rid) { - None => odd_future(errors::bad_resource()), - Some(resource) => { - let op = resources::seek(resource, offset, whence) - .and_then(move |_| Ok(empty_buf())); - Box::new(op) - } - } -} - -fn op_remove( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_remove().unwrap(); - let path_ = inner.path().unwrap(); - let path = PathBuf::from(path_); - let recursive = inner.recursive(); - - if let Err(e) = cli.check_write(path.to_str().unwrap()) { - return odd_future(e); - } - - blocking(base.sync(), move || { - debug!("op_remove {}", path.display()); - let metadata = fs::metadata(&path)?; - if metadata.is_file() { - fs::remove_file(&path)?; - } else if recursive { - remove_dir_all(&path)?; - } else { - fs::remove_dir(&path)?; - } - Ok(empty_buf()) - }) -} - -// Prototype https://github.com/denoland/deno/blob/golang/os.go#L171-L184 -fn op_read_file( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_read_file().unwrap(); - let cmd_id = base.cmd_id(); - let filename_ = inner.filename().unwrap(); - let filename = PathBuf::from(filename_); - debug!("op_read_file {}", filename.display()); - if let Err(e) = cli.check_read(&filename_) { - return odd_future(e); - } - blocking(base.sync(), move || { - let vec = fs::read(&filename)?; - // Build the response message. memcpy data into inner. - // TODO(ry) zero-copy. - let builder = &mut FlatBufferBuilder::new(); - let data_off = builder.create_vector(vec.as_slice()); - let inner = msg::ReadFileRes::create( - builder, - &msg::ReadFileResArgs { - data: Some(data_off), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReadFileRes, - ..Default::default() - }, - )) - }) -} - -fn op_copy_file( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_copy_file().unwrap(); - let from_ = inner.from().unwrap(); - let from = PathBuf::from(from_); - let to_ = inner.to().unwrap(); - let to = PathBuf::from(to_); - - if let Err(e) = cli.check_read(&from_) { - return odd_future(e); - } - if let Err(e) = cli.check_write(&to_) { - return odd_future(e); - } - - debug!("op_copy_file {} {}", from.display(), to.display()); - blocking(base.sync(), move || { - // On *nix, Rust deem non-existent path as invalid input - // See https://github.com/rust-lang/rust/issues/54800 - // Once the issue is reolved, we should remove this workaround. - if cfg!(unix) && !from.is_file() { - return Err(errors::new( - ErrorKind::NotFound, - "File not found".to_string(), - )); - } - - fs::copy(&from, &to)?; - Ok(empty_buf()) - }) -} - -macro_rules! to_seconds { - ($time:expr) => {{ - // Unwrap is safe here as if the file is before the unix epoch - // something is very wrong. - $time - .and_then(|t| Ok(t.duration_since(UNIX_EPOCH).unwrap().as_secs())) - .unwrap_or(0) - }}; -} - -#[cfg(any(unix))] -fn get_mode(perm: &fs::Permissions) -> u32 { - perm.mode() -} - -#[cfg(not(any(unix)))] -fn get_mode(_perm: &fs::Permissions) -> u32 { - 0 -} - -fn op_cwd( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - Box::new(futures::future::result(|| -> OpResult { - let path = std::env::current_dir()?; - let builder = &mut FlatBufferBuilder::new(); - let cwd = - builder.create_string(&path.into_os_string().into_string().unwrap()); - let inner = - msg::CwdRes::create(builder, &msg::CwdResArgs { cwd: Some(cwd) }); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::CwdRes, - ..Default::default() - }, - )) - }())) -} - -fn op_stat( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_stat().unwrap(); - let cmd_id = base.cmd_id(); - let filename_ = inner.filename().unwrap(); - let filename = PathBuf::from(filename_); - let lstat = inner.lstat(); - - if let Err(e) = cli.check_read(&filename_) { - return odd_future(e); - } - - blocking(base.sync(), move || { - let builder = &mut FlatBufferBuilder::new(); - debug!("op_stat {} {}", filename.display(), lstat); - let metadata = if lstat { - fs::symlink_metadata(&filename)? - } else { - fs::metadata(&filename)? - }; - - let inner = msg::StatRes::create( - builder, - &msg::StatResArgs { - is_file: metadata.is_file(), - is_symlink: metadata.file_type().is_symlink(), - len: metadata.len(), - modified: to_seconds!(metadata.modified()), - accessed: to_seconds!(metadata.accessed()), - created: to_seconds!(metadata.created()), - mode: get_mode(&metadata.permissions()), - has_mode: cfg!(target_family = "unix"), - ..Default::default() - }, - ); - - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::StatRes, - ..Default::default() - }, - )) - }) -} - -fn op_read_dir( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_read_dir().unwrap(); - let cmd_id = base.cmd_id(); - let path = String::from(inner.path().unwrap()); - - if let Err(e) = cli.check_read(&path) { - return odd_future(e); - } - - blocking(base.sync(), move || -> OpResult { - debug!("op_read_dir {}", path); - let builder = &mut FlatBufferBuilder::new(); - let entries: Vec<_> = fs::read_dir(Path::new(&path))? - .map(|entry| { - let entry = entry.unwrap(); - let metadata = entry.metadata().unwrap(); - let file_type = metadata.file_type(); - let name = builder.create_string(entry.file_name().to_str().unwrap()); - let path = builder.create_string(entry.path().to_str().unwrap()); - - msg::StatRes::create( - builder, - &msg::StatResArgs { - is_file: file_type.is_file(), - is_symlink: file_type.is_symlink(), - len: metadata.len(), - modified: to_seconds!(metadata.modified()), - accessed: to_seconds!(metadata.accessed()), - created: to_seconds!(metadata.created()), - name: Some(name), - path: Some(path), - mode: get_mode(&metadata.permissions()), - has_mode: cfg!(target_family = "unix"), - }, - ) - }).collect(); - - let entries = builder.create_vector(&entries); - let inner = msg::ReadDirRes::create( - builder, - &msg::ReadDirResArgs { - entries: Some(entries), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReadDirRes, - ..Default::default() - }, - )) - }) -} - -fn op_write_file( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - let inner = base.inner_as_write_file().unwrap(); - let filename = String::from(inner.filename().unwrap()); - let update_perm = inner.update_perm(); - let perm = inner.perm(); - let is_create = inner.is_create(); - let is_append = inner.is_append(); - - if let Err(e) = cli.check_write(&filename) { - return odd_future(e); - } - - blocking(base.sync(), move || -> OpResult { - debug!("op_write_file {} {}", filename, data.len()); - deno_fs::write_file_2( - Path::new(&filename), - data, - update_perm, - perm, - is_create, - is_append, - )?; - Ok(empty_buf()) - }) -} - -fn op_rename( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_rename().unwrap(); - let oldpath = PathBuf::from(inner.oldpath().unwrap()); - let newpath_ = inner.newpath().unwrap(); - let newpath = PathBuf::from(newpath_); - if let Err(e) = cli.check_write(&newpath_) { - return odd_future(e); - } - blocking(base.sync(), move || -> OpResult { - debug!("op_rename {} {}", oldpath.display(), newpath.display()); - fs::rename(&oldpath, &newpath)?; - Ok(empty_buf()) - }) -} - -fn op_symlink( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_symlink().unwrap(); - let oldname = PathBuf::from(inner.oldname().unwrap()); - let newname_ = inner.newname().unwrap(); - let newname = PathBuf::from(newname_); - - if let Err(e) = cli.check_write(&newname_) { - return odd_future(e); - } - // TODO Use type for Windows. - if cfg!(windows) { - return odd_future(errors::new( - ErrorKind::Other, - "Not implemented".to_string(), - )); - } - blocking(base.sync(), move || -> OpResult { - debug!("op_symlink {} {}", oldname.display(), newname.display()); - #[cfg(any(unix))] - std::os::unix::fs::symlink(&oldname, &newname)?; - Ok(empty_buf()) - }) -} - -fn op_read_link( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_readlink().unwrap(); - let cmd_id = base.cmd_id(); - let name_ = inner.name().unwrap(); - let name = PathBuf::from(name_); - - if let Err(e) = cli.check_read(&name_) { - return odd_future(e); - } - - blocking(base.sync(), move || -> OpResult { - debug!("op_read_link {}", name.display()); - let path = fs::read_link(&name)?; - let builder = &mut FlatBufferBuilder::new(); - let path_off = builder.create_string(path.to_str().unwrap()); - let inner = msg::ReadlinkRes::create( - builder, - &msg::ReadlinkResArgs { - path: Some(path_off), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReadlinkRes, - ..Default::default() - }, - )) - }) -} - -fn op_repl_start( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_repl_start().unwrap(); - let cmd_id = base.cmd_id(); - let history_file = String::from(inner.history_file().unwrap()); - - debug!("op_repl_start {}", history_file); - let history_path = repl::history_path(&cli.state.dir, &history_file); - let repl = repl::Repl::new(history_path); - let resource = resources::add_repl(repl); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::ReplStartRes::create( - builder, - &msg::ReplStartResArgs { rid: resource.rid }, - ); - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReplStartRes, - ..Default::default() - }, - )) -} - -fn op_repl_readline( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let inner = base.inner_as_repl_readline().unwrap(); - let cmd_id = base.cmd_id(); - let rid = inner.rid(); - let prompt = inner.prompt().unwrap().to_owned(); - debug!("op_repl_readline {} {}", rid, prompt); - - blocking(base.sync(), move || -> OpResult { - let repl = resources::get_repl(rid)?; - let line = repl.lock().unwrap().readline(&prompt)?; - - let builder = &mut FlatBufferBuilder::new(); - let line_off = builder.create_string(&line); - let inner = msg::ReplReadlineRes::create( - builder, - &msg::ReplReadlineResArgs { - line: Some(line_off), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ReplReadlineRes, - ..Default::default() - }, - )) - }) -} - -fn op_truncate( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - - let inner = base.inner_as_truncate().unwrap(); - let filename = String::from(inner.name().unwrap()); - let len = inner.len(); - - if let Err(e) = cli.check_write(&filename) { - return odd_future(e); - } - - blocking(base.sync(), move || { - debug!("op_truncate {} {}", filename, len); - let f = fs::OpenOptions::new().write(true).open(&filename)?; - f.set_len(u64::from(len))?; - Ok(empty_buf()) - }) -} - -fn op_listen( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - if let Err(e) = cli.check_net("listen") { - return odd_future(e); - } - - let cmd_id = base.cmd_id(); - let inner = base.inner_as_listen().unwrap(); - let network = inner.network().unwrap(); - assert_eq!(network, "tcp"); - let address = inner.address().unwrap(); - - Box::new(futures::future::result((move || { - let addr = resolve_addr(address).wait()?; - - let listener = TcpListener::bind(&addr)?; - let resource = resources::add_tcp_listener(listener); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::ListenRes::create( - builder, - &msg::ListenResArgs { rid: resource.rid }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ListenRes, - ..Default::default() - }, - )) - })())) -} - -fn new_conn(cmd_id: u32, tcp_stream: TcpStream) -> OpResult { - let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); - // TODO forward socket_addr to client. - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::NewConn::create( - builder, - &msg::NewConnArgs { - rid: tcp_stream_resource.rid, - ..Default::default() - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::NewConn, - ..Default::default() - }, - )) -} - -fn op_accept( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - if let Err(e) = cli.check_net("accept") { - return odd_future(e); - } - let cmd_id = base.cmd_id(); - let inner = base.inner_as_accept().unwrap(); - let server_rid = inner.rid(); - - match resources::lookup(server_rid) { - None => odd_future(errors::bad_resource()), - Some(server_resource) => { - let op = tokio_util::accept(server_resource) - .map_err(DenoError::from) - .and_then(move |(tcp_stream, _socket_addr)| { - new_conn(cmd_id, tcp_stream) - }); - Box::new(op) - } - } -} - -fn op_dial( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - if let Err(e) = cli.check_net("dial") { - return odd_future(e); - } - let cmd_id = base.cmd_id(); - let inner = base.inner_as_dial().unwrap(); - let network = inner.network().unwrap(); - assert_eq!(network, "tcp"); // TODO Support others. - let address = inner.address().unwrap(); - - let op = - resolve_addr(address) - .map_err(DenoError::from) - .and_then(move |addr| { - TcpStream::connect(&addr) - .map_err(DenoError::from) - .and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream)) - }); - Box::new(op) -} - -fn op_metrics( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::MetricsRes::create( - builder, - &msg::MetricsResArgs::from(&cli.state.metrics), - ); - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::MetricsRes, - ..Default::default() - }, - )) -} - -fn op_resources( - _cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - - let builder = &mut FlatBufferBuilder::new(); - let serialized_resources = table_entries(); - - let res: Vec<_> = serialized_resources - .iter() - .map(|(key, value)| { - let repr = builder.create_string(value); - - msg::Resource::create( - builder, - &msg::ResourceArgs { - rid: *key, - repr: Some(repr), - }, - ) - }).collect(); - - let resources = builder.create_vector(&res); - let inner = msg::ResourcesRes::create( - builder, - &msg::ResourcesResArgs { - resources: Some(resources), - }, - ); - - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::ResourcesRes, - ..Default::default() - }, - )) -} - -fn subprocess_stdio_map(v: msg::ProcessStdio) -> std::process::Stdio { - match v { - msg::ProcessStdio::Inherit => std::process::Stdio::inherit(), - msg::ProcessStdio::Piped => std::process::Stdio::piped(), - msg::ProcessStdio::Null => std::process::Stdio::null(), - } -} - -fn op_run(cli: &Cli, base: &msg::Base<'_>, data: deno_buf) -> Box<OpWithError> { - assert!(base.sync()); - let cmd_id = base.cmd_id(); - - if let Err(e) = cli.check_run() { - return odd_future(e); - } - - assert_eq!(data.len(), 0); - let inner = base.inner_as_run().unwrap(); - let args = inner.args().unwrap(); - let env = inner.env().unwrap(); - let cwd = inner.cwd(); - - let mut c = Command::new(args.get(0)); - (1..args.len()).for_each(|i| { - let arg = args.get(i); - c.arg(arg); - }); - cwd.map(|d| c.current_dir(d)); - (0..env.len()).for_each(|i| { - let entry = env.get(i); - c.env(entry.key().unwrap(), entry.value().unwrap()); - }); - - c.stdin(subprocess_stdio_map(inner.stdin())); - c.stdout(subprocess_stdio_map(inner.stdout())); - c.stderr(subprocess_stdio_map(inner.stderr())); - - // Spawn the command. - let child = match c.spawn_async() { - Ok(v) => v, - Err(err) => { - return odd_future(err.into()); - } - }; - - let pid = child.id(); - let resources = resources::add_child(child); - - let mut res_args = msg::RunResArgs { - rid: resources.child_rid, - pid, - ..Default::default() - }; - - if let Some(stdin_rid) = resources.stdin_rid { - res_args.stdin_rid = stdin_rid; - } - if let Some(stdout_rid) = resources.stdout_rid { - res_args.stdout_rid = stdout_rid; - } - if let Some(stderr_rid) = resources.stderr_rid { - res_args.stderr_rid = stderr_rid; - } - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::RunRes::create(builder, &res_args); - ok_future(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::RunRes, - ..Default::default() - }, - )) -} - -fn op_run_status( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - let inner = base.inner_as_run_status().unwrap(); - let rid = inner.rid(); - - if let Err(e) = cli.check_run() { - return odd_future(e); - } - - let future = match resources::child_status(rid) { - Err(e) => { - return odd_future(e); - } - Ok(f) => f, - }; - - let future = future.and_then(move |run_status| { - let code = run_status.code(); - - #[cfg(unix)] - let signal = run_status.signal(); - #[cfg(not(unix))] - let signal = None; - - code - .or(signal) - .expect("Should have either an exit code or a signal."); - let got_signal = signal.is_some(); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::RunStatusRes::create( - builder, - &msg::RunStatusResArgs { - got_signal, - exit_code: code.unwrap_or(-1), - exit_signal: signal.unwrap_or(-1), - }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::RunStatusRes, - ..Default::default() - }, - )) - }); - Box::new(future) -} - -struct GetMessageFuture { - pub state: Arc<IsolateState>, -} - -impl Future for GetMessageFuture { - type Item = Option<Buf>; - type Error = (); - - fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> { - assert!(self.state.worker_channels.is_some()); - match self.state.worker_channels { - None => panic!("expected worker_channels"), - Some(ref wc) => { - let mut wc = wc.lock().unwrap(); - wc.1.poll() - } - } - } -} - -fn op_worker_get_message( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - assert_eq!(data.len(), 0); - let cmd_id = base.cmd_id(); - - let op = GetMessageFuture { - state: cli.state.clone(), - }; - let op = op.map_err(move |_| -> DenoError { unimplemented!() }); - let op = op.and_then(move |maybe_buf| -> DenoResult<Buf> { - debug!("op_worker_get_message"); - let builder = &mut FlatBufferBuilder::new(); - - let data = maybe_buf.as_ref().map(|buf| builder.create_vector(buf)); - let inner = msg::WorkerGetMessageRes::create( - builder, - &msg::WorkerGetMessageResArgs { data }, - ); - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::WorkerGetMessageRes, - ..Default::default() - }, - )) - }); - Box::new(op) -} - -fn op_worker_post_message( - cli: &Cli, - base: &msg::Base<'_>, - data: deno_buf, -) -> Box<OpWithError> { - let cmd_id = base.cmd_id(); - - let d = Vec::from(data.as_ref()).into_boxed_slice(); - - assert!(cli.state.worker_channels.is_some()); - let tx = match cli.state.worker_channels { - None => panic!("expected worker_channels"), - Some(ref wc) => { - let wc = wc.lock().unwrap(); - wc.0.clone() - } - }; - let op = tx.send(d); - let op = op.map_err(|e| errors::new(ErrorKind::Other, e.to_string())); - let op = op.and_then(move |_| -> DenoResult<Buf> { - let builder = &mut FlatBufferBuilder::new(); - - Ok(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - ..Default::default() - }, - )) - }); - Box::new(op) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::cli::Cli; - use crate::isolate_state::IsolateState; - use crate::permissions::{DenoPermissions, PermissionAccessor}; - - #[test] - fn fetch_module_meta_fails_without_read() { - let state = Arc::new(IsolateState::mock()); - let permissions = DenoPermissions { - allow_write: PermissionAccessor::from(true), - allow_env: PermissionAccessor::from(true), - allow_net: PermissionAccessor::from(true), - allow_run: PermissionAccessor::from(true), - ..Default::default() - }; - let cli = Cli::new(None, state, permissions); - let builder = &mut FlatBufferBuilder::new(); - let fetch_msg_args = msg::FetchModuleMetaDataArgs { - specifier: Some(builder.create_string("./somefile")), - referrer: Some(builder.create_string(".")), - }; - let inner = msg::FetchModuleMetaData::create(builder, &fetch_msg_args); - let base_args = msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchModuleMetaData, - ..Default::default() - }; - let base = msg::Base::create(builder, &base_args); - msg::finish_base_buffer(builder, base); - let data = builder.finished_data(); - let final_msg = msg::get_root_as_base(&data); - let fetch_result = - op_fetch_module_meta_data(&cli, &final_msg, deno_buf::empty()).wait(); - match fetch_result { - Ok(_) => assert!(true), - Err(e) => assert_eq!(e.to_string(), permission_denied().to_string()), - } - } - - #[test] - fn fetch_module_meta_fails_without_write() { - let state = Arc::new(IsolateState::mock()); - let permissions = DenoPermissions { - allow_read: PermissionAccessor::from(true), - allow_env: PermissionAccessor::from(true), - allow_net: PermissionAccessor::from(true), - allow_run: PermissionAccessor::from(true), - ..Default::default() - }; - let cli = Cli::new(None, state, permissions); - let builder = &mut FlatBufferBuilder::new(); - let fetch_msg_args = msg::FetchModuleMetaDataArgs { - specifier: Some(builder.create_string("./somefile")), - referrer: Some(builder.create_string(".")), - }; - let inner = msg::FetchModuleMetaData::create(builder, &fetch_msg_args); - let base_args = msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchModuleMetaData, - ..Default::default() - }; - let base = msg::Base::create(builder, &base_args); - msg::finish_base_buffer(builder, base); - let data = builder.finished_data(); - let final_msg = msg::get_root_as_base(&data); - let fetch_result = - op_fetch_module_meta_data(&cli, &final_msg, deno_buf::empty()).wait(); - match fetch_result { - Ok(_) => assert!(true), - Err(e) => assert_eq!(e.to_string(), permission_denied().to_string()), - } - } - - #[test] - fn fetch_module_meta_fails_without_net() { - let state = Arc::new(IsolateState::mock()); - let permissions = DenoPermissions { - allow_read: PermissionAccessor::from(true), - allow_write: PermissionAccessor::from(true), - allow_env: PermissionAccessor::from(true), - allow_run: PermissionAccessor::from(true), - ..Default::default() - }; - let cli = Cli::new(None, state, permissions); - let builder = &mut FlatBufferBuilder::new(); - let fetch_msg_args = msg::FetchModuleMetaDataArgs { - specifier: Some(builder.create_string("./somefile")), - referrer: Some(builder.create_string(".")), - }; - let inner = msg::FetchModuleMetaData::create(builder, &fetch_msg_args); - let base_args = msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchModuleMetaData, - ..Default::default() - }; - let base = msg::Base::create(builder, &base_args); - msg::finish_base_buffer(builder, base); - let data = builder.finished_data(); - let final_msg = msg::get_root_as_base(&data); - let fetch_result = - op_fetch_module_meta_data(&cli, &final_msg, deno_buf::empty()).wait(); - match fetch_result { - Ok(_) => assert!(true), - Err(e) => assert_eq!(e.to_string(), permission_denied().to_string()), - } - } - - #[test] - fn fetch_module_meta_not_permission_denied_with_permissions() { - let state = Arc::new(IsolateState::mock()); - let permissions = DenoPermissions { - allow_read: PermissionAccessor::from(true), - allow_write: PermissionAccessor::from(true), - allow_net: PermissionAccessor::from(true), - ..Default::default() - }; - let cli = Cli::new(None, state, permissions); - let builder = &mut FlatBufferBuilder::new(); - let fetch_msg_args = msg::FetchModuleMetaDataArgs { - specifier: Some(builder.create_string("./somefile")), - referrer: Some(builder.create_string(".")), - }; - let inner = msg::FetchModuleMetaData::create(builder, &fetch_msg_args); - let base_args = msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::FetchModuleMetaData, - ..Default::default() - }; - let base = msg::Base::create(builder, &base_args); - msg::finish_base_buffer(builder, base); - let data = builder.finished_data(); - let final_msg = msg::get_root_as_base(&data); - let fetch_result = - op_fetch_module_meta_data(&cli, &final_msg, deno_buf::empty()).wait(); - match fetch_result { - Ok(_) => assert!(true), - Err(e) => assert!(e.to_string() != permission_denied().to_string()), - } - } -} diff --git a/src/permissions.rs b/src/permissions.rs deleted file mode 100644 index 9093c14f0..000000000 --- a/src/permissions.rs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use atty; - -use crate::flags::DenoFlags; - -use ansi_term::Style; -use crate::errors::permission_denied; -use crate::errors::DenoResult; -use std::fmt; -use std::io; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; - -/// Tri-state value for storing permission state -pub enum PermissionAccessorState { - Allow = 0, - Ask = 1, - Deny = 2, -} - -impl From<usize> for PermissionAccessorState { - fn from(val: usize) -> Self { - match val { - 0 => PermissionAccessorState::Allow, - 1 => PermissionAccessorState::Ask, - 2 => PermissionAccessorState::Deny, - _ => unreachable!(), - } - } -} - -impl From<bool> for PermissionAccessorState { - fn from(val: bool) -> Self { - match val { - true => PermissionAccessorState::Allow, - false => PermissionAccessorState::Ask, - } - } -} - -impl fmt::Display for PermissionAccessorState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PermissionAccessorState::Allow => f.pad("Allow"), - PermissionAccessorState::Ask => f.pad("Ask"), - PermissionAccessorState::Deny => f.pad("Deny"), - } - } -} - -#[derive(Debug)] -pub struct PermissionAccessor { - state: Arc<AtomicUsize>, -} - -impl PermissionAccessor { - pub fn new(state: PermissionAccessorState) -> Self { - Self { - state: Arc::new(AtomicUsize::new(state as usize)), - } - } - - pub fn is_allow(&self) -> bool { - match self.get_state() { - PermissionAccessorState::Allow => true, - _ => false, - } - } - - /// If the state is "Allow" walk it back to the default "Ask" - /// Don't do anything if state is "Deny" - pub fn revoke(&self) { - if self.is_allow() { - self.ask(); - } - } - - pub fn allow(&self) { - self.set_state(PermissionAccessorState::Allow) - } - - pub fn ask(&self) { - self.set_state(PermissionAccessorState::Ask) - } - - pub fn deny(&self) { - self.set_state(PermissionAccessorState::Deny) - } - - /// Update this accessors state based on a PromptResult value - /// This will only update the state if the PromptResult value - /// is one of the "Always" values - pub fn update_with_prompt_result(&self, prompt_result: &PromptResult) { - match prompt_result { - PromptResult::AllowAlways => self.allow(), - PromptResult::DenyAlways => self.deny(), - _ => {} - } - } - - #[inline] - pub fn get_state(&self) -> PermissionAccessorState { - self.state.load(Ordering::SeqCst).into() - } - fn set_state(&self, state: PermissionAccessorState) { - self.state.store(state as usize, Ordering::SeqCst) - } -} - -impl From<bool> for PermissionAccessor { - fn from(val: bool) -> Self { - Self::new(PermissionAccessorState::from(val)) - } -} - -impl Default for PermissionAccessor { - fn default() -> Self { - Self { - state: Arc::new(AtomicUsize::new(PermissionAccessorState::Ask as usize)), - } - } -} - -#[cfg_attr(feature = "cargo-clippy", allow(stutter))] -#[derive(Debug, Default)] -pub struct DenoPermissions { - // Keep in sync with src/permissions.ts - pub allow_read: PermissionAccessor, - pub allow_write: PermissionAccessor, - pub allow_net: PermissionAccessor, - pub allow_env: PermissionAccessor, - pub allow_run: PermissionAccessor, - pub no_prompts: AtomicBool, -} - -impl DenoPermissions { - pub fn from_flags(flags: &DenoFlags) -> Self { - Self { - allow_read: PermissionAccessor::from(flags.allow_read), - allow_write: PermissionAccessor::from(flags.allow_write), - allow_env: PermissionAccessor::from(flags.allow_env), - allow_net: PermissionAccessor::from(flags.allow_net), - allow_run: PermissionAccessor::from(flags.allow_run), - no_prompts: AtomicBool::new(flags.no_prompts), - } - } - - pub fn check_run(&self) -> DenoResult<()> { - match self.allow_run.get_state() { - PermissionAccessorState::Allow => Ok(()), - PermissionAccessorState::Ask => { - match self.try_permissions_prompt("access to run a subprocess") { - Err(e) => Err(e), - Ok(v) => { - self.allow_run.update_with_prompt_result(&v); - v.check()?; - Ok(()) - } - } - } - PermissionAccessorState::Deny => Err(permission_denied()), - } - } - - pub fn check_read(&self, filename: &str) -> DenoResult<()> { - match self.allow_read.get_state() { - PermissionAccessorState::Allow => Ok(()), - PermissionAccessorState::Ask => match self - .try_permissions_prompt(&format!("read access to \"{}\"", filename)) - { - Err(e) => Err(e), - Ok(v) => { - self.allow_read.update_with_prompt_result(&v); - v.check()?; - Ok(()) - } - }, - PermissionAccessorState::Deny => Err(permission_denied()), - } - } - - pub fn check_write(&self, filename: &str) -> DenoResult<()> { - match self.allow_write.get_state() { - PermissionAccessorState::Allow => Ok(()), - PermissionAccessorState::Ask => match self - .try_permissions_prompt(&format!("write access to \"{}\"", filename)) - { - Err(e) => Err(e), - Ok(v) => { - self.allow_write.update_with_prompt_result(&v); - v.check()?; - Ok(()) - } - }, - PermissionAccessorState::Deny => Err(permission_denied()), - } - } - - pub fn check_net(&self, domain_name: &str) -> DenoResult<()> { - match self.allow_net.get_state() { - PermissionAccessorState::Allow => Ok(()), - PermissionAccessorState::Ask => match self.try_permissions_prompt( - &format!("network access to \"{}\"", domain_name), - ) { - Err(e) => Err(e), - Ok(v) => { - self.allow_net.update_with_prompt_result(&v); - v.check()?; - Ok(()) - } - }, - PermissionAccessorState::Deny => Err(permission_denied()), - } - } - - pub fn check_env(&self) -> DenoResult<()> { - match self.allow_env.get_state() { - PermissionAccessorState::Allow => Ok(()), - PermissionAccessorState::Ask => { - match self.try_permissions_prompt("access to environment variables") { - Err(e) => Err(e), - Ok(v) => { - self.allow_env.update_with_prompt_result(&v); - v.check()?; - Ok(()) - } - } - } - PermissionAccessorState::Deny => Err(permission_denied()), - } - } - - /// Try to present the user with a permission prompt - /// will error with permission_denied if no_prompts is enabled - fn try_permissions_prompt(&self, message: &str) -> DenoResult<PromptResult> { - if self.no_prompts.load(Ordering::SeqCst) { - return Err(permission_denied()); - } - if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) { - return Err(permission_denied()); - }; - permission_prompt(message) - } - - pub fn allows_run(&self) -> bool { - return self.allow_run.is_allow(); - } - - pub fn allows_read(&self) -> bool { - return self.allow_read.is_allow(); - } - - pub fn allows_write(&self) -> bool { - return self.allow_write.is_allow(); - } - - pub fn allows_net(&self) -> bool { - return self.allow_net.is_allow(); - } - - pub fn allows_env(&self) -> bool { - return self.allow_env.is_allow(); - } - - pub fn revoke_run(&self) -> DenoResult<()> { - self.allow_run.revoke(); - return Ok(()); - } - - pub fn revoke_read(&self) -> DenoResult<()> { - self.allow_read.revoke(); - return Ok(()); - } - - pub fn revoke_write(&self) -> DenoResult<()> { - self.allow_write.revoke(); - return Ok(()); - } - - pub fn revoke_net(&self) -> DenoResult<()> { - self.allow_net.revoke(); - return Ok(()); - } - - pub fn revoke_env(&self) -> DenoResult<()> { - self.allow_env.revoke(); - return Ok(()); - } -} - -/// Quad-state value for representing user input on permission prompt -#[derive(Debug, Clone)] -pub enum PromptResult { - AllowAlways = 0, - AllowOnce = 1, - DenyOnce = 2, - DenyAlways = 3, -} - -impl PromptResult { - /// If value is any form of deny this will error with permission_denied - pub fn check(&self) -> DenoResult<()> { - match self { - PromptResult::DenyOnce => Err(permission_denied()), - PromptResult::DenyAlways => Err(permission_denied()), - _ => Ok(()), - } - } -} - -impl fmt::Display for PromptResult { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PromptResult::AllowAlways => f.pad("AllowAlways"), - PromptResult::AllowOnce => f.pad("AllowOnce"), - PromptResult::DenyOnce => f.pad("DenyOnce"), - PromptResult::DenyAlways => f.pad("DenyAlways"), - } - } -} - -fn permission_prompt(message: &str) -> DenoResult<PromptResult> { - let msg = format!("⚠️ Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", message); - // print to stderr so that if deno is > to a file this is still displayed. - eprint!("{}", Style::new().bold().paint(msg)); - loop { - let mut input = String::new(); - let stdin = io::stdin(); - let _nread = stdin.read_line(&mut input)?; - let ch = input.chars().next().unwrap(); - match ch.to_ascii_lowercase() { - 'a' => return Ok(PromptResult::AllowAlways), - 'y' => return Ok(PromptResult::AllowOnce), - 'n' => return Ok(PromptResult::DenyOnce), - 'd' => return Ok(PromptResult::DenyAlways), - _ => { - // If we don't get a recognized option try again. - let msg_again = format!("Unrecognized option '{}' [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", ch); - eprint!("{}", Style::new().bold().paint(msg_again)); - } - }; - } -} diff --git a/src/repl.rs b/src/repl.rs deleted file mode 100644 index 55bf4a114..000000000 --- a/src/repl.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use rustyline; - -use crate::msg::ErrorKind; -use std::error::Error; - -use crate::deno_dir::DenoDir; -use crate::errors::new as deno_error; -use crate::errors::DenoResult; -use std::path::PathBuf; - -#[cfg(not(windows))] -use rustyline::Editor; - -// Work around the issue that on Windows, `struct Editor` does not implement the -// `Send` trait, because it embeds a windows HANDLE which is a type alias for -// *mut c_void. This value isn't actually a pointer and there's nothing that -// can be mutated through it, so hack around it. TODO: a prettier solution. -#[cfg(windows)] -use std::ops::{Deref, DerefMut}; - -#[cfg(windows)] -struct Editor<T: rustyline::Helper> { - inner: rustyline::Editor<T>, -} - -#[cfg(windows)] -unsafe impl<T: rustyline::Helper> Send for Editor<T> {} - -#[cfg(windows)] -impl<T: rustyline::Helper> Editor<T> { - pub fn new() -> Editor<T> { - Editor { - inner: rustyline::Editor::<T>::new(), - } - } -} - -#[cfg(windows)] -impl<T: rustyline::Helper> Deref for Editor<T> { - type Target = rustyline::Editor<T>; - - fn deref(&self) -> &rustyline::Editor<T> { - &self.inner - } -} - -#[cfg(windows)] -impl<T: rustyline::Helper> DerefMut for Editor<T> { - fn deref_mut(&mut self) -> &mut rustyline::Editor<T> { - &mut self.inner - } -} - -pub struct Repl { - editor: Editor<()>, - history_file: PathBuf, -} - -impl Repl { - pub fn new(history_file: PathBuf) -> Self { - let mut repl = Self { - editor: Editor::<()>::new(), - history_file, - }; - - repl.load_history(); - repl - } - - fn load_history(&mut self) { - debug!("Loading REPL history: {:?}", self.history_file); - self - .editor - .load_history(&self.history_file.to_str().unwrap()) - .map_err(|e| debug!("Unable to load history file: {:?} {}", self.history_file, e)) - // ignore this error (e.g. it occurs on first load) - .unwrap_or(()) - } - - fn save_history(&mut self) -> DenoResult<()> { - self - .editor - .save_history(&self.history_file.to_str().unwrap()) - .map(|_| debug!("Saved REPL history to: {:?}", self.history_file)) - .map_err(|e| { - eprintln!("Unable to save REPL history: {:?} {}", self.history_file, e); - deno_error(ErrorKind::Other, e.description().to_string()) - }) - } - - pub fn readline(&mut self, prompt: &str) -> DenoResult<String> { - self - .editor - .readline(&prompt) - .map(|line| { - self.editor.add_history_entry(line.as_ref()); - line - }).map_err(|e| deno_error(ErrorKind::Other, e.description().to_string())) - // Forward error to TS side for processing - } -} - -impl Drop for Repl { - fn drop(&mut self) { - self.save_history().unwrap(); - } -} - -pub fn history_path(dir: &DenoDir, history_file: &str) -> PathBuf { - let mut p: PathBuf = dir.root.clone(); - p.push(history_file); - p -} diff --git a/src/resolve_addr.rs b/src/resolve_addr.rs deleted file mode 100644 index f26655be1..000000000 --- a/src/resolve_addr.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -use futures::Async; -use futures::Future; -use futures::Poll; -use std::error::Error; -use std::fmt; -use std::net::SocketAddr; -use std::net::ToSocketAddrs; - -/// Go-style network address parsing. Returns a future. -/// Examples: -/// "192.0.2.1:25" -/// ":80" -/// "[2001:db8::1]:80" -/// "198.51.100.1:80" -/// "deno.land:443" -pub fn resolve_addr(address: &str) -> ResolveAddrFuture { - ResolveAddrFuture { - address: address.to_string(), - } -} - -#[derive(Debug)] -pub enum ResolveAddrError { - Syntax, - Resolution(std::io::Error), -} - -impl fmt::Display for ResolveAddrError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.description()) - } -} - -impl Error for ResolveAddrError { - fn description(&self) -> &str { - match self { - ResolveAddrError::Syntax => "invalid address syntax", - ResolveAddrError::Resolution(e) => e.description(), - } - } -} - -pub struct ResolveAddrFuture { - address: String, -} - -impl Future for ResolveAddrFuture { - type Item = SocketAddr; - type Error = ResolveAddrError; - - fn poll(&mut self) -> Poll<Self::Item, Self::Error> { - // The implementation of this is not actually async at the moment, - // however we intend to use async DNS resolution in the future and - // so we expose this as a future instead of Result. - match split(&self.address) { - None => Err(ResolveAddrError::Syntax), - Some(addr_port_pair) => { - // I absolutely despise the .to_socket_addrs() API. - let r = addr_port_pair - .to_socket_addrs() - .map_err(ResolveAddrError::Resolution); - - r.and_then(|mut iter| match iter.next() { - Some(a) => Ok(Async::Ready(a)), - None => panic!("There should be at least one result"), - }) - } - } - } -} - -fn split(address: &str) -> Option<(&str, u16)> { - address.rfind(':').and_then(|i| { - let (a, p) = address.split_at(i); - // Default to localhost if given just the port. Example: ":80" - let addr = if !a.is_empty() { a } else { "0.0.0.0" }; - // If this looks like an ipv6 IP address. Example: "[2001:db8::1]" - // Then we remove the brackets. - let addr = if addr.starts_with('[') && addr.ends_with(']') { - let l = addr.len() - 1; - addr.get(1..l).unwrap() - } else { - addr - }; - - let p = p.trim_start_matches(':'); - match p.parse::<u16>() { - Err(_) => None, - Ok(port) => Some((addr, port)), - } - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::net::Ipv4Addr; - use std::net::Ipv6Addr; - use std::net::SocketAddrV4; - use std::net::SocketAddrV6; - - #[test] - fn split1() { - assert_eq!(split("127.0.0.1:80"), Some(("127.0.0.1", 80))); - } - - #[test] - fn split2() { - assert_eq!(split(":80"), Some(("0.0.0.0", 80))); - } - - #[test] - fn split3() { - assert_eq!(split("no colon"), None); - } - - #[test] - fn split4() { - assert_eq!(split("deno.land:443"), Some(("deno.land", 443))); - } - - #[test] - fn split5() { - assert_eq!(split("[2001:db8::1]:8080"), Some(("2001:db8::1", 8080))); - } - - #[test] - fn resolve_addr1() { - let expected = - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80)); - let actual = resolve_addr("127.0.0.1:80").wait().unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn resolve_addr3() { - let expected = - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 0, 2, 1), 25)); - let actual = resolve_addr("192.0.2.1:25").wait().unwrap(); - assert_eq!(actual, expected); - } - - #[test] - fn resolve_addr_ipv6() { - let expected = SocketAddr::V6(SocketAddrV6::new( - Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), - 8080, - 0, - 0, - )); - let actual = resolve_addr("[2001:db8::1]:8080").wait().unwrap(); - assert_eq!(actual, expected); - } -} diff --git a/src/resources.rs b/src/resources.rs deleted file mode 100644 index 1540f4ff7..000000000 --- a/src/resources.rs +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// Think of Resources as File Descriptors. They are integers that are allocated -// by the privileged side of Deno to refer to various resources. The simplest -// example are standard file system files and stdio - but there will be other -// resources added in the future that might not correspond to operating system -// level File Descriptors. To avoid confusion we call them "resources" not "file -// descriptors". This module implements a global resource table. Ops (AKA -// handlers) look up resources by their integer id here. - -use crate::cli::Buf; -use crate::errors; -use crate::errors::bad_resource; -use crate::errors::DenoError; -use crate::errors::DenoResult; -use crate::http_body::HttpBody; -use crate::isolate_state::WorkerChannels; -use crate::repl::Repl; - -use futures; -use futures::Future; -use futures::Poll; -use futures::Sink; -use futures::Stream; -use hyper; -use std; -use std::collections::HashMap; -use std::io::{Error, Read, Seek, SeekFrom, Write}; -use std::net::{Shutdown, SocketAddr}; -use std::process::ExitStatus; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::{Arc, Mutex}; -use tokio; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio::net::TcpStream; -use tokio_process; - -pub type ResourceId = u32; // Sometimes referred to RID. - -// These store Deno's file descriptors. These are not necessarily the operating -// system ones. -type ResourceTable = HashMap<ResourceId, Repr>; - -#[cfg(not(windows))] -use std::os::unix::io::FromRawFd; - -#[cfg(windows)] -use std::os::windows::io::FromRawHandle; - -#[cfg(windows)] -extern crate winapi; - -lazy_static! { - // Starts at 3 because stdio is [0-2]. - static ref NEXT_RID: AtomicUsize = AtomicUsize::new(3); - static ref RESOURCE_TABLE: Mutex<ResourceTable> = Mutex::new({ - let mut m = HashMap::new(); - // TODO Load these lazily during lookup? - m.insert(0, Repr::Stdin(tokio::io::stdin())); - - m.insert(1, Repr::Stdout({ - #[cfg(not(windows))] - let stdout = unsafe { std::fs::File::from_raw_fd(1) }; - #[cfg(windows)] - let stdout = unsafe { - std::fs::File::from_raw_handle(winapi::um::processenv::GetStdHandle( - winapi::um::winbase::STD_OUTPUT_HANDLE)) - }; - tokio::fs::File::from_std(stdout) - })); - - m.insert(2, Repr::Stderr(tokio::io::stderr())); - m - }); -} - -// Internal representation of Resource. -enum Repr { - Stdin(tokio::io::Stdin), - Stdout(tokio::fs::File), - Stderr(tokio::io::Stderr), - FsFile(tokio::fs::File), - // Since TcpListener might be closed while there is a pending accept task, - // we need to track the task so that when the listener is closed, - // this pending task could be notified and die. - // Currently TcpListener itself does not take care of this issue. - // See: https://github.com/tokio-rs/tokio/issues/846 - TcpListener(tokio::net::TcpListener, Option<futures::task::Task>), - TcpStream(tokio::net::TcpStream), - HttpBody(HttpBody), - Repl(Arc<Mutex<Repl>>), - // Enum size is bounded by the largest variant. - // Use `Box` around large `Child` struct. - // https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant - Child(Box<tokio_process::Child>), - ChildStdin(tokio_process::ChildStdin), - ChildStdout(tokio_process::ChildStdout), - ChildStderr(tokio_process::ChildStderr), - Worker(WorkerChannels), -} - -/// If the given rid is open, this returns the type of resource, E.G. "worker". -/// If the rid is closed or was never open, it returns None. -pub fn get_type(rid: ResourceId) -> Option<String> { - let table = RESOURCE_TABLE.lock().unwrap(); - table.get(&rid).map(inspect_repr) -} - -pub fn table_entries() -> Vec<(u32, String)> { - let table = RESOURCE_TABLE.lock().unwrap(); - - table - .iter() - .map(|(key, value)| (*key, inspect_repr(&value))) - .collect() -} - -#[test] -fn test_table_entries() { - let mut entries = table_entries(); - entries.sort(); - assert_eq!(entries[0], (0, String::from("stdin"))); - assert_eq!(entries[1], (1, String::from("stdout"))); - assert_eq!(entries[2], (2, String::from("stderr"))); -} - -fn inspect_repr(repr: &Repr) -> String { - let h_repr = match repr { - Repr::Stdin(_) => "stdin", - Repr::Stdout(_) => "stdout", - Repr::Stderr(_) => "stderr", - Repr::FsFile(_) => "fsFile", - Repr::TcpListener(_, _) => "tcpListener", - Repr::TcpStream(_) => "tcpStream", - Repr::HttpBody(_) => "httpBody", - Repr::Repl(_) => "repl", - Repr::Child(_) => "child", - Repr::ChildStdin(_) => "childStdin", - Repr::ChildStdout(_) => "childStdout", - Repr::ChildStderr(_) => "childStderr", - Repr::Worker(_) => "worker", - }; - - String::from(h_repr) -} - -// Abstract async file interface. -// Ideally in unix, if Resource represents an OS rid, it will be the same. -#[derive(Clone, Debug)] -pub struct Resource { - pub rid: ResourceId, -} - -impl Resource { - // TODO Should it return a Resource instead of net::TcpStream? - pub fn poll_accept(&mut self) -> Poll<(TcpStream, SocketAddr), Error> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - None => Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Listener has been closed", - )), - Some(repr) => match repr { - Repr::TcpListener(ref mut s, _) => s.poll_accept(), - _ => panic!("Cannot accept"), - }, - } - } - - // close(2) is done by dropping the value. Therefore we just need to remove - // the resource from the RESOURCE_TABLE. - pub fn close(&self) { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let r = table.remove(&self.rid); - assert!(r.is_some()); - } - - pub fn shutdown(&mut self, how: Shutdown) -> Result<(), DenoError> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - None => panic!("bad rid"), - Some(repr) => match repr { - Repr::TcpStream(ref mut f) => { - TcpStream::shutdown(f, how).map_err(DenoError::from) - } - _ => panic!("Cannot shutdown"), - }, - } - } -} - -impl Read for Resource { - fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> { - unimplemented!(); - } -} - -impl AsyncRead for Resource { - fn poll_read(&mut self, buf: &mut [u8]) -> Poll<usize, Error> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - None => panic!("bad rid"), - Some(repr) => match repr { - Repr::FsFile(ref mut f) => f.poll_read(buf), - Repr::Stdin(ref mut f) => f.poll_read(buf), - Repr::TcpStream(ref mut f) => f.poll_read(buf), - Repr::HttpBody(ref mut f) => f.poll_read(buf), - Repr::ChildStdout(ref mut f) => f.poll_read(buf), - Repr::ChildStderr(ref mut f) => f.poll_read(buf), - _ => panic!("Cannot read"), - }, - } - } -} - -impl Write for Resource { - fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> { - unimplemented!() - } - - fn flush(&mut self) -> std::io::Result<()> { - unimplemented!() - } -} - -impl AsyncWrite for Resource { - fn poll_write(&mut self, buf: &[u8]) -> Poll<usize, Error> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - None => panic!("bad rid"), - Some(repr) => match repr { - Repr::FsFile(ref mut f) => f.poll_write(buf), - Repr::Stdout(ref mut f) => f.poll_write(buf), - Repr::Stderr(ref mut f) => f.poll_write(buf), - Repr::TcpStream(ref mut f) => f.poll_write(buf), - Repr::ChildStdin(ref mut f) => f.poll_write(buf), - _ => panic!("Cannot write"), - }, - } - } - - fn shutdown(&mut self) -> futures::Poll<(), std::io::Error> { - unimplemented!() - } -} - -fn new_rid() -> ResourceId { - let next_rid = NEXT_RID.fetch_add(1, Ordering::SeqCst); - next_rid as ResourceId -} - -pub fn add_fs_file(fs_file: tokio::fs::File) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - match tg.insert(rid, Repr::FsFile(fs_file)) { - Some(_) => panic!("There is already a file with that rid"), - None => Resource { rid }, - } -} - -pub fn add_tcp_listener(listener: tokio::net::TcpListener) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - let r = tg.insert(rid, Repr::TcpListener(listener, None)); - assert!(r.is_none()); - Resource { rid } -} - -pub fn add_tcp_stream(stream: tokio::net::TcpStream) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - let r = tg.insert(rid, Repr::TcpStream(stream)); - assert!(r.is_none()); - Resource { rid } -} - -pub fn add_hyper_body(body: hyper::Body) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - let body = HttpBody::from(body); - let r = tg.insert(rid, Repr::HttpBody(body)); - assert!(r.is_none()); - Resource { rid } -} - -pub fn add_repl(repl: Repl) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - let r = tg.insert(rid, Repr::Repl(Arc::new(Mutex::new(repl)))); - assert!(r.is_none()); - Resource { rid } -} - -pub fn add_worker(wc: WorkerChannels) -> Resource { - let rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - let r = tg.insert(rid, Repr::Worker(wc)); - assert!(r.is_none()); - Resource { rid } -} - -pub fn worker_post_message( - rid: ResourceId, - buf: Buf, -) -> futures::sink::Send<futures::sync::mpsc::Sender<Buf>> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&rid); - match maybe_repr { - Some(Repr::Worker(ref mut wc)) => { - // unwrap here is incorrect, but doing it anyway - wc.0.clone().send(buf) - } - _ => panic!("bad resource"), // futures::future::err(bad_resource()).into(), - } -} - -pub struct WorkerReceiver { - rid: ResourceId, -} - -// Invert the dumbness that tokio_process causes by making Child itself a future. -impl Future for WorkerReceiver { - type Item = Option<Buf>; - type Error = DenoError; - - fn poll(&mut self) -> Poll<Option<Buf>, DenoError> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - Some(Repr::Worker(ref mut wc)) => wc.1.poll().map_err(|()| { - errors::new(errors::ErrorKind::Other, "recv msg error".to_string()) - }), - _ => Err(bad_resource()), - } - } -} - -pub fn worker_recv_message(rid: ResourceId) -> WorkerReceiver { - WorkerReceiver { rid } -} - -#[cfg_attr(feature = "cargo-clippy", allow(stutter))] -pub struct ChildResources { - pub child_rid: ResourceId, - pub stdin_rid: Option<ResourceId>, - pub stdout_rid: Option<ResourceId>, - pub stderr_rid: Option<ResourceId>, -} - -pub fn add_child(mut c: tokio_process::Child) -> ChildResources { - let child_rid = new_rid(); - let mut tg = RESOURCE_TABLE.lock().unwrap(); - - let mut resources = ChildResources { - child_rid, - stdin_rid: None, - stdout_rid: None, - stderr_rid: None, - }; - - if c.stdin().is_some() { - let stdin = c.stdin().take().unwrap(); - let rid = new_rid(); - let r = tg.insert(rid, Repr::ChildStdin(stdin)); - assert!(r.is_none()); - resources.stdin_rid = Some(rid); - } - if c.stdout().is_some() { - let stdout = c.stdout().take().unwrap(); - let rid = new_rid(); - let r = tg.insert(rid, Repr::ChildStdout(stdout)); - assert!(r.is_none()); - resources.stdout_rid = Some(rid); - } - if c.stderr().is_some() { - let stderr = c.stderr().take().unwrap(); - let rid = new_rid(); - let r = tg.insert(rid, Repr::ChildStderr(stderr)); - assert!(r.is_none()); - resources.stderr_rid = Some(rid); - } - - let r = tg.insert(child_rid, Repr::Child(Box::new(c))); - assert!(r.is_none()); - - resources -} - -pub struct ChildStatus { - rid: ResourceId, -} - -// Invert the dumbness that tokio_process causes by making Child itself a future. -impl Future for ChildStatus { - type Item = ExitStatus; - type Error = DenoError; - - fn poll(&mut self) -> Poll<ExitStatus, DenoError> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&self.rid); - match maybe_repr { - Some(Repr::Child(ref mut child)) => child.poll().map_err(DenoError::from), - _ => Err(bad_resource()), - } - } -} - -pub fn child_status(rid: ResourceId) -> DenoResult<ChildStatus> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&rid); - match maybe_repr { - Some(Repr::Child(ref mut _child)) => Ok(ChildStatus { rid }), - _ => Err(bad_resource()), - } -} - -pub fn get_repl(rid: ResourceId) -> DenoResult<Arc<Mutex<Repl>>> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&rid); - match maybe_repr { - Some(Repr::Repl(ref mut r)) => Ok(r.clone()), - _ => Err(bad_resource()), - } -} - -pub fn lookup(rid: ResourceId) -> Option<Resource> { - debug!("resource lookup {}", rid); - let table = RESOURCE_TABLE.lock().unwrap(); - table.get(&rid).map(|_| Resource { rid }) -} - -// TODO(kevinkassimo): revamp this after the following lands: -// https://github.com/tokio-rs/tokio/pull/785 -pub fn seek( - resource: Resource, - offset: i32, - whence: u32, -) -> Box<dyn Future<Item = (), Error = DenoError> + Send> { - let mut table = RESOURCE_TABLE.lock().unwrap(); - // We take ownership of File here. - // It is put back below while still holding the lock. - let maybe_repr = table.remove(&resource.rid); - match maybe_repr { - None => panic!("bad rid"), - Some(Repr::FsFile(f)) => { - let seek_from = match whence { - 0 => SeekFrom::Start(offset as u64), - 1 => SeekFrom::Current(offset as i64), - 2 => SeekFrom::End(offset as i64), - _ => { - return Box::new(futures::future::err(errors::new( - errors::ErrorKind::InvalidSeekMode, - format!("Invalid seek mode: {}", whence), - ))); - } - }; - // Trait Clone not implemented on tokio::fs::File, - // so convert to std File first. - let std_file = f.into_std(); - // Create a copy and immediately put back. - // We don't want to block other resource ops. - // try_clone() would yield a copy containing the same - // underlying fd, so operations on the copy would also - // affect the one in resource table, and we don't need - // to write back. - let maybe_std_file_copy = std_file.try_clone(); - // Insert the entry back with the same rid. - table.insert( - resource.rid, - Repr::FsFile(tokio_fs::File::from_std(std_file)), - ); - if maybe_std_file_copy.is_err() { - return Box::new(futures::future::err(DenoError::from( - maybe_std_file_copy.unwrap_err(), - ))); - } - let mut std_file_copy = maybe_std_file_copy.unwrap(); - return Box::new(futures::future::lazy(move || { - let result = std_file_copy - .seek(seek_from) - .map(|_| { - return (); - }).map_err(DenoError::from); - futures::future::result(result) - })); - } - _ => panic!("cannot seek"), - } -} diff --git a/src/startup_data.rs b/src/startup_data.rs deleted file mode 100644 index 29ae4db7d..000000000 --- a/src/startup_data.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use deno_core::deno_buf; -use deno_core::{StartupData, StartupScript}; - -pub fn deno_isolate_init() -> StartupData { - if cfg!(feature = "no-snapshot-init") { - debug!("Deno isolate init without snapshots."); - #[cfg(not(feature = "check-only"))] - let source_bytes = - include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/main.js")); - #[cfg(feature = "check-only")] - let source_bytes = vec![]; - - StartupData::Script(StartupScript { - filename: "gen/bundle/main.js".to_string(), - source: std::str::from_utf8(source_bytes).unwrap().to_string(), - }) - } else { - debug!("Deno isolate init with snapshots."); - #[cfg(not(any(feature = "check-only", feature = "no-snapshot-init")))] - let data = - include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/snapshot_deno.bin")); - #[cfg(any(feature = "check-only", feature = "no-snapshot-init"))] - let data = vec![]; - - unsafe { - StartupData::Snapshot(deno_buf::from_raw_parts(data.as_ptr(), data.len())) - } - } -} - -pub fn compiler_isolate_init() -> StartupData { - if cfg!(feature = "no-snapshot-init") { - debug!("Deno isolate init without snapshots."); - #[cfg(not(feature = "check-only"))] - let source_bytes = - include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/bundle/compiler.js")); - #[cfg(feature = "check-only")] - let source_bytes = vec![]; - - StartupData::Script(StartupScript { - filename: "gen/bundle/compiler.js".to_string(), - source: std::str::from_utf8(source_bytes).unwrap().to_string(), - }) - } else { - debug!("Deno isolate init with snapshots."); - #[cfg(not(any(feature = "check-only", feature = "no-snapshot-init")))] - let data = - include_bytes!(concat!(env!("GN_OUT_DIR"), "/gen/snapshot_compiler.bin")); - #[cfg(any(feature = "check-only", feature = "no-snapshot-init"))] - let data = vec![]; - - unsafe { - StartupData::Snapshot(deno_buf::from_raw_parts(data.as_ptr(), data.len())) - } - } -} diff --git a/src/tokio_util.rs b/src/tokio_util.rs deleted file mode 100644 index 810b826b4..000000000 --- a/src/tokio_util.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::resources::Resource; -use futures; -use futures::Future; -use futures::Poll; -use std::io; -use std::mem; -use std::net::SocketAddr; -use tokio; -use tokio::net::TcpStream; - -pub fn run<F>(future: F) -where - F: Future<Item = (), Error = ()> + Send + 'static, -{ - // tokio::runtime::current_thread::run(future) - tokio::run(future) -} - -pub fn block_on<F, R, E>(future: F) -> Result<R, E> -where - F: Send + 'static + Future<Item = R, Error = E>, - R: Send + 'static, - E: Send + 'static, -{ - let (tx, rx) = futures::sync::oneshot::channel(); - tokio::spawn(future.then(move |r| tx.send(r).map_err(|_| unreachable!()))); - rx.wait().unwrap() -} - -// Set the default executor so we can use tokio::spawn(). It's difficult to -// pass around mut references to the runtime, so using with_default is -// preferable. Ideally Tokio would provide this function. -#[cfg(test)] -pub fn init<F>(f: F) -where - F: FnOnce(), -{ - use tokio_executor; - let rt = tokio::runtime::Runtime::new().unwrap(); - let mut executor = rt.executor(); - let mut enter = tokio_executor::enter().expect("Multiple executors at once"); - tokio_executor::with_default(&mut executor, &mut enter, move |_enter| f()); -} - -#[derive(Debug)] -enum AcceptState { - Pending(Resource), - Empty, -} - -/// Simply accepts a connection. -pub fn accept(r: Resource) -> Accept { - Accept { - state: AcceptState::Pending(r), - } -} - -/// A future which can be used to easily read available number of bytes to fill -/// a buffer. -/// -/// Created by the [`read`] function. -#[derive(Debug)] -pub struct Accept { - state: AcceptState, -} - -impl Future for Accept { - type Item = (TcpStream, SocketAddr); - type Error = io::Error; - - fn poll(&mut self) -> Poll<Self::Item, Self::Error> { - let (stream, addr) = match self.state { - AcceptState::Pending(ref mut r) => try_ready!(r.poll_accept()), - AcceptState::Empty => panic!("poll Accept after it's done"), - }; - - match mem::replace(&mut self.state, AcceptState::Empty) { - AcceptState::Pending(_) => Ok((stream, addr).into()), - AcceptState::Empty => panic!("invalid internal state"), - } - } -} - -/// `futures::future::poll_fn` only support `F: FnMut()->Poll<T, E>` -/// However, we require that `F: FnOnce()->Poll<T, E>`. -/// Therefore, we created our version of `poll_fn`. -pub fn poll_fn<T, E, F>(f: F) -> PollFn<F> -where - F: FnOnce() -> Poll<T, E>, -{ - PollFn { inner: Some(f) } -} - -pub struct PollFn<F> { - inner: Option<F>, -} - -impl<T, E, F> Future for PollFn<F> -where - F: FnOnce() -> Poll<T, E>, -{ - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll<T, E> { - let f = self.inner.take().expect("Inner fn has been taken."); - f() - } -} - -pub fn panic_on_error<I, E, F>(f: F) -> impl Future<Item = I, Error = ()> -where - F: Future<Item = I, Error = E>, - E: std::fmt::Debug, -{ - f.map_err(|err| panic!("Future got unexpected error: {:?}", err)) -} diff --git a/src/tokio_write.rs b/src/tokio_write.rs deleted file mode 100644 index 945de375d..000000000 --- a/src/tokio_write.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// TODO Submit this file upstream into tokio-io/src/io/write.rs -use std::io; -use std::mem; - -use futures::{Future, Poll}; -use tokio::io::AsyncWrite; - -/// A future used to write some data to a stream. -/// -/// This is created by the [`write`] top-level method. -/// -/// [`write`]: fn.write.html -#[derive(Debug)] -pub struct Write<A, T> { - state: State<A, T>, -} - -#[derive(Debug)] -enum State<A, T> { - Pending { a: A, buf: T }, - Empty, -} - -/// Creates a future that will write some of the buffer `buf` to -/// the stream `a` provided. -/// -/// Any error which happens during writing will cause both the stream and the -/// buffer to get destroyed. -pub fn write<A, T>(a: A, buf: T) -> Write<A, T> -where - A: AsyncWrite, - T: AsRef<[u8]>, -{ - Write { - state: State::Pending { a, buf }, - } -} - -impl<A, T> Future for Write<A, T> -where - A: AsyncWrite, - T: AsRef<[u8]>, -{ - type Item = (A, T, usize); - type Error = io::Error; - - fn poll(&mut self) -> Poll<(A, T, usize), io::Error> { - let nwritten = match self.state { - State::Pending { - ref mut a, - ref mut buf, - } => try_ready!(a.poll_write(buf.as_ref())), - State::Empty => panic!("poll a Read after it's done"), - }; - - match mem::replace(&mut self.state, State::Empty) { - State::Pending { a, buf } => Ok((a, buf, nwritten).into()), - State::Empty => panic!("invalid internal state"), - } - } -} diff --git a/src/version.rs b/src/version.rs deleted file mode 100644 index e6ec9008b..000000000 --- a/src/version.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -pub const DENO: &str = env!("CARGO_PKG_VERSION"); - -pub fn v8() -> &'static str { - deno_core::v8_version() -} diff --git a/src/workers.rs b/src/workers.rs deleted file mode 100644 index edded7756..000000000 --- a/src/workers.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use crate::cli::Buf; -use crate::cli::Cli; -use crate::flags::DenoFlags; -use crate::isolate::Isolate; -use crate::isolate_state::IsolateState; -use crate::isolate_state::WorkerChannels; -use crate::js_errors::JSErrorColor; -use crate::permissions::DenoPermissions; -use crate::resources; -use crate::tokio_util; -use deno_core::JSError; -use deno_core::StartupData; -use futures::future::lazy; -use futures::sync::mpsc; -use futures::sync::oneshot; -use futures::Future; -use futures::Poll; -use std::sync::Arc; -use std::thread; - -/// Rust interface for WebWorkers. -pub struct Worker { - isolate: Isolate, -} - -impl Worker { - pub fn new( - startup_data: Option<StartupData>, - flags: DenoFlags, - argv: Vec<String>, - permissions: DenoPermissions, - ) -> (Self, WorkerChannels) { - let (worker_in_tx, worker_in_rx) = mpsc::channel::<Buf>(1); - let (worker_out_tx, worker_out_rx) = mpsc::channel::<Buf>(1); - - let internal_channels = (worker_out_tx, worker_in_rx); - let external_channels = (worker_in_tx, worker_out_rx); - - let state = - Arc::new(IsolateState::new(flags, argv, Some(internal_channels))); - - let cli = Cli::new(startup_data, state, permissions); - let isolate = Isolate::new(cli); - - let worker = Worker { isolate }; - (worker, external_channels) - } - - pub fn execute(&mut self, js_source: &str) -> Result<(), JSError> { - self.isolate.execute(js_source) - } -} - -impl Future for Worker { - type Item = (); - type Error = JSError; - - fn poll(&mut self) -> Poll<(), JSError> { - self.isolate.poll() - } -} - -pub fn spawn( - startup_data: Option<StartupData>, - state: &IsolateState, - js_source: String, - permissions: DenoPermissions, -) -> resources::Resource { - // TODO This function should return a Future, so that the caller can retrieve - // the JSError if one is thrown. Currently it just prints to stderr and calls - // exit(1). - // let (js_error_tx, js_error_rx) = oneshot::channel::<JSError>(); - let (p, c) = oneshot::channel::<resources::Resource>(); - let builder = thread::Builder::new().name("worker".to_string()); - - let flags = state.flags.clone(); - let argv = state.argv.clone(); - - let _tid = builder - .spawn(move || { - tokio_util::run(lazy(move || { - let (mut worker, external_channels) = - Worker::new(startup_data, flags, argv, permissions); - let resource = resources::add_worker(external_channels); - p.send(resource.clone()).unwrap(); - - worker - .execute("denoMain()") - .expect("worker denoMain failed"); - worker - .execute("workerMain()") - .expect("worker workerMain failed"); - worker.execute(&js_source).expect("worker js_source failed"); - - worker.then(move |r| -> Result<(), ()> { - resource.close(); - debug!("workers.rs after resource close"); - if let Err(err) = r { - eprintln!("{}", JSErrorColor(&err).to_string()); - std::process::exit(1); - } - Ok(()) - }) - })); - - debug!("workers.rs after spawn"); - }).unwrap(); - - c.wait().unwrap() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::startup_data; - - #[test] - fn test_spawn() { - let startup_data = startup_data::compiler_isolate_init(); - let resource = spawn( - Some(startup_data), - &IsolateState::mock(), - r#" - onmessage = function(e) { - let s = new TextDecoder().decode(e.data);; - console.log("msg from main script", s); - if (s == "exit") { - close(); - return; - } else { - console.assert(s === "hi"); - } - postMessage(new Uint8Array([1, 2, 3])); - console.log("after postMessage"); - } - "#.into(), - DenoPermissions::default(), - ); - let msg = String::from("hi").into_boxed_str().into_boxed_bytes(); - - let r = resources::worker_post_message(resource.rid, msg).wait(); - assert!(r.is_ok()); - - let maybe_msg = - resources::worker_recv_message(resource.rid).wait().unwrap(); - assert!(maybe_msg.is_some()); - assert_eq!(*maybe_msg.unwrap(), [1, 2, 3]); - - let msg = String::from("exit").into_boxed_str().into_boxed_bytes(); - let r = resources::worker_post_message(resource.rid, msg).wait(); - assert!(r.is_ok()); - } - - #[test] - fn removed_from_resource_table_on_close() { - let startup_data = startup_data::compiler_isolate_init(); - let resource = spawn( - Some(startup_data), - &IsolateState::mock(), - "onmessage = () => close();".into(), - DenoPermissions::default(), - ); - - assert_eq!( - resources::get_type(resource.rid), - Some("worker".to_string()) - ); - - let msg = String::from("hi").into_boxed_str().into_boxed_bytes(); - let r = resources::worker_post_message(resource.rid, msg).wait(); - assert!(r.is_ok()); - println!("rid {:?}", resource.rid); - - // TODO Need a way to get a future for when a resource closes. - // For now, just sleep for a bit. - // resource.close(); - thread::sleep(std::time::Duration::from_millis(1000)); - assert_eq!(resources::get_type(resource.rid), None); - } -} |