summaryrefslogtreecommitdiff
path: root/cli/ops
diff options
context:
space:
mode:
Diffstat (limited to 'cli/ops')
-rw-r--r--cli/ops/compiler.rs99
-rw-r--r--cli/ops/dispatch_json.rs111
-rw-r--r--cli/ops/dispatch_minimal.rs110
-rw-r--r--cli/ops/errors.rs56
-rw-r--r--cli/ops/fetch.rs73
-rw-r--r--cli/ops/files.rs136
-rw-r--r--cli/ops/fs.rs525
-rw-r--r--cli/ops/io.rs46
-rw-r--r--cli/ops/metrics.rs21
-rw-r--r--cli/ops/mod.rs25
-rw-r--r--cli/ops/net.rs151
-rw-r--r--cli/ops/os.rs157
-rw-r--r--cli/ops/performance.rs30
-rw-r--r--cli/ops/permissions.rs44
-rw-r--r--cli/ops/process.rs157
-rw-r--r--cli/ops/random.rs24
-rw-r--r--cli/ops/repl.rs50
-rw-r--r--cli/ops/resources.rs14
-rw-r--r--cli/ops/timers.rs42
-rw-r--r--cli/ops/tls.rs76
-rw-r--r--cli/ops/workers.rs227
21 files changed, 2174 insertions, 0 deletions
diff --git a/cli/ops/compiler.rs b/cli/ops/compiler.rs
new file mode 100644
index 000000000..4228842dd
--- /dev/null
+++ b/cli/ops/compiler.rs
@@ -0,0 +1,99 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::futures::future::join_all;
+use crate::futures::Future;
+use crate::state::ThreadSafeState;
+use deno::*;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct CacheArgs {
+ module_id: String,
+ contents: String,
+ extension: String,
+}
+
+pub fn op_cache(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: CacheArgs = serde_json::from_value(args)?;
+
+ let module_specifier = ModuleSpecifier::resolve_url(&args.module_id)
+ .expect("Should be valid module specifier");
+
+ state.ts_compiler.cache_compiler_output(
+ &module_specifier,
+ &args.extension,
+ &args.contents,
+ )?;
+
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+struct FetchSourceFilesArgs {
+ specifiers: Vec<String>,
+ referrer: String,
+}
+
+pub fn op_fetch_source_files(
+ state: &ThreadSafeState,
+ args: Value,
+ _data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: FetchSourceFilesArgs = serde_json::from_value(args)?;
+
+ // TODO(ry) Maybe a security hole. Only the compiler worker should have access
+ // to this. Need a test to demonstrate the hole.
+ let is_dyn_import = false;
+
+ let mut futures = vec![];
+ for specifier in &args.specifiers {
+ let resolved_specifier =
+ state.resolve(specifier, &args.referrer, false, is_dyn_import)?;
+ let fut = state
+ .file_fetcher
+ .fetch_source_file_async(&resolved_specifier);
+ futures.push(fut);
+ }
+
+ let future = join_all(futures)
+ .map_err(ErrBox::from)
+ .and_then(move |files| {
+ let res = files
+ .into_iter()
+ .map(|file| {
+ json!({
+ "url": file.url.to_string(),
+ "filename": file.filename.to_str().unwrap(),
+ "mediaType": file.media_type as i32,
+ "sourceCode": String::from_utf8(file.source_code).unwrap(),
+ })
+ })
+ .collect();
+
+ futures::future::ok(res)
+ });
+
+ Ok(JsonOp::Async(Box::new(future)))
+}
+
+#[derive(Deserialize)]
+struct FetchAssetArgs {
+ name: String,
+}
+
+pub fn op_fetch_asset(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: FetchAssetArgs = serde_json::from_value(args)?;
+ if let Some(source_code) = crate::js::get_asset(&args.name) {
+ Ok(JsonOp::Sync(json!(source_code)))
+ } else {
+ panic!("op_fetch_asset bad asset {}", args.name)
+ }
+}
diff --git a/cli/ops/dispatch_json.rs b/cli/ops/dispatch_json.rs
new file mode 100644
index 000000000..3a8faf2a8
--- /dev/null
+++ b/cli/ops/dispatch_json.rs
@@ -0,0 +1,111 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use crate::tokio_util;
+use deno::*;
+use futures::Future;
+use futures::Poll;
+pub use serde_derive::Deserialize;
+use serde_json::json;
+pub use serde_json::Value;
+
+pub type AsyncJsonOp = Box<dyn Future<Item = Value, Error = ErrBox> + Send>;
+
+pub enum JsonOp {
+ Sync(Value),
+ Async(AsyncJsonOp),
+}
+
+fn json_err(err: ErrBox) -> Value {
+ use crate::deno_error::GetErrorKind;
+ json!({
+ "message": err.to_string(),
+ "kind": err.kind() as u32,
+ })
+}
+
+fn serialize_result(
+ promise_id: Option<u64>,
+ result: Result<Value, ErrBox>,
+) -> Buf {
+ let value = match result {
+ Ok(v) => json!({ "ok": v, "promiseId": promise_id }),
+ Err(err) => json!({ "err": json_err(err), "promiseId": promise_id }),
+ };
+ let mut vec = serde_json::to_vec(&value).unwrap();
+ debug!("JSON response pre-align, len={}", vec.len());
+ // Align to 32bit word, padding with the space character.
+ vec.resize((vec.len() + 3usize) & !3usize, b' ');
+ vec.into_boxed_slice()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct AsyncArgs {
+ promise_id: Option<u64>,
+}
+
+pub fn json_op<D>(d: D) -> impl Fn(&[u8], Option<PinnedBuf>) -> CoreOp
+where
+ D: Fn(Value, Option<PinnedBuf>) -> Result<JsonOp, ErrBox>,
+{
+ move |control: &[u8], zero_copy: Option<PinnedBuf>| {
+ let async_args: AsyncArgs = serde_json::from_slice(control).unwrap();
+ let promise_id = async_args.promise_id;
+ let is_sync = promise_id.is_none();
+
+ let result = serde_json::from_slice(control)
+ .map_err(ErrBox::from)
+ .and_then(|args| d(args, zero_copy));
+
+ // Convert to CoreOp
+ match result {
+ Ok(JsonOp::Sync(sync_value)) => {
+ assert!(promise_id.is_none());
+ CoreOp::Sync(serialize_result(promise_id, Ok(sync_value)))
+ }
+ Ok(JsonOp::Async(fut)) => {
+ assert!(promise_id.is_some());
+ let fut2 = Box::new(fut.then(move |result| -> Result<Buf, ()> {
+ Ok(serialize_result(promise_id, result))
+ }));
+ CoreOp::Async(fut2)
+ }
+ Err(sync_err) => {
+ let buf = serialize_result(promise_id, Err(sync_err));
+ if is_sync {
+ CoreOp::Sync(buf)
+ } else {
+ CoreOp::Async(Box::new(futures::future::ok(buf)))
+ }
+ }
+ }
+ }
+}
+
+// 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_json<F>(f: F) -> Poll<Value, ErrBox>
+where
+ F: FnOnce() -> Result<Value, ErrBox>,
+{
+ use futures::Async::*;
+ match tokio_threadpool::blocking(f) {
+ Ok(Ready(Ok(v))) => Ok(Ready(v)),
+ Ok(Ready(Err(err))) => Err(err),
+ Ok(NotReady) => Ok(NotReady),
+ Err(err) => panic!("blocking error {}", err),
+ }
+}
+
+pub fn blocking_json<F>(is_sync: bool, f: F) -> Result<JsonOp, ErrBox>
+where
+ F: 'static + Send + FnOnce() -> Result<Value, ErrBox>,
+{
+ if is_sync {
+ Ok(JsonOp::Sync(f()?))
+ } else {
+ Ok(JsonOp::Async(Box::new(futures::sync::oneshot::spawn(
+ tokio_util::poll_fn(move || convert_blocking_json(f)),
+ &tokio_executor::DefaultExecutor::current(),
+ ))))
+ }
+}
diff --git a/cli/ops/dispatch_minimal.rs b/cli/ops/dispatch_minimal.rs
new file mode 100644
index 000000000..618a040bf
--- /dev/null
+++ b/cli/ops/dispatch_minimal.rs
@@ -0,0 +1,110 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+// Do not add flatbuffer dependencies to this module.
+//! Connects to js/dispatch_minimal.ts sendAsyncMinimal This acts as a faster
+//! alternative to flatbuffers using a very simple list of int32s to lay out
+//! messages. The first i32 is used to determine if a message a flatbuffer
+//! message or a "minimal" message.
+use deno::Buf;
+use deno::CoreOp;
+use deno::ErrBox;
+use deno::Op;
+use deno::PinnedBuf;
+use futures::Future;
+
+pub type MinimalOp = dyn Future<Item = i32, Error = ErrBox> + Send;
+pub type Dispatcher = fn(i32, Option<PinnedBuf>) -> Box<MinimalOp>;
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+// This corresponds to RecordMinimal on the TS side.
+pub struct Record {
+ pub promise_id: i32,
+ pub arg: i32,
+ pub result: i32,
+}
+
+impl Into<Buf> for Record {
+ fn into(self) -> Buf {
+ let vec = vec![self.promise_id, self.arg, self.result];
+ let buf32 = vec.into_boxed_slice();
+ let ptr = Box::into_raw(buf32) as *mut [u8; 3 * 4];
+ unsafe { Box::from_raw(ptr) }
+ }
+}
+
+pub fn parse_min_record(bytes: &[u8]) -> Option<Record> {
+ if bytes.len() % std::mem::size_of::<i32>() != 0 {
+ return None;
+ }
+ let p = bytes.as_ptr();
+ #[allow(clippy::cast_ptr_alignment)]
+ let p32 = p as *const i32;
+ let s = unsafe { std::slice::from_raw_parts(p32, bytes.len() / 4) };
+
+ if s.len() != 3 {
+ return None;
+ }
+ let ptr = s.as_ptr();
+ let ints = unsafe { std::slice::from_raw_parts(ptr, 3) };
+ Some(Record {
+ promise_id: ints[0],
+ arg: ints[1],
+ result: ints[2],
+ })
+}
+
+#[test]
+fn test_parse_min_record() {
+ let buf = vec![1, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0];
+ assert_eq!(
+ parse_min_record(&buf),
+ Some(Record {
+ promise_id: 1,
+ arg: 3,
+ result: 4,
+ })
+ );
+
+ let buf = vec![];
+ assert_eq!(parse_min_record(&buf), None);
+
+ let buf = vec![5];
+ assert_eq!(parse_min_record(&buf), None);
+}
+
+pub fn minimal_op(
+ d: Dispatcher,
+) -> impl Fn(&[u8], Option<PinnedBuf>) -> CoreOp {
+ move |control: &[u8], zero_copy: Option<PinnedBuf>| {
+ let mut record = parse_min_record(control).unwrap();
+ let is_sync = record.promise_id == 0;
+ let rid = record.arg;
+ let min_op = d(rid, zero_copy);
+
+ // Convert to CoreOp
+ let fut = Box::new(min_op.then(move |result| -> Result<Buf, ()> {
+ match result {
+ Ok(r) => {
+ record.result = r;
+ }
+ Err(err) => {
+ // TODO(ry) The dispatch_minimal doesn't properly pipe errors back to
+ // the caller.
+ debug!("swallowed err {}", err);
+ record.result = -1;
+ }
+ }
+ Ok(record.into())
+ }));
+
+ if is_sync {
+ // Warning! Possible deadlocks can occur if we try to wait for a future
+ // while in a future. The safe but expensive alternative is to use
+ // tokio_util::block_on.
+ // This block is only exercised for readSync and writeSync, which I think
+ // works since they're simple polling futures.
+ Op::Sync(fut.wait().unwrap())
+ } else {
+ Op::Async(fut)
+ }
+ }
+}
diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs
new file mode 100644
index 000000000..cd21a3880
--- /dev/null
+++ b/cli/ops/errors.rs
@@ -0,0 +1,56 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::fmt_errors::JSError;
+use crate::source_maps::get_orig_position;
+use crate::source_maps::CachedMaps;
+use crate::state::ThreadSafeState;
+use deno::*;
+use std::collections::HashMap;
+
+#[derive(Deserialize)]
+struct FormatErrorArgs {
+ error: String,
+}
+
+pub fn op_format_error(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: FormatErrorArgs = serde_json::from_value(args)?;
+ let error = JSError::from_json(&args.error, &state.ts_compiler);
+
+ Ok(JsonOp::Sync(json!({
+ "error": error.to_string(),
+ })))
+}
+
+#[derive(Deserialize)]
+struct ApplySourceMap {
+ filename: String,
+ line: i32,
+ column: i32,
+}
+
+pub fn op_apply_source_map(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ApplySourceMap = serde_json::from_value(args)?;
+
+ let mut mappings_map: CachedMaps = HashMap::new();
+ let (orig_filename, orig_line, orig_column) = get_orig_position(
+ args.filename,
+ args.line.into(),
+ args.column.into(),
+ &mut mappings_map,
+ &state.ts_compiler,
+ );
+
+ Ok(JsonOp::Sync(json!({
+ "filename": orig_filename.to_string(),
+ "line": orig_line as u32,
+ "column": orig_column as u32,
+ })))
+}
diff --git a/cli/ops/fetch.rs b/cli/ops/fetch.rs
new file mode 100644
index 000000000..f69065f1d
--- /dev/null
+++ b/cli/ops/fetch.rs
@@ -0,0 +1,73 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::http_util::get_client;
+use crate::resources;
+use crate::state::ThreadSafeState;
+use deno::*;
+use http::header::HeaderName;
+use http::header::HeaderValue;
+use http::Method;
+use hyper;
+use hyper::rt::Future;
+use std;
+use std::convert::From;
+
+#[derive(Deserialize)]
+struct FetchArgs {
+ method: Option<String>,
+ url: String,
+ headers: Vec<(String, String)>,
+}
+
+pub fn op_fetch(
+ state: &ThreadSafeState,
+ args: Value,
+ data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: FetchArgs = serde_json::from_value(args)?;
+ let url = args.url;
+
+ let client = get_client();
+
+ let method = match args.method {
+ Some(method_str) => Method::from_bytes(method_str.as_bytes())?,
+ None => Method::GET,
+ };
+
+ let url_ = url::Url::parse(&url).map_err(ErrBox::from)?;
+ state.check_net_url(&url_)?;
+
+ let mut request = client.request(method, url_);
+
+ if let Some(buf) = data {
+ request = request.body(Vec::from(&*buf));
+ }
+
+ for (key, value) in args.headers {
+ let name = HeaderName::from_bytes(key.as_bytes()).unwrap();
+ let v = HeaderValue::from_str(&value).unwrap();
+ request = request.header(name, v);
+ }
+ debug!("Before fetch {}", url);
+ let future = request.send().map_err(ErrBox::from).and_then(move |res| {
+ let status = res.status();
+ let mut res_headers = Vec::new();
+ for (key, val) in res.headers().iter() {
+ res_headers.push((key.to_string(), val.to_str().unwrap().to_owned()));
+ }
+
+ let body = res.into_body();
+ let body_resource = resources::add_reqwest_body(body);
+
+ let json_res = json!({
+ "bodyRid": body_resource.rid,
+ "status": status.as_u16(),
+ "statusText": status.canonical_reason().unwrap_or(""),
+ "headers": res_headers
+ });
+
+ futures::future::ok(json_res)
+ });
+
+ Ok(JsonOp::Async(Box::new(future)))
+}
diff --git a/cli/ops/files.rs b/cli/ops/files.rs
new file mode 100644
index 000000000..01abff3a9
--- /dev/null
+++ b/cli/ops/files.rs
@@ -0,0 +1,136 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::fs as deno_fs;
+use crate::resources;
+use crate::state::ThreadSafeState;
+use deno::*;
+use futures::Future;
+use std;
+use std::convert::From;
+use tokio;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct OpenArgs {
+ promise_id: Option<u64>,
+ filename: String,
+ mode: String,
+}
+
+pub fn op_open(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: OpenArgs = serde_json::from_value(args)?;
+ let (filename, filename_) = deno_fs::resolve_from_cwd(&args.filename)?;
+ let mode = args.mode.as_ref();
+
+ 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" => {
+ state.check_read(&filename_)?;
+ }
+ "w" | "a" | "x" => {
+ state.check_write(&filename_)?;
+ }
+ &_ => {
+ state.check_read(&filename_)?;
+ state.check_write(&filename_)?;
+ }
+ }
+
+ let is_sync = args.promise_id.is_none();
+ let op = open_options.open(filename).map_err(ErrBox::from).and_then(
+ move |fs_file| {
+ let resource = resources::add_fs_file(fs_file);
+ futures::future::ok(json!(resource.rid))
+ },
+ );
+
+ if is_sync {
+ let buf = op.wait()?;
+ Ok(JsonOp::Sync(buf))
+ } else {
+ Ok(JsonOp::Async(Box::new(op)))
+ }
+}
+
+#[derive(Deserialize)]
+struct CloseArgs {
+ rid: i32,
+}
+
+pub fn op_close(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: CloseArgs = serde_json::from_value(args)?;
+
+ let resource = resources::lookup(args.rid as u32)?;
+ resource.close();
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct SeekArgs {
+ promise_id: Option<u64>,
+ rid: i32,
+ offset: i32,
+ whence: i32,
+}
+
+pub fn op_seek(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: SeekArgs = serde_json::from_value(args)?;
+
+ let resource = resources::lookup(args.rid as u32)?;
+ let op = resources::seek(resource, args.offset, args.whence as u32)
+ .and_then(move |_| futures::future::ok(json!({})));
+ if args.promise_id.is_none() {
+ let buf = op.wait()?;
+ Ok(JsonOp::Sync(buf))
+ } else {
+ Ok(JsonOp::Async(Box::new(op)))
+ }
+}
diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs
new file mode 100644
index 000000000..c549bef32
--- /dev/null
+++ b/cli/ops/fs.rs
@@ -0,0 +1,525 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+// Some deserializer fields are only used on Unix and Windows build fails without it
+#![allow(dead_code)]
+use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value};
+use crate::deno_error::DenoError;
+use crate::deno_error::ErrorKind;
+use crate::fs as deno_fs;
+use crate::state::ThreadSafeState;
+use deno::*;
+use remove_dir_all::remove_dir_all;
+use std::convert::From;
+use std::fs;
+use std::path::PathBuf;
+use std::time::UNIX_EPOCH;
+
+#[cfg(unix)]
+use std::os::unix::fs::PermissionsExt;
+
+#[derive(Deserialize)]
+struct ChdirArgs {
+ directory: String,
+}
+
+pub fn op_chdir(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ChdirArgs = serde_json::from_value(args)?;
+ std::env::set_current_dir(&args.directory)?;
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MkdirArgs {
+ promise_id: Option<u64>,
+ path: String,
+ recursive: bool,
+ mode: u32,
+}
+
+pub fn op_mkdir(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: MkdirArgs = serde_json::from_value(args)?;
+ let (path, path_) = deno_fs::resolve_from_cwd(args.path.as_ref())?;
+
+ state.check_write(&path_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_mkdir {}", path_);
+ deno_fs::mkdir(&path, args.mode, args.recursive)?;
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ChmodArgs {
+ promise_id: Option<u64>,
+ path: String,
+ mode: u32,
+}
+
+pub fn op_chmod(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ChmodArgs = serde_json::from_value(args)?;
+ let (path, path_) = deno_fs::resolve_from_cwd(args.path.as_ref())?;
+
+ state.check_write(&path_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_chmod {}", &path_);
+ // Still check file/dir exists on windows
+ let _metadata = fs::metadata(&path)?;
+ #[cfg(any(unix))]
+ {
+ let mut permissions = _metadata.permissions();
+ permissions.set_mode(args.mode);
+ fs::set_permissions(&path, permissions)?;
+ }
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ChownArgs {
+ promise_id: Option<u64>,
+ path: String,
+ uid: u32,
+ gid: u32,
+}
+
+pub fn op_chown(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ChownArgs = serde_json::from_value(args)?;
+
+ state.check_write(&args.path)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_chown {}", &args.path);
+ match deno_fs::chown(args.path.as_ref(), args.uid, args.gid) {
+ Ok(_) => Ok(json!({})),
+ Err(e) => Err(e),
+ }
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RemoveArgs {
+ promise_id: Option<u64>,
+ path: String,
+ recursive: bool,
+}
+
+pub fn op_remove(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: RemoveArgs = serde_json::from_value(args)?;
+ let (path, path_) = deno_fs::resolve_from_cwd(args.path.as_ref())?;
+ let recursive = args.recursive;
+
+ state.check_write(&path_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_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(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct CopyFileArgs {
+ promise_id: Option<u64>,
+ from: String,
+ to: String,
+}
+
+pub fn op_copy_file(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: CopyFileArgs = serde_json::from_value(args)?;
+
+ let (from, from_) = deno_fs::resolve_from_cwd(args.from.as_ref())?;
+ let (to, to_) = deno_fs::resolve_from_cwd(args.to.as_ref())?;
+
+ state.check_read(&from_)?;
+ state.check_write(&to_)?;
+
+ debug!("op_copy_file {} {}", from.display(), to.display());
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_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(
+ DenoError::new(ErrorKind::NotFound, "File not found".to_string())
+ .into(),
+ );
+ }
+
+ fs::copy(&from, &to)?;
+ Ok(json!({}))
+ })
+}
+
+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
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct StatArgs {
+ promise_id: Option<u64>,
+ filename: String,
+ lstat: bool,
+}
+
+pub fn op_stat(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: StatArgs = serde_json::from_value(args)?;
+
+ let (filename, filename_) =
+ deno_fs::resolve_from_cwd(args.filename.as_ref())?;
+ let lstat = args.lstat;
+
+ state.check_read(&filename_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_stat {} {}", filename.display(), lstat);
+ let metadata = if lstat {
+ fs::symlink_metadata(&filename)?
+ } else {
+ fs::metadata(&filename)?
+ };
+
+ Ok(json!({
+ "isFile": metadata.is_file(),
+ "isSymlink": 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()),
+ "hasMode": cfg!(target_family = "unix"), // false on windows,
+ }))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ReadDirArgs {
+ promise_id: Option<u64>,
+ path: String,
+}
+
+pub fn op_read_dir(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ReadDirArgs = serde_json::from_value(args)?;
+ let (path, path_) = deno_fs::resolve_from_cwd(args.path.as_ref())?;
+
+ state.check_read(&path_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_read_dir {}", path.display());
+
+ let entries: Vec<_> = fs::read_dir(path)?
+ .map(|entry| {
+ let entry = entry.unwrap();
+ let metadata = entry.metadata().unwrap();
+ let file_type = metadata.file_type();
+
+ json!({
+ "isFile": file_type.is_file(),
+ "isSymlink": 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()),
+ "name": entry.file_name().to_str().unwrap(),
+ "hasMode": cfg!(target_family = "unix"), // false on windows,
+ })
+ })
+ .collect();
+
+ Ok(json!({ "entries": entries }))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RenameArgs {
+ promise_id: Option<u64>,
+ oldpath: String,
+ newpath: String,
+}
+
+pub fn op_rename(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: RenameArgs = serde_json::from_value(args)?;
+
+ let (oldpath, oldpath_) = deno_fs::resolve_from_cwd(args.oldpath.as_ref())?;
+ let (newpath, newpath_) = deno_fs::resolve_from_cwd(args.newpath.as_ref())?;
+
+ state.check_read(&oldpath_)?;
+ state.check_write(&oldpath_)?;
+ state.check_write(&newpath_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_rename {} {}", oldpath.display(), newpath.display());
+ fs::rename(&oldpath, &newpath)?;
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct LinkArgs {
+ promise_id: Option<u64>,
+ oldname: String,
+ newname: String,
+}
+
+pub fn op_link(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: LinkArgs = serde_json::from_value(args)?;
+
+ let (oldname, oldname_) = deno_fs::resolve_from_cwd(args.oldname.as_ref())?;
+ let (newname, newname_) = deno_fs::resolve_from_cwd(args.newname.as_ref())?;
+
+ state.check_read(&oldname_)?;
+ state.check_write(&newname_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_link {} {}", oldname.display(), newname.display());
+ std::fs::hard_link(&oldname, &newname)?;
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct SymlinkArgs {
+ promise_id: Option<u64>,
+ oldname: String,
+ newname: String,
+}
+
+pub fn op_symlink(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: SymlinkArgs = serde_json::from_value(args)?;
+
+ let (oldname, _oldname_) = deno_fs::resolve_from_cwd(args.oldname.as_ref())?;
+ let (newname, newname_) = deno_fs::resolve_from_cwd(args.newname.as_ref())?;
+
+ state.check_write(&newname_)?;
+ // TODO Use type for Windows.
+ if cfg!(windows) {
+ return Err(
+ DenoError::new(ErrorKind::Other, "Not implemented".to_string()).into(),
+ );
+ }
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_symlink {} {}", oldname.display(), newname.display());
+ #[cfg(any(unix))]
+ std::os::unix::fs::symlink(&oldname, &newname)?;
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ReadLinkArgs {
+ promise_id: Option<u64>,
+ name: String,
+}
+
+pub fn op_read_link(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ReadLinkArgs = serde_json::from_value(args)?;
+
+ let (name, name_) = deno_fs::resolve_from_cwd(args.name.as_ref())?;
+
+ state.check_read(&name_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_read_link {}", name.display());
+ let path = fs::read_link(&name)?;
+ let path_str = path.to_str().unwrap();
+
+ Ok(json!(path_str))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct TruncateArgs {
+ promise_id: Option<u64>,
+ name: String,
+ len: u64,
+}
+
+pub fn op_truncate(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: TruncateArgs = serde_json::from_value(args)?;
+
+ let (filename, filename_) = deno_fs::resolve_from_cwd(args.name.as_ref())?;
+ let len = args.len;
+
+ state.check_write(&filename_)?;
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_truncate {} {}", filename_, len);
+ let f = fs::OpenOptions::new().write(true).open(&filename)?;
+ f.set_len(len)?;
+ Ok(json!({}))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MakeTempDirArgs {
+ promise_id: Option<u64>,
+ dir: Option<String>,
+ prefix: Option<String>,
+ suffix: Option<String>,
+}
+
+pub fn op_make_temp_dir(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: MakeTempDirArgs = serde_json::from_value(args)?;
+
+ // FIXME
+ state.check_write("make_temp")?;
+
+ let dir = args.dir.map(PathBuf::from);
+ let prefix = args.prefix.map(String::from);
+ let suffix = args.suffix.map(String::from);
+
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ // 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 path_str = path.to_str().unwrap();
+
+ Ok(json!(path_str))
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Utime {
+ promise_id: Option<u64>,
+ filename: String,
+ atime: u64,
+ mtime: u64,
+}
+
+pub fn op_utime(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: Utime = serde_json::from_value(args)?;
+ state.check_write(&args.filename)?;
+ let is_sync = args.promise_id.is_none();
+ blocking_json(is_sync, move || {
+ debug!("op_utimes {} {} {}", args.filename, args.atime, args.mtime);
+ utime::set_file_times(args.filename, args.atime, args.mtime)?;
+ Ok(json!({}))
+ })
+}
+
+pub fn op_cwd(
+ _state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let path = std::env::current_dir()?;
+ let path_str = path.into_os_string().into_string().unwrap();
+ Ok(JsonOp::Sync(json!(path_str)))
+}
diff --git a/cli/ops/io.rs b/cli/ops/io.rs
new file mode 100644
index 000000000..8b8520c35
--- /dev/null
+++ b/cli/ops/io.rs
@@ -0,0 +1,46 @@
+use super::dispatch_minimal::MinimalOp;
+use crate::deno_error;
+use crate::resources;
+use crate::tokio_read;
+use crate::tokio_write;
+use deno::ErrBox;
+use deno::PinnedBuf;
+use futures::Future;
+
+pub fn op_read(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> {
+ debug!("read rid={}", rid);
+ let zero_copy = match zero_copy {
+ None => {
+ return Box::new(futures::future::err(deno_error::no_buffer_specified()))
+ }
+ Some(buf) => buf,
+ };
+
+ match resources::lookup(rid as u32) {
+ Err(e) => Box::new(futures::future::err(e)),
+ Ok(resource) => Box::new(
+ tokio_read::read(resource, zero_copy)
+ .map_err(ErrBox::from)
+ .and_then(move |(_resource, _buf, nread)| Ok(nread as i32)),
+ ),
+ }
+}
+
+pub fn op_write(rid: i32, zero_copy: Option<PinnedBuf>) -> Box<MinimalOp> {
+ debug!("write rid={}", rid);
+ let zero_copy = match zero_copy {
+ None => {
+ return Box::new(futures::future::err(deno_error::no_buffer_specified()))
+ }
+ Some(buf) => buf,
+ };
+
+ match resources::lookup(rid as u32) {
+ Err(e) => Box::new(futures::future::err(e)),
+ Ok(resource) => Box::new(
+ tokio_write::write(resource, zero_copy)
+ .map_err(ErrBox::from)
+ .and_then(move |(_resource, _buf, nwritten)| Ok(nwritten as i32)),
+ ),
+ }
+}
diff --git a/cli/ops/metrics.rs b/cli/ops/metrics.rs
new file mode 100644
index 000000000..e1a23f6c8
--- /dev/null
+++ b/cli/ops/metrics.rs
@@ -0,0 +1,21 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{JsonOp, Value};
+use crate::state::ThreadSafeState;
+use deno::*;
+use std::sync::atomic::Ordering;
+
+pub fn op_metrics(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let m = &state.metrics;
+
+ Ok(JsonOp::Sync(json!({
+ "opsDispatched": m.ops_dispatched.load(Ordering::SeqCst) as u64,
+ "opsCompleted": m.ops_completed.load(Ordering::SeqCst) as u64,
+ "bytesSentControl": m.bytes_sent_control.load(Ordering::SeqCst) as u64,
+ "bytesSentData": m.bytes_sent_data.load(Ordering::SeqCst) as u64,
+ "bytesReceived": m.bytes_received.load(Ordering::SeqCst) as u64
+ })))
+}
diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs
new file mode 100644
index 000000000..0c07adcc9
--- /dev/null
+++ b/cli/ops/mod.rs
@@ -0,0 +1,25 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+mod dispatch_json;
+mod dispatch_minimal;
+
+pub use dispatch_json::json_op;
+pub use dispatch_json::JsonOp;
+pub use dispatch_minimal::minimal_op;
+
+pub mod compiler;
+pub mod errors;
+pub mod fetch;
+pub mod files;
+pub mod fs;
+pub mod io;
+pub mod metrics;
+pub mod net;
+pub mod os;
+pub mod performance;
+pub mod permissions;
+pub mod process;
+pub mod random;
+pub mod repl;
+pub mod resources;
+pub mod timers;
+pub mod workers;
diff --git a/cli/ops/net.rs b/cli/ops/net.rs
new file mode 100644
index 000000000..507eff504
--- /dev/null
+++ b/cli/ops/net.rs
@@ -0,0 +1,151 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::resolve_addr::resolve_addr;
+use crate::resources;
+use crate::resources::Resource;
+use crate::state::ThreadSafeState;
+use crate::tokio_util;
+use deno::*;
+use futures::Future;
+use std;
+use std::convert::From;
+use std::net::Shutdown;
+use tokio;
+use tokio::net::TcpListener;
+use tokio::net::TcpStream;
+
+#[derive(Deserialize)]
+struct AcceptArgs {
+ rid: i32,
+}
+
+pub fn op_accept(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: AcceptArgs = serde_json::from_value(args)?;
+ let server_rid = args.rid as u32;
+
+ let server_resource = resources::lookup(server_rid)?;
+ let op = tokio_util::accept(server_resource)
+ .and_then(move |(tcp_stream, _socket_addr)| {
+ let local_addr = tcp_stream.local_addr()?;
+ let remote_addr = tcp_stream.peer_addr()?;
+ let tcp_stream_resource = resources::add_tcp_stream(tcp_stream);
+ Ok((tcp_stream_resource, local_addr, remote_addr))
+ })
+ .map_err(ErrBox::from)
+ .and_then(move |(tcp_stream_resource, local_addr, remote_addr)| {
+ futures::future::ok(json!({
+ "rid": tcp_stream_resource.rid,
+ "localAddr": local_addr.to_string(),
+ "remoteAddr": remote_addr.to_string(),
+ }))
+ });
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
+
+#[derive(Deserialize)]
+struct DialArgs {
+ transport: String,
+ hostname: String,
+ port: u16,
+}
+
+pub fn op_dial(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: DialArgs = serde_json::from_value(args)?;
+ assert_eq!(args.transport, "tcp"); // TODO Support others.
+
+ // TODO(ry) Using format! is suboptimal here. Better would be if
+ // state.check_net and resolve_addr() took hostname and port directly.
+ let address = format!("{}:{}", args.hostname, args.port);
+
+ state.check_net(&address)?;
+
+ let op = resolve_addr(&address).and_then(move |addr| {
+ TcpStream::connect(&addr)
+ .map_err(ErrBox::from)
+ .and_then(move |tcp_stream| {
+ let local_addr = tcp_stream.local_addr()?;
+ let remote_addr = tcp_stream.peer_addr()?;
+ let tcp_stream_resource = resources::add_tcp_stream(tcp_stream);
+ Ok((tcp_stream_resource, local_addr, remote_addr))
+ })
+ .map_err(ErrBox::from)
+ .and_then(move |(tcp_stream_resource, local_addr, remote_addr)| {
+ futures::future::ok(json!({
+ "rid": tcp_stream_resource.rid,
+ "localAddr": local_addr.to_string(),
+ "remoteAddr": remote_addr.to_string(),
+ }))
+ })
+ });
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
+
+#[derive(Deserialize)]
+struct ShutdownArgs {
+ rid: i32,
+ how: i32,
+}
+
+pub fn op_shutdown(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ShutdownArgs = serde_json::from_value(args)?;
+
+ let rid = args.rid as u32;
+ let how = args.how;
+ let mut resource = resources::lookup(rid)?;
+
+ let shutdown_mode = match how {
+ 0 => Shutdown::Read,
+ 1 => Shutdown::Write,
+ _ => unimplemented!(),
+ };
+
+ // Use UFCS for disambiguation
+ Resource::shutdown(&mut resource, shutdown_mode)?;
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+struct ListenArgs {
+ transport: String,
+ hostname: String,
+ port: u16,
+}
+
+pub fn op_listen(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ListenArgs = serde_json::from_value(args)?;
+ assert_eq!(args.transport, "tcp");
+
+ // TODO(ry) Using format! is suboptimal here. Better would be if
+ // state.check_net and resolve_addr() took hostname and port directly.
+ let address = format!("{}:{}", args.hostname, args.port);
+
+ state.check_net(&address)?;
+
+ let addr = resolve_addr(&address).wait()?;
+ let listener = TcpListener::bind(&addr)?;
+ let local_addr = listener.local_addr()?;
+ let resource = resources::add_tcp_listener(listener);
+
+ Ok(JsonOp::Sync(json!({
+ "rid": resource.rid,
+ "localAddr": local_addr.to_string()
+ })))
+}
diff --git a/cli/ops/os.rs b/cli/ops/os.rs
new file mode 100644
index 000000000..92f640afd
--- /dev/null
+++ b/cli/ops/os.rs
@@ -0,0 +1,157 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::colors;
+use crate::fs as deno_fs;
+use crate::state::ThreadSafeState;
+use crate::version;
+use atty;
+use deno::*;
+use log;
+use std::collections::HashMap;
+use std::env;
+use sys_info;
+use url::Url;
+
+/// BUILD_OS and BUILD_ARCH match the values in Deno.build. See js/build.ts.
+#[cfg(target_os = "macos")]
+static BUILD_OS: &str = "mac";
+#[cfg(target_os = "linux")]
+static BUILD_OS: &str = "linux";
+#[cfg(target_os = "windows")]
+static BUILD_OS: &str = "win";
+#[cfg(target_arch = "x86_64")]
+static BUILD_ARCH: &str = "x64";
+
+pub fn op_start(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ Ok(JsonOp::Sync(json!({
+ "cwd": deno_fs::normalize_path(&env::current_dir().unwrap()),
+ "pid": std::process::id(),
+ "argv": state.argv,
+ "mainModule": state.main_module().map(|x| x.as_str().to_string()),
+ "debugFlag": state
+ .flags
+ .log_level
+ .map_or(false, |l| l == log::Level::Debug),
+ "versionFlag": state.flags.version,
+ "v8Version": version::v8(),
+ "denoVersion": version::DENO,
+ "tsVersion": version::TYPESCRIPT,
+ "noColor": !colors::use_color(),
+ "os": BUILD_OS,
+ "arch": BUILD_ARCH,
+ })))
+}
+
+pub fn op_home_dir(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ state.check_env()?;
+ let path = dirs::home_dir()
+ .unwrap_or_default()
+ .into_os_string()
+ .into_string()
+ .unwrap_or_default();
+ Ok(JsonOp::Sync(json!(path)))
+}
+
+pub fn op_exec_path(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ state.check_env()?;
+ let current_exe = env::current_exe().unwrap();
+ // Now apply URL parser to current exe to get fully resolved path, otherwise
+ // we might get `./` and `../` bits in `exec_path`
+ let exe_url = Url::from_file_path(current_exe).unwrap();
+ let path = exe_url.to_file_path().unwrap();
+ Ok(JsonOp::Sync(json!(path)))
+}
+
+#[derive(Deserialize)]
+struct SetEnv {
+ key: String,
+ value: String,
+}
+
+pub fn op_set_env(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: SetEnv = serde_json::from_value(args)?;
+ state.check_env()?;
+ env::set_var(args.key, args.value);
+ Ok(JsonOp::Sync(json!({})))
+}
+
+pub fn op_env(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ state.check_env()?;
+ let v = env::vars().collect::<HashMap<String, String>>();
+ Ok(JsonOp::Sync(json!(v)))
+}
+
+#[derive(Deserialize)]
+struct GetEnv {
+ key: String,
+}
+
+pub fn op_get_env(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: GetEnv = serde_json::from_value(args)?;
+ state.check_env()?;
+ let r = match env::var(args.key) {
+ Err(env::VarError::NotPresent) => json!([]),
+ v => json!([v?]),
+ };
+ Ok(JsonOp::Sync(r))
+}
+
+#[derive(Deserialize)]
+struct Exit {
+ code: i32,
+}
+
+pub fn op_exit(
+ _s: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: Exit = serde_json::from_value(args)?;
+ std::process::exit(args.code)
+}
+
+pub fn op_is_tty(
+ _s: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ Ok(JsonOp::Sync(json!({
+ "stdin": atty::is(atty::Stream::Stdin),
+ "stdout": atty::is(atty::Stream::Stdout),
+ "stderr": atty::is(atty::Stream::Stderr),
+ })))
+}
+
+pub fn op_hostname(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ state.check_env()?;
+ let hostname = sys_info::hostname().unwrap_or_else(|_| "".to_owned());
+ Ok(JsonOp::Sync(json!(hostname)))
+}
diff --git a/cli/ops/performance.rs b/cli/ops/performance.rs
new file mode 100644
index 000000000..090fc3323
--- /dev/null
+++ b/cli/ops/performance.rs
@@ -0,0 +1,30 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{JsonOp, Value};
+use crate::state::ThreadSafeState;
+use deno::*;
+
+// Returns a milliseconds and nanoseconds subsec
+// since the start time of the deno runtime.
+// If the High precision flag is not set, the
+// nanoseconds are rounded on 2ms.
+pub fn op_now(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let seconds = state.start_time.elapsed().as_secs();
+ let mut subsec_nanos = state.start_time.elapsed().subsec_nanos();
+ let reduced_time_precision = 2_000_000; // 2ms in nanoseconds
+
+ // If the permission is not enabled
+ // Round the nano result on 2 milliseconds
+ // see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
+ if !state.permissions.allows_hrtime() {
+ subsec_nanos -= subsec_nanos % reduced_time_precision
+ }
+
+ Ok(JsonOp::Sync(json!({
+ "seconds": seconds,
+ "subsecNanos": subsec_nanos,
+ })))
+}
diff --git a/cli/ops/permissions.rs b/cli/ops/permissions.rs
new file mode 100644
index 000000000..5d14f39be
--- /dev/null
+++ b/cli/ops/permissions.rs
@@ -0,0 +1,44 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::state::ThreadSafeState;
+use deno::*;
+
+pub fn op_permissions(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ Ok(JsonOp::Sync(json!({
+ "run": state.permissions.allows_run(),
+ "read": state.permissions.allows_read(),
+ "write": state.permissions.allows_write(),
+ "net": state.permissions.allows_net(),
+ "env": state.permissions.allows_env(),
+ "hrtime": state.permissions.allows_hrtime(),
+ })))
+}
+
+#[derive(Deserialize)]
+struct RevokePermissionArgs {
+ permission: String,
+}
+
+pub fn op_revoke_permission(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: RevokePermissionArgs = serde_json::from_value(args)?;
+ let permission = args.permission.as_ref();
+ match permission {
+ "run" => state.permissions.revoke_run(),
+ "read" => state.permissions.revoke_read(),
+ "write" => state.permissions.revoke_write(),
+ "net" => state.permissions.revoke_net(),
+ "env" => state.permissions.revoke_env(),
+ "hrtime" => state.permissions.revoke_hrtime(),
+ _ => Ok(()),
+ }?;
+
+ Ok(JsonOp::Sync(json!({})))
+}
diff --git a/cli/ops/process.rs b/cli/ops/process.rs
new file mode 100644
index 000000000..8dff53c6e
--- /dev/null
+++ b/cli/ops/process.rs
@@ -0,0 +1,157 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::resources;
+use crate::signal::kill;
+use crate::state::ThreadSafeState;
+use deno::*;
+use futures;
+use futures::Future;
+use std;
+use std::convert::From;
+use std::process::Command;
+use tokio_process::CommandExt;
+
+#[cfg(unix)]
+use std::os::unix::process::ExitStatusExt;
+
+fn subprocess_stdio_map(s: &str) -> std::process::Stdio {
+ match s {
+ "inherit" => std::process::Stdio::inherit(),
+ "piped" => std::process::Stdio::piped(),
+ "null" => std::process::Stdio::null(),
+ _ => unreachable!(),
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RunArgs {
+ args: Vec<String>,
+ cwd: Option<String>,
+ env: Vec<(String, String)>,
+ stdin: String,
+ stdout: String,
+ stderr: String,
+ stdin_rid: u32,
+ stdout_rid: u32,
+ stderr_rid: u32,
+}
+
+pub fn op_run(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let run_args: RunArgs = serde_json::from_value(args)?;
+
+ state.check_run()?;
+
+ let args = run_args.args;
+ let env = run_args.env;
+ let cwd = run_args.cwd;
+
+ let mut c = Command::new(args.get(0).unwrap());
+ (1..args.len()).for_each(|i| {
+ let arg = args.get(i).unwrap();
+ c.arg(arg);
+ });
+ cwd.map(|d| c.current_dir(d));
+ for (key, value) in &env {
+ c.env(key, value);
+ }
+
+ // TODO: make this work with other resources, eg. sockets
+ let stdin_rid = run_args.stdin_rid;
+ if stdin_rid > 0 {
+ c.stdin(resources::get_file(stdin_rid)?);
+ } else {
+ c.stdin(subprocess_stdio_map(run_args.stdin.as_ref()));
+ }
+
+ let stdout_rid = run_args.stdout_rid;
+ if stdout_rid > 0 {
+ c.stdout(resources::get_file(stdout_rid)?);
+ } else {
+ c.stdout(subprocess_stdio_map(run_args.stdout.as_ref()));
+ }
+
+ let stderr_rid = run_args.stderr_rid;
+ if stderr_rid > 0 {
+ c.stderr(resources::get_file(stderr_rid)?);
+ } else {
+ c.stderr(subprocess_stdio_map(run_args.stderr.as_ref()));
+ }
+
+ // Spawn the command.
+ let child = c.spawn_async().map_err(ErrBox::from)?;
+
+ let pid = child.id();
+ let resources = resources::add_child(child);
+
+ Ok(JsonOp::Sync(json!({
+ "rid": resources.child_rid,
+ "pid": pid,
+ "stdinRid": resources.stdin_rid,
+ "stdoutRid": resources.stdout_rid,
+ "stderrRid": resources.stderr_rid,
+ })))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct RunStatusArgs {
+ rid: i32,
+}
+
+pub fn op_run_status(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: RunStatusArgs = serde_json::from_value(args)?;
+ let rid = args.rid as u32;
+
+ state.check_run()?;
+
+ let future = resources::child_status(rid)?;
+
+ 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();
+
+ futures::future::ok(json!({
+ "gotSignal": got_signal,
+ "exitCode": code.unwrap_or(-1),
+ "exitSignal": signal.unwrap_or(-1),
+ }))
+ });
+
+ Ok(JsonOp::Async(Box::new(future)))
+}
+
+#[derive(Deserialize)]
+struct KillArgs {
+ pid: i32,
+ signo: i32,
+}
+
+pub fn op_kill(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ state.check_run()?;
+
+ let args: KillArgs = serde_json::from_value(args)?;
+ kill(args.pid, args.signo)?;
+ Ok(JsonOp::Sync(json!({})))
+}
diff --git a/cli/ops/random.rs b/cli/ops/random.rs
new file mode 100644
index 000000000..7470eab40
--- /dev/null
+++ b/cli/ops/random.rs
@@ -0,0 +1,24 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{JsonOp, Value};
+use crate::state::ThreadSafeState;
+use deno::*;
+use rand::thread_rng;
+use rand::Rng;
+
+pub fn op_get_random_values(
+ state: &ThreadSafeState,
+ _args: Value,
+ zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ assert!(zero_copy.is_some());
+
+ if let Some(ref seeded_rng) = state.seeded_rng {
+ let mut rng = seeded_rng.lock().unwrap();
+ rng.fill(&mut zero_copy.unwrap()[..]);
+ } else {
+ let mut rng = thread_rng();
+ rng.fill(&mut zero_copy.unwrap()[..]);
+ }
+
+ Ok(JsonOp::Sync(json!({})))
+}
diff --git a/cli/ops/repl.rs b/cli/ops/repl.rs
new file mode 100644
index 000000000..7ab7509de
--- /dev/null
+++ b/cli/ops/repl.rs
@@ -0,0 +1,50 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value};
+use crate::repl;
+use crate::resources;
+use crate::state::ThreadSafeState;
+use deno::*;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct ReplStartArgs {
+ history_file: String,
+}
+
+pub fn op_repl_start(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ReplStartArgs = serde_json::from_value(args)?;
+
+ debug!("op_repl_start {}", args.history_file);
+ let history_path = repl::history_path(&state.dir, &args.history_file);
+ let repl = repl::Repl::new(history_path);
+ let resource = resources::add_repl(repl);
+
+ Ok(JsonOp::Sync(json!(resource.rid)))
+}
+
+#[derive(Deserialize)]
+struct ReplReadlineArgs {
+ rid: i32,
+ prompt: String,
+}
+
+pub fn op_repl_readline(
+ _state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: ReplReadlineArgs = serde_json::from_value(args)?;
+ let rid = args.rid;
+ let prompt = args.prompt;
+ debug!("op_repl_readline {} {}", rid, prompt);
+
+ blocking_json(false, move || {
+ let repl = resources::get_repl(rid as u32)?;
+ let line = repl.lock().unwrap().readline(&prompt)?;
+ Ok(json!(line))
+ })
+}
diff --git a/cli/ops/resources.rs b/cli/ops/resources.rs
new file mode 100644
index 000000000..dafd01d08
--- /dev/null
+++ b/cli/ops/resources.rs
@@ -0,0 +1,14 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{JsonOp, Value};
+use crate::resources::table_entries;
+use crate::state::ThreadSafeState;
+use deno::*;
+
+pub fn op_resources(
+ _state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let serialized_resources = table_entries();
+ Ok(JsonOp::Sync(json!(serialized_resources)))
+}
diff --git a/cli/ops/timers.rs b/cli/ops/timers.rs
new file mode 100644
index 000000000..abcd5c1b3
--- /dev/null
+++ b/cli/ops/timers.rs
@@ -0,0 +1,42 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::state::ThreadSafeState;
+use deno::*;
+use futures::Future;
+use std;
+use std::time::Duration;
+use std::time::Instant;
+
+pub fn op_global_timer_stop(
+ state: &ThreadSafeState,
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let state = state;
+ let mut t = state.global_timer.lock().unwrap();
+ t.cancel();
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+struct GlobalTimerArgs {
+ timeout: u64,
+}
+
+pub fn op_global_timer(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: GlobalTimerArgs = serde_json::from_value(args)?;
+ let val = args.timeout;
+
+ let state = state;
+ let mut t = state.global_timer.lock().unwrap();
+ let deadline = Instant::now() + Duration::from_millis(val);
+ let f = t
+ .new_timeout(deadline)
+ .then(move |_| futures::future::ok(json!({})));
+
+ Ok(JsonOp::Async(Box::new(f)))
+}
diff --git a/cli/ops/tls.rs b/cli/ops/tls.rs
new file mode 100644
index 000000000..2b1d94f2b
--- /dev/null
+++ b/cli/ops/tls.rs
@@ -0,0 +1,76 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::resolve_addr::resolve_addr;
+use crate::resources;
+use crate::state::ThreadSafeState;
+use deno::*;
+use futures::Future;
+use std;
+use std::convert::From;
+use std::sync::Arc;
+use tokio;
+use tokio::net::TcpStream;
+use tokio_rustls::{rustls::ClientConfig, TlsConnector};
+use webpki;
+use webpki::DNSNameRef;
+use webpki_roots;
+
+#[derive(Deserialize)]
+struct DialTLSArgs {
+ hostname: String,
+ port: u16,
+}
+
+pub fn op_dial_tls(
+ state: &ThreadSafeState,
+ args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: DialTLSArgs = serde_json::from_value(args)?;
+
+ // TODO(ry) Using format! is suboptimal here. Better would be if
+ // state.check_net and resolve_addr() took hostname and port directly.
+ let address = format!("{}:{}", args.hostname, args.port);
+
+ state.check_net(&address)?;
+
+ let mut domain = args.hostname;
+ if domain.is_empty() {
+ domain.push_str("localhost");
+ }
+
+ let op = resolve_addr(&address).and_then(move |addr| {
+ TcpStream::connect(&addr)
+ .and_then(move |tcp_stream| {
+ let local_addr = tcp_stream.local_addr()?;
+ let remote_addr = tcp_stream.peer_addr()?;
+ let mut config = ClientConfig::new();
+ config
+ .root_store
+ .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
+
+ let tls_connector = TlsConnector::from(Arc::new(config));
+ Ok((tls_connector, tcp_stream, local_addr, remote_addr))
+ })
+ .map_err(ErrBox::from)
+ .and_then(
+ move |(tls_connector, tcp_stream, local_addr, remote_addr)| {
+ let dnsname = DNSNameRef::try_from_ascii_str(&domain)
+ .expect("Invalid DNS lookup");
+ tls_connector
+ .connect(dnsname, tcp_stream)
+ .map_err(ErrBox::from)
+ .and_then(move |tls_stream| {
+ let tls_stream_resource = resources::add_tls_stream(tls_stream);
+ futures::future::ok(json!({
+ "rid": tls_stream_resource.rid,
+ "localAddr": local_addr.to_string(),
+ "remoteAddr": remote_addr.to_string(),
+ }))
+ })
+ },
+ )
+ });
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
diff --git a/cli/ops/workers.rs b/cli/ops/workers.rs
new file mode 100644
index 000000000..6950f25d6
--- /dev/null
+++ b/cli/ops/workers.rs
@@ -0,0 +1,227 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use super::dispatch_json::{Deserialize, JsonOp, Value};
+use crate::deno_error::js_check;
+use crate::deno_error::DenoError;
+use crate::deno_error::ErrorKind;
+use crate::resources;
+use crate::startup_data;
+use crate::state::ThreadSafeState;
+use crate::worker::Worker;
+use deno::*;
+use futures;
+use futures::Async;
+use futures::Future;
+use futures::Sink;
+use futures::Stream;
+use std;
+use std::convert::From;
+
+struct GetMessageFuture {
+ pub state: ThreadSafeState,
+}
+
+impl Future for GetMessageFuture {
+ type Item = Option<Buf>;
+ type Error = ();
+
+ fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
+ let mut wc = self.state.worker_channels.lock().unwrap();
+ wc.1
+ .poll()
+ .map_err(|err| panic!("worker_channel recv err {:?}", err))
+ }
+}
+
+/// Get message from host as guest worker
+pub fn op_worker_get_message(
+ state: &ThreadSafeState,
+ _args: Value,
+ _data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let op = GetMessageFuture {
+ state: state.clone(),
+ };
+
+ let op = op
+ .map_err(move |_| -> ErrBox { unimplemented!() })
+ .and_then(move |maybe_buf| {
+ debug!("op_worker_get_message");
+
+ futures::future::ok(json!({
+ "data": maybe_buf.map(|buf| buf.to_owned())
+ }))
+ });
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
+
+/// Post message to host as guest worker
+pub fn op_worker_post_message(
+ state: &ThreadSafeState,
+ _args: Value,
+ data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice();
+
+ let tx = {
+ let wc = state.worker_channels.lock().unwrap();
+ wc.0.clone()
+ };
+ tx.send(d)
+ .wait()
+ .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;
+
+ Ok(JsonOp::Sync(json!({})))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct CreateWorkerArgs {
+ specifier: String,
+ include_deno_namespace: bool,
+ has_source_code: bool,
+ source_code: String,
+}
+
+/// Create worker as the host
+pub fn op_create_worker(
+ state: &ThreadSafeState,
+ args: Value,
+ _data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: CreateWorkerArgs = serde_json::from_value(args)?;
+
+ let specifier = args.specifier.as_ref();
+ // Only include deno namespace if requested AND current worker
+ // has included namespace (to avoid escalation).
+ let include_deno_namespace =
+ args.include_deno_namespace && state.include_deno_namespace;
+ let has_source_code = args.has_source_code;
+ let source_code = args.source_code;
+
+ let parent_state = state.clone();
+
+ let mut module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
+
+ let mut child_argv = parent_state.argv.clone();
+
+ if !has_source_code {
+ if let Some(module) = state.main_module() {
+ module_specifier =
+ ModuleSpecifier::resolve_import(specifier, &module.to_string())?;
+ child_argv[1] = module_specifier.to_string();
+ }
+ }
+
+ let child_state = ThreadSafeState::new(
+ parent_state.flags.clone(),
+ child_argv,
+ parent_state.progress.clone(),
+ include_deno_namespace,
+ )?;
+ let rid = child_state.resource.rid;
+ let name = format!("USER-WORKER-{}", specifier);
+ let deno_main_call = format!("denoMain({})", include_deno_namespace);
+
+ let mut worker =
+ Worker::new(name, startup_data::deno_isolate_init(), child_state);
+ js_check(worker.execute(&deno_main_call));
+ js_check(worker.execute("workerMain()"));
+
+ let exec_cb = move |worker: Worker| {
+ let mut workers_tl = parent_state.workers.lock().unwrap();
+ workers_tl.insert(rid, worker.shared());
+ json!(rid)
+ };
+
+ // Has provided source code, execute immediately.
+ if has_source_code {
+ js_check(worker.execute(&source_code));
+ return Ok(JsonOp::Sync(exec_cb(worker)));
+ }
+
+ let op = worker
+ .execute_mod_async(&module_specifier, false)
+ .and_then(move |()| Ok(exec_cb(worker)));
+
+ let result = op.wait()?;
+ Ok(JsonOp::Sync(result))
+}
+
+#[derive(Deserialize)]
+struct HostGetWorkerClosedArgs {
+ rid: i32,
+}
+
+/// Return when the worker closes
+pub fn op_host_get_worker_closed(
+ state: &ThreadSafeState,
+ args: Value,
+ _data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: HostGetWorkerClosedArgs = serde_json::from_value(args)?;
+
+ let rid = args.rid as u32;
+ let state = state.clone();
+
+ let shared_worker_future = {
+ let workers_tl = state.workers.lock().unwrap();
+ let worker = workers_tl.get(&rid).unwrap();
+ worker.clone()
+ };
+
+ let op = Box::new(
+ shared_worker_future.then(move |_result| futures::future::ok(json!({}))),
+ );
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
+
+#[derive(Deserialize)]
+struct HostGetMessageArgs {
+ rid: i32,
+}
+
+/// Get message from guest worker as host
+pub fn op_host_get_message(
+ _state: &ThreadSafeState,
+ args: Value,
+ _data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: HostGetMessageArgs = serde_json::from_value(args)?;
+
+ let rid = args.rid as u32;
+ let op = resources::get_message_from_worker(rid)
+ .map_err(move |_| -> ErrBox { unimplemented!() })
+ .and_then(move |maybe_buf| {
+ futures::future::ok(json!({
+ "data": maybe_buf.map(|buf| buf.to_owned())
+ }))
+ });
+
+ Ok(JsonOp::Async(Box::new(op)))
+}
+
+#[derive(Deserialize)]
+struct HostPostMessageArgs {
+ rid: i32,
+}
+
+/// Post message to guest worker as host
+pub fn op_host_post_message(
+ _state: &ThreadSafeState,
+ args: Value,
+ data: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
+ let args: HostPostMessageArgs = serde_json::from_value(args)?;
+
+ let rid = args.rid as u32;
+
+ let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice();
+
+ resources::post_message_to_worker(rid, d)
+ .wait()
+ .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;
+
+ Ok(JsonOp::Sync(json!({})))
+}