mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
introduce JSON serialization for ops (#2799)
Converts env(), exit(), execPath(), utime() and utimeSync() to use JSON instead of flatbuffers.
This commit is contained in:
parent
47c216317f
commit
bc467b265f
16 changed files with 355 additions and 241 deletions
|
@ -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",
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
38
cli/msg.fbs
38
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;
|
||||
|
|
|
@ -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
|
||||
|
|
113
cli/ops/dispatch_json.rs
Normal file
113
cli/ops/dispatch_json.rs
Normal file
|
@ -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(),
|
||||
))))
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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!({}))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
117
cli/ops/os.rs
117
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),
|
||||
})))
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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}`);
|
||||
|
|
94
js/dispatch_json.ts
Normal file
94
js/dispatch_json.ts
Normal file
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
63
js/os.ts
63
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);
|
||||
}
|
||||
|
|
37
js/utime.ts
37
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)
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue