summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-08-22 22:30:14 -0700
committerGitHub <noreply@github.com>2019-08-22 22:30:14 -0700
commitbc467b265fbe06ace24f5d9536bd8eb36ae4a601 (patch)
tree6c1761c852398a63fe31fc912841b449fe3851c3
parent47c216317f8eb5bf277663a732a79f6b07ba79ef (diff)
introduce JSON serialization for ops (#2799)
Converts env(), exit(), execPath(), utime() and utimeSync() to use JSON instead of flatbuffers.
-rw-r--r--cli/BUILD.gn7
-rw-r--r--cli/main.rs2
-rw-r--r--cli/msg.fbs38
-rw-r--r--cli/ops/dispatch_flatbuffers.rs20
-rw-r--r--cli/ops/dispatch_json.rs113
-rw-r--r--cli/ops/dispatch_minimal.rs11
-rw-r--r--cli/ops/fs.rs37
-rw-r--r--cli/ops/mod.rs36
-rw-r--r--cli/ops/os.rs117
-rw-r--r--cli/ops/utils.rs2
-rw-r--r--js/dispatch.ts15
-rw-r--r--js/dispatch_flatbuffers.ts2
-rw-r--r--js/dispatch_json.ts94
-rw-r--r--js/dispatch_minimal.ts2
-rw-r--r--js/os.ts63
-rw-r--r--js/utime.ts37
16 files changed, 355 insertions, 241 deletions
diff --git a/cli/BUILD.gn b/cli/BUILD.gn
index c7a80907e..08050ae06 100644
--- a/cli/BUILD.gn
+++ b/cli/BUILD.gn
@@ -83,7 +83,9 @@ ts_sources = [
"../js/dir.ts",
"../js/dispatch.ts",
"../js/dispatch_flatbuffers.ts",
+ "../js/dispatch_json.ts",
"../js/dispatch_minimal.ts",
+ "../js/dom_file.ts",
"../js/dom_types.ts",
"../js/dom_util.ts",
"../js/error_stack.ts",
@@ -91,12 +93,11 @@ ts_sources = [
"../js/event.ts",
"../js/event_target.ts",
"../js/fetch.ts",
- "../js/format_error.ts",
- "../js/dom_file.ts",
"../js/file_info.ts",
"../js/files.ts",
"../js/flatbuffers.ts",
"../js/form_data.ts",
+ "../js/format_error.ts",
"../js/get_random_values.ts",
"../js/globals.ts",
"../js/headers.ts",
@@ -134,10 +135,10 @@ ts_sources = [
"../js/url_search_params.ts",
"../js/util.ts",
"../js/utime.ts",
+ "../js/version.ts",
"../js/window.ts",
"../js/workers.ts",
"../js/write_file.ts",
- "../js/version.ts",
"../js/xeval.ts",
"../tsconfig.json",
diff --git a/cli/main.rs b/cli/main.rs
index 3f8f113b2..2e82b8ee8 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -13,6 +13,8 @@ extern crate indexmap;
#[cfg(unix)]
extern crate nix;
extern crate rand;
+extern crate serde;
+extern crate serde_derive;
extern crate url;
mod ansi;
diff --git a/cli/msg.fbs b/cli/msg.fbs
index 8fb92fee9..3a40b80f5 100644
--- a/cli/msg.fbs
+++ b/cli/msg.fbs
@@ -12,9 +12,6 @@ union Any {
Cwd,
CwdRes,
Dial,
- Environ,
- EnvironRes,
- Exit,
Fetch,
FetchSourceFile,
FetchSourceFileRes,
@@ -29,8 +26,6 @@ union Any {
HostGetMessageRes,
HostGetWorkerClosed,
HostPostMessage,
- IsTTY,
- IsTTYRes,
Kill,
Link,
Listen,
@@ -77,9 +72,6 @@ union Any {
Truncate,
HomeDir,
HomeDirRes,
- ExecPath,
- ExecPathRes,
- Utime,
WorkerGetMessage,
WorkerGetMessageRes,
WorkerPostMessage,
@@ -286,21 +278,11 @@ 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;
@@ -469,18 +451,6 @@ table HomeDirRes {
path: string;
}
-table ExecPath {}
-
-table ExecPathRes {
- path: string;
-}
-
-table Utime {
- filename: string;
- atime: uint64;
- mtime: uint64;
-}
-
table Open {
filename: string;
perm: uint;
@@ -600,14 +570,6 @@ table NowRes {
subsec_nanos: uint32;
}
-table IsTTY {}
-
-table IsTTYRes {
- stdin: bool;
- stdout: bool;
- stderr: bool;
-}
-
table Seek {
rid: uint32;
offset: int;
diff --git a/cli/ops/dispatch_flatbuffers.rs b/cli/ops/dispatch_flatbuffers.rs
index 2b2e5050d..b9dd4d9fa 100644
--- a/cli/ops/dispatch_flatbuffers.rs
+++ b/cli/ops/dispatch_flatbuffers.rs
@@ -13,13 +13,11 @@ use super::files::{op_close, op_open, op_read, op_seek, op_write};
use super::fs::{
op_chdir, op_chmod, op_chown, op_copy_file, op_cwd, op_link,
op_make_temp_dir, op_mkdir, op_read_dir, op_read_link, op_remove, op_rename,
- op_stat, op_symlink, op_truncate, op_utime,
+ op_stat, op_symlink, op_truncate,
};
use super::metrics::op_metrics;
use super::net::{op_accept, op_dial, op_listen, op_shutdown};
-use super::os::{
- op_env, op_exec_path, op_exit, op_home_dir, op_is_tty, op_set_env, op_start,
-};
+use super::os::{op_home_dir, op_set_env, op_start};
use super::performance::op_now;
use super::permissions::{op_permissions, op_revoke_permission};
use super::process::{op_kill, op_run, op_run_status};
@@ -65,13 +63,8 @@ pub fn dispatch(
let op_result = op_func(state, &base, zero_copy);
- let state = state.clone();
-
match op_result {
- Ok(Op::Sync(buf)) => {
- state.metrics_op_completed(buf.len());
- Op::Sync(buf)
- }
+ Ok(Op::Sync(buf)) => Op::Sync(buf),
Ok(Op::Async(fut)) => {
let result_fut = Box::new(
fut
@@ -107,7 +100,6 @@ pub fn dispatch(
},
)
};
- state.metrics_op_completed(buf.len());
Ok(buf)
})
.map_err(|err| panic!("unexpected error {:?}", err)),
@@ -129,7 +121,6 @@ pub fn dispatch(
..Default::default()
},
);
- state.metrics_op_completed(response_buf.len());
Op::Sync(response_buf)
}
}
@@ -162,9 +153,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::CreateWorker => Some(op_create_worker),
msg::Any::Cwd => Some(op_cwd),
msg::Any::Dial => Some(op_dial),
- msg::Any::Environ => Some(op_env),
- msg::Any::ExecPath => Some(op_exec_path),
- msg::Any::Exit => Some(op_exit),
msg::Any::Fetch => Some(op_fetch),
msg::Any::FetchSourceFile => Some(op_fetch_source_file),
msg::Any::FormatError => Some(op_format_error),
@@ -174,7 +162,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::HostGetMessage => Some(op_host_get_message),
msg::Any::HostGetWorkerClosed => Some(op_host_get_worker_closed),
msg::Any::HostPostMessage => Some(op_host_post_message),
- msg::Any::IsTTY => Some(op_is_tty),
msg::Any::Kill => Some(op_kill),
msg::Any::Link => Some(op_link),
msg::Any::Listen => Some(op_listen),
@@ -203,7 +190,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::Symlink => Some(op_symlink),
msg::Any::Truncate => Some(op_truncate),
msg::Any::HomeDir => Some(op_home_dir),
- msg::Any::Utime => Some(op_utime),
msg::Any::Write => Some(op_write),
// TODO(ry) split these out so that only the appropriate Workers can access
diff --git a/cli/ops/dispatch_json.rs b/cli/ops/dispatch_json.rs
new file mode 100644
index 000000000..a575aedb3
--- /dev/null
+++ b/cli/ops/dispatch_json.rs
@@ -0,0 +1,113 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use crate::state::ThreadSafeState;
+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,
+ })
+}
+
+pub type Dispatcher = fn(
+ state: &ThreadSafeState,
+ args: Value,
+ zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox>;
+
+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 vec = serde_json::to_vec(&value).unwrap();
+ vec.into_boxed_slice()
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct AsyncArgs {
+ promise_id: Option<u64>,
+}
+
+pub fn dispatch(
+ d: Dispatcher,
+ state: &ThreadSafeState,
+ control: &[u8],
+ zero_copy: Option<PinnedBuf>,
+) -> CoreOp {
+ 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(move |args| d(state, args, zero_copy));
+ 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
index 37ad56813..22d0a92f8 100644
--- a/cli/ops/dispatch_minimal.rs
+++ b/cli/ops/dispatch_minimal.rs
@@ -74,19 +74,15 @@ fn test_parse_min_record() {
pub fn dispatch(
d: Dispatcher,
- state: &ThreadSafeState,
+ _state: &ThreadSafeState,
control: &[u8],
zero_copy: Option<PinnedBuf>,
) -> CoreOp {
let mut record = parse_min_record(control).unwrap();
let is_sync = record.promise_id == 0;
-
// TODO(ry) Currently there aren't any sync minimal ops. This is just a sanity
// check. Remove later.
assert!(!is_sync);
-
- let state = state.clone();
-
let rid = record.arg;
let min_op = d(rid, zero_copy);
@@ -102,10 +98,9 @@ pub fn dispatch(
record.result = -1;
}
}
- let buf: Buf = record.into();
- state.metrics_op_completed(buf.len());
- Ok(buf)
+ Ok(record.into())
}));
+
if is_sync {
Op::Sync(fut.wait().unwrap())
} else {
diff --git a/cli/ops/fs.rs b/cli/ops/fs.rs
index d46ed91e1..f655e4e2d 100644
--- a/cli/ops/fs.rs
+++ b/cli/ops/fs.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use super::dispatch_flatbuffers::serialize_response;
+use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value};
use super::utils::*;
use crate::deno_error::DenoError;
use crate::deno_error::ErrorKind;
@@ -13,7 +14,6 @@ use std::convert::From;
use std::fs;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
-use utime;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
@@ -456,24 +456,27 @@ pub fn op_make_temp_dir(
})
}
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Utime {
+ promise_id: Option<u64>,
+ filename: String,
+ atime: u64,
+ mtime: u64,
+}
+
pub fn op_utime(
state: &ThreadSafeState,
- base: &msg::Base<'_>,
- data: Option<PinnedBuf>,
-) -> CliOpResult {
- assert!(data.is_none());
-
- let inner = base.inner_as_utime().unwrap();
- let filename = String::from(inner.filename().unwrap());
- let atime = inner.atime();
- let mtime = inner.mtime();
-
- state.check_write(&filename)?;
-
- blocking(base.sync(), move || {
- debug!("op_utimes {} {} {}", filename, atime, mtime);
- utime::set_file_times(filename, atime, mtime)?;
- Ok(empty_buf())
+ 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!({}))
})
}
diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs
index 92c0f8e62..240132960 100644
--- a/cli/ops/mod.rs
+++ b/cli/ops/mod.rs
@@ -4,6 +4,7 @@ use deno::*;
mod compiler;
mod dispatch_flatbuffers;
+mod dispatch_json;
mod dispatch_minimal;
mod errors;
mod fetch;
@@ -23,9 +24,16 @@ mod timers;
mod utils;
mod workers;
+// Warning! These values are duplicated in the TypeScript code (js/dispatch.ts),
+// update with care.
pub const OP_FLATBUFFER: OpId = 44;
pub const OP_READ: OpId = 1;
pub const OP_WRITE: OpId = 2;
+pub const OP_EXIT: OpId = 3;
+pub const OP_IS_TTY: OpId = 4;
+pub const OP_ENV: OpId = 5;
+pub const OP_EXEC_PATH: OpId = 6;
+pub const OP_UTIME: OpId = 7;
pub fn dispatch(
state: &ThreadSafeState,
@@ -43,10 +51,36 @@ pub fn dispatch(
OP_WRITE => {
dispatch_minimal::dispatch(io::op_write, state, control, zero_copy)
}
+ OP_EXIT => dispatch_json::dispatch(os::op_exit, state, control, zero_copy),
+ OP_IS_TTY => {
+ dispatch_json::dispatch(os::op_is_tty, state, control, zero_copy)
+ }
+ OP_ENV => dispatch_json::dispatch(os::op_env, state, control, zero_copy),
+ OP_EXEC_PATH => {
+ dispatch_json::dispatch(os::op_exec_path, state, control, zero_copy)
+ }
+ OP_UTIME => {
+ dispatch_json::dispatch(fs::op_utime, state, control, zero_copy)
+ }
OP_FLATBUFFER => dispatch_flatbuffers::dispatch(state, control, zero_copy),
_ => panic!("bad op_id"),
};
state.metrics_op_dispatched(bytes_sent_control, bytes_sent_zero_copy);
- op
+
+ match op {
+ Op::Sync(buf) => {
+ state.metrics_op_completed(buf.len());
+ Op::Sync(buf)
+ }
+ Op::Async(fut) => {
+ use crate::futures::Future;
+ let state = state.clone();
+ let result_fut = Box::new(fut.map(move |buf: Buf| {
+ state.clone().metrics_op_completed(buf.len());
+ buf
+ }));
+ Op::Async(result_fut)
+ }
+ }
}
diff --git a/cli/ops/os.rs b/cli/ops/os.rs
index fbf430d7a..53ef63c60 100644
--- a/cli/ops/os.rs
+++ b/cli/ops/os.rs
@@ -1,16 +1,18 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use super::dispatch_flatbuffers::serialize_response;
+use super::dispatch_json::{Deserialize, JsonOp, Value};
use super::utils::*;
use crate::ansi;
use crate::fs as deno_fs;
use crate::msg;
-use crate::msg_util;
use crate::state::ThreadSafeState;
use crate::version;
use atty;
use deno::*;
use flatbuffers::FlatBufferBuilder;
use log;
+use std::collections::HashMap;
+use std::env;
use url::Url;
pub fn op_start(
@@ -25,7 +27,7 @@ pub fn op_start(
let argv = state.argv.iter().map(String::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_path = env::current_dir().unwrap();
let cwd_off =
builder.create_string(deno_fs::normalize_path(cwd_path.as_ref()).as_ref());
@@ -110,32 +112,16 @@ pub fn op_home_dir(
pub fn op_exec_path(
state: &ThreadSafeState,
- base: &msg::Base<'_>,
- data: Option<PinnedBuf>,
-) -> CliOpResult {
- assert!(data.is_none());
- let cmd_id = base.cmd_id();
-
+ _args: Value,
+ _zero_copy: Option<PinnedBuf>,
+) -> Result<JsonOp, ErrBox> {
state.check_env()?;
-
- let builder = &mut FlatBufferBuilder::new();
- let current_exe = std::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 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().to_str().unwrap().to_owned();
- let path = Some(builder.create_string(&path));
- let inner = msg::ExecPathRes::create(builder, &msg::ExecPathResArgs { path });
-
- ok_buf(serialize_response(
- cmd_id,
- builder,
- msg::BaseArgs {
- inner: Some(inner.as_union_value()),
- inner_type: msg::Any::ExecPathRes,
- ..Default::default()
- },
- ))
+ let path = exe_url.to_file_path().unwrap();
+ Ok(JsonOp::Sync(json!(path)))
}
pub fn op_set_env(
@@ -148,71 +134,42 @@ pub fn op_set_env(
let key = inner.key().unwrap();
let value = inner.value().unwrap();
state.check_env()?;
- std::env::set_var(key, value);
+ env::set_var(key, value);
ok_buf(empty_buf())
}
pub fn op_env(
state: &ThreadSafeState,
- base: &msg::Base<'_>,
- data: Option<PinnedBuf>,
-) -> CliOpResult {
- assert!(data.is_none());
- let cmd_id = base.cmd_id();
-
+ _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)))
+}
- 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) },
- );
- let response_buf = serialize_response(
- cmd_id,
- builder,
- msg::BaseArgs {
- inner: Some(inner.as_union_value()),
- inner_type: msg::Any::EnvironRes,
- ..Default::default()
- },
- );
- ok_buf(response_buf)
+#[derive(Deserialize)]
+struct Exit {
+ code: i32,
}
pub fn op_exit(
- _state: &ThreadSafeState,
- base: &msg::Base<'_>,
- _data: Option<PinnedBuf>,
-) -> CliOpResult {
- let inner = base.inner_as_exit().unwrap();
- std::process::exit(inner.code())
+ _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(
- _state: &ThreadSafeState,
- base: &msg::Base<'_>,
- _data: Option<PinnedBuf>,
-) -> CliOpResult {
- 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_buf(serialize_response(
- base.cmd_id(),
- builder,
- msg::BaseArgs {
- inner: Some(inner.as_union_value()),
- inner_type: msg::Any::IsTTYRes,
- ..Default::default()
- },
- ))
+ _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),
+ })))
}
diff --git a/cli/ops/utils.rs b/cli/ops/utils.rs
index a9b0b442c..95b13b77a 100644
--- a/cli/ops/utils.rs
+++ b/cli/ops/utils.rs
@@ -19,7 +19,7 @@ pub fn empty_buf() -> 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<F>(f: F) -> Poll<Buf, ErrBox>
+pub fn convert_blocking<F>(f: F) -> Poll<Buf, ErrBox>
where
F: FnOnce() -> Result<Buf, ErrBox>,
{
diff --git a/js/dispatch.ts b/js/dispatch.ts
index 423469d38..0c5c59553 100644
--- a/js/dispatch.ts
+++ b/js/dispatch.ts
@@ -1,20 +1,29 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as minimal from "./dispatch_minimal";
import * as flatbuffers from "./dispatch_flatbuffers";
+import * as json from "./dispatch_json";
// These consts are shared with Rust. Update with care.
export const OP_FLATBUFFER = 44;
export const OP_READ = 1;
export const OP_WRITE = 2;
+export const OP_EXIT = 3;
+export const OP_IS_TTY = 4;
+export const OP_ENV = 5;
+export const OP_EXEC_PATH = 6;
+export const OP_UTIME = 7;
-export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
+export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
switch (opId) {
case OP_FLATBUFFER:
- flatbuffers.handleAsyncMsgFromRust(opId, ui8);
+ flatbuffers.asyncMsgFromRust(opId, ui8);
break;
case OP_WRITE:
case OP_READ:
- minimal.handleAsyncMsgFromRust(opId, ui8);
+ minimal.asyncMsgFromRust(opId, ui8);
+ break;
+ case OP_UTIME:
+ json.asyncMsgFromRust(opId, ui8);
break;
default:
throw Error("bad opId");
diff --git a/js/dispatch_flatbuffers.ts b/js/dispatch_flatbuffers.ts
index 87a01037c..0e375dbdf 100644
--- a/js/dispatch_flatbuffers.ts
+++ b/js/dispatch_flatbuffers.ts
@@ -19,7 +19,7 @@ interface FlatbufferRecord {
base: msg.Base;
}
-export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
+export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
let { promiseId, base } = flatbufferRecordFromBuf(ui8);
const promise = promiseTable.get(promiseId);
util.assert(promise != null, `Expecting promise in table. ${promiseId}`);
diff --git a/js/dispatch_json.ts b/js/dispatch_json.ts
new file mode 100644
index 000000000..e8c976164
--- /dev/null
+++ b/js/dispatch_json.ts
@@ -0,0 +1,94 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+// Do not add flatbuffer dependencies to this module.
+// TODO(ry) Currently ErrorKind enum is defined in FlatBuffers. Therefore
+// we must still reference the msg_generated.ts. This should be removed!
+import { ErrorKind } from "gen/cli/msg_generated";
+import * as util from "./util";
+import { TextEncoder, TextDecoder } from "./text_encoding";
+import { core } from "./core";
+import { DenoError } from "./errors";
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type Ok = any;
+
+interface JsonError {
+ kind: ErrorKind;
+ message: string;
+}
+
+interface JsonResponse {
+ ok?: Ok;
+ err?: JsonError;
+ promiseId?: number; // only present in async mesasges.
+}
+
+const promiseTable = new Map<number, util.Resolvable<number>>();
+let _nextPromiseId = 1;
+
+function nextPromiseId(): number {
+ return _nextPromiseId++;
+}
+
+function decode(ui8: Uint8Array): JsonResponse {
+ const s = new TextDecoder().decode(ui8);
+ return JSON.parse(s) as JsonResponse;
+}
+
+function encode(args: object): Uint8Array {
+ const s = JSON.stringify(args);
+ return new TextEncoder().encode(s);
+}
+
+function toDenoError(err: JsonError): DenoError<ErrorKind> {
+ return new DenoError(err.kind, err.message);
+}
+
+export function asyncMsgFromRust(opId: number, res: Uint8Array): void {
+ const { ok, err, promiseId } = decode(res);
+ const promise = promiseTable.get(promiseId!)!;
+ if (!promise) {
+ throw Error(`Async op ${opId} had bad promiseId`);
+ }
+ promiseTable.delete(promiseId!);
+
+ if (err) {
+ promise.reject(toDenoError(err));
+ } else if (ok) {
+ promise.resolve(ok);
+ } else {
+ util.unreachable();
+ }
+}
+
+export function sendSync(
+ opId: number,
+ args: object = {},
+ zeroCopy?: Uint8Array
+): Ok {
+ const argsUi8 = encode(args);
+ const res = core.dispatch(opId, argsUi8, zeroCopy);
+ if (!res) {
+ return;
+ }
+ const { ok, err, promiseId } = decode(res);
+ util.assert(!promiseId);
+ if (err) {
+ throw toDenoError(err);
+ }
+ return ok;
+}
+
+export function sendAsync(
+ opId: number,
+ args: object = {},
+ zeroCopy?: Uint8Array
+): Promise<Ok> {
+ const promiseId = nextPromiseId();
+ args = Object.assign(args, { promiseId });
+ const argsUi8 = encode(args);
+ const promise = util.createResolvable<Ok>();
+ promiseTable.set(promiseId, promise);
+ const r = core.dispatch(opId, argsUi8, zeroCopy);
+ util.assert(!r);
+ return promise;
+}
diff --git a/js/dispatch_minimal.ts b/js/dispatch_minimal.ts
index fc3fc61b9..9a310fd22 100644
--- a/js/dispatch_minimal.ts
+++ b/js/dispatch_minimal.ts
@@ -40,7 +40,7 @@ const scratchBytes = new Uint8Array(
);
util.assert(scratchBytes.byteLength === scratch32.length * 4);
-export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
+export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
const buf32 = new Int32Array(ui8.buffer, ui8.byteOffset, ui8.byteLength / 4);
const record = recordFromBufMinimal(opId, buf32);
const { promiseId, result } = record;
diff --git a/js/os.ts b/js/os.ts
index 59c44145d..f8938ab70 100644
--- a/js/os.ts
+++ b/js/os.ts
@@ -1,7 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { core } from "./core";
-import { handleAsyncMsgFromRust } from "./dispatch";
+import * as dispatch from "./dispatch";
import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers";
+import * as dispatchJson from "./dispatch_json";
import { assert } from "./util";
import * as util from "./util";
import { window } from "./window";
@@ -23,21 +24,12 @@ function setGlobals(pid_: number, noColor_: boolean): void {
* console.log(Deno.isTTY().stdout);
*/
export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } {
- const builder = flatbuffers.createBuilder();
- const inner = msg.IsTTY.createIsTTY(builder);
- const baseRes = sendSync(builder, msg.Any.IsTTY, inner)!;
- assert(msg.Any.IsTTYRes === baseRes.innerType());
- const res = new msg.IsTTYRes();
- assert(baseRes.inner(res) != null);
-
- return { stdin: res.stdin(), stdout: res.stdout(), stderr: res.stderr() };
+ return dispatchJson.sendSync(dispatch.OP_IS_TTY);
}
/** Exit the Deno process with optional exit code. */
-export function exit(exitCode = 0): never {
- const builder = flatbuffers.createBuilder();
- const inner = msg.Exit.createExit(builder, exitCode);
- sendSync(builder, msg.Any.Exit, inner);
+export function exit(code = 0): never {
+ dispatchJson.sendSync(dispatch.OP_EXIT, { code });
return util.unreachable();
}
@@ -49,22 +41,6 @@ function setEnv(key: string, value: string): void {
sendSync(builder, msg.Any.SetEnv, inner);
}
-function createEnv(inner: msg.EnvironRes): { [index: string]: string } {
- const env: { [index: string]: string } = {};
-
- for (let i = 0; i < inner.mapLength(); i++) {
- const item = inner.map(i)!;
- env[item.key()!] = item.value()!;
- }
-
- return new Proxy(env, {
- set(obj, prop: string, value: string): boolean {
- setEnv(prop, value);
- return Reflect.set(obj, prop, value);
- }
- });
-}
-
/** Returns a snapshot of the environment variables at invocation. Mutating a
* property in the object will set that variable in the environment for
* the process. The environment object will only accept `string`s
@@ -77,19 +53,13 @@ function createEnv(inner: msg.EnvironRes): { [index: string]: string } {
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
*/
export function env(): { [index: string]: string } {
- /* Ideally we could write
- const res = sendSync({
- command: msg.Command.ENV,
+ const env = dispatchJson.sendSync(dispatch.OP_ENV);
+ return new Proxy(env, {
+ set(obj, prop: string, value: string): boolean {
+ setEnv(prop, value);
+ return Reflect.set(obj, prop, value);
+ }
});
- */
- const builder = flatbuffers.createBuilder();
- const inner = msg.Environ.createEnviron(builder);
- const baseRes = sendSync(builder, msg.Any.Environ, inner)!;
- assert(msg.Any.EnvironRes === baseRes.innerType());
- const res = new msg.EnvironRes();
- assert(baseRes.inner(res) != null);
- // TypeScript cannot track assertion above, therefore not null assertion
- return createEnv(res);
}
/** Send to the privileged side that we have setup and are ready. */
@@ -111,7 +81,7 @@ export function start(
preserveDenoNamespace = true,
source?: string
): msg.StartRes {
- core.setAsyncHandler(handleAsyncMsgFromRust);
+ core.setAsyncHandler(dispatch.asyncMsgFromRust);
// First we send an empty `Start` message to let the privileged side know we
// are ready. The response should be a `StartRes` message containing the CLI
@@ -163,12 +133,5 @@ export function homeDir(): string {
* Requires the `--allow-env` flag.
*/
export function execPath(): string {
- const builder = flatbuffers.createBuilder();
- const inner = msg.ExecPath.createExecPath(builder);
- const baseRes = sendSync(builder, msg.Any.ExecPath, inner)!;
- assert(msg.Any.ExecPathRes === baseRes.innerType());
- const res = new msg.ExecPathRes();
- assert(baseRes.inner(res) != null);
- const path = res.path()!;
- return path;
+ return dispatchJson.sendSync(dispatch.OP_EXEC_PATH);
}
diff --git a/js/utime.ts b/js/utime.ts
index 89914b4ca..c71710458 100644
--- a/js/utime.ts
+++ b/js/utime.ts
@@ -1,24 +1,9 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
-import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers";
-import * as util from "./util";
+import { sendSync, sendAsync } from "./dispatch_json";
+import { OP_UTIME } from "./dispatch";
-function req(
- filename: string,
- atime: number | Date,
- mtime: number | Date
-): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
- const atimeSec = atime instanceof Date ? Math.floor(+atime / 1000) : atime;
- const mtimeSec = mtime instanceof Date ? Math.floor(+mtime / 1000) : mtime;
-
- const builder = flatbuffers.createBuilder();
- const filename_ = builder.createString(filename);
- const atimeParts = util.splitNumberToParts(atimeSec);
- const atimeMS_ = builder.createLong(atimeParts[0], atimeParts[1]);
- const mtimeParts = util.splitNumberToParts(mtimeSec);
- const mtimeMS_ = builder.createLong(mtimeParts[0], mtimeParts[1]);
-
- const inner = msg.Utime.createUtime(builder, filename_, atimeMS_, mtimeMS_);
- return [builder, msg.Any.Utime, inner];
+function toSecondsFromEpoch(v: number | Date): number {
+ return v instanceof Date ? v.valueOf() / 1000 : v;
}
/** Synchronously changes the access and modification times of a file system
@@ -32,7 +17,12 @@ export function utimeSync(
atime: number | Date,
mtime: number | Date
): void {
- sendSync(...req(filename, atime, mtime));
+ sendSync(OP_UTIME, {
+ filename,
+ // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
+ atime: toSecondsFromEpoch(atime),
+ mtime: toSecondsFromEpoch(mtime)
+ });
}
/** Changes the access and modification times of a file system object
@@ -46,5 +36,10 @@ export async function utime(
atime: number | Date,
mtime: number | Date
): Promise<void> {
- await sendAsync(...req(filename, atime, mtime));
+ await sendAsync(OP_UTIME, {
+ filename,
+ // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
+ atime: toSecondsFromEpoch(atime),
+ mtime: toSecondsFromEpoch(mtime)
+ });
}