From 846d91b7b1ba7988dc2c468f5ad07b85b7e8ea29 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 21 Aug 2019 10:00:05 -0400 Subject: [PATCH 1/7] Add ability to dispatch ops using JSON Converts env(), exit(), execPath(), utime() and utimeSync() to use JSON instead of flatbuffers. --- Cargo.lock | 3 + cli/BUILD.gn | 9 +-- cli/Cargo.toml | 2 +- cli/main.rs | 2 + cli/msg.fbs | 38 ----------- cli/ops/dispatch_flatbuffers.rs | 11 +--- cli/ops/dispatch_json.rs | 113 ++++++++++++++++++++++++++++++++ cli/ops/fs.rs | 37 ++++++----- cli/ops/mod.rs | 19 ++++++ cli/ops/os.rs | 113 ++++++++++---------------------- cli/ops/utils.rs | 2 +- js/dispatch.ts | 13 ++++ js/dispatch_json.ts | 99 ++++++++++++++++++++++++++++ js/os.ts | 63 ++++-------------- js/utime.ts | 35 ++++------ 15 files changed, 341 insertions(+), 218 deletions(-) create mode 100644 cli/ops/dispatch_json.rs create mode 100644 js/dispatch_json.ts diff --git a/Cargo.lock b/Cargo.lock index 964c0ab10f8e29..db4b48154c7be1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,6 +1006,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" diff --git a/cli/BUILD.gn b/cli/BUILD.gn index 3a5912471c83b0..0ca1006b488ba2 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", @@ -111,6 +112,7 @@ ts_sources = [ "../js/mock_builtin.js", "../js/net.ts", "../js/os.ts", + "../js/performance.ts", "../js/permissions.ts", "../js/plugins.d.ts", "../js/process.ts", @@ -132,11 +134,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/performance.ts", - "../js/version.ts", "../js/xeval.ts", "../tsconfig.json", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4a486c3955bf7d..ec12c39ed53178 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -37,7 +37,7 @@ regex = "1.2.0" remove_dir_all = "0.5.2" ring = "~0.14.6" rustyline = "5.0.1" -serde = "1.0.98" +serde = { version = "1.0.98", features = ["derive"] } serde_derive = "1.0.98" serde_json = { version = "1.0.40", features = [ "preserve_order" ] } source-map-mappings = "0.5.0" diff --git a/cli/main.rs b/cli/main.rs index 3f8f113b2298a9..2e82b8ee868fe9 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 8fb92fee9f01c6..3a40b80f570b49 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 2b2e5050dfc013..151cfead809576 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}; @@ -162,9 +160,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option { 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 +169,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option { 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 +197,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option { 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 00000000000000..a575aedb3e1d42 --- /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 + 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, +) -> Result; + +fn serialize_result( + promise_id: Option, + result: Result, +) -> 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, +} + +pub fn dispatch( + d: Dispatcher, + state: &ThreadSafeState, + control: &[u8], + zero_copy: Option, +) -> 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 { + 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) -> Poll +where + F: FnOnce() -> Result, +{ + 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(is_sync: bool, f: F) -> Result +where + F: 'static + Send + FnOnce() -> Result, +{ + 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/fs.rs b/cli/ops/fs.rs index d46ed91e1ce9c1..f655e4e2db486b 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, + filename: String, + atime: u64, + mtime: u64, +} + pub fn op_utime( state: &ThreadSafeState, - base: &msg::Base<'_>, - data: Option, -) -> 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, +) -> Result { + 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 92c0f8e62b8ef1..a8f330a17e0de2 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,6 +51,17 @@ 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"), }; diff --git a/cli/ops/os.rs b/cli/ops/os.rs index fbf430d7adeb39..af635140d67047 100644 --- a/cli/ops/os.rs +++ b/cli/ops/os.rs @@ -1,10 +1,10 @@ // 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; @@ -110,32 +110,16 @@ pub fn op_home_dir( pub fn op_exec_path( state: &ThreadSafeState, - base: &msg::Base<'_>, - data: Option, -) -> CliOpResult { - assert!(data.is_none()); - let cmd_id = base.cmd_id(); - + _args: Value, + _zero_copy: Option, +) -> Result { 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` + // 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( @@ -154,65 +138,40 @@ pub fn op_set_env( pub fn op_env( state: &ThreadSafeState, - base: &msg::Base<'_>, - data: Option, -) -> CliOpResult { - assert!(data.is_none()); - let cmd_id = base.cmd_id(); - + _args: Value, + _zero_copy: Option, +) -> Result { state.check_env()?; + // TODO(ry) can this be done with serde_json::Value::from_iter() ? + let mut vars = std::collections::HashMap::new(); + for (key, value) in std::env::vars() { + vars.insert(key, value); + } + Ok(JsonOp::Sync(json!(vars))) +} - 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, -) -> CliOpResult { - let inner = base.inner_as_exit().unwrap(); - std::process::exit(inner.code()) + _s: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + 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, -) -> 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, +) -> Result { + 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 a9b0b442c5f25e..95b13b77aeea1e 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) -> Poll +pub fn convert_blocking(f: F) -> Poll where F: FnOnce() -> Result, { diff --git a/js/dispatch.ts b/js/dispatch.ts index 423469d3805459..1b476797b2ce4a 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -1,11 +1,17 @@ // 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 { switch (opId) { @@ -16,6 +22,13 @@ export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void { case OP_READ: minimal.handleAsyncMsgFromRust(opId, ui8); break; + case OP_EXIT: + case OP_IS_TTY: + case OP_ENV: + case OP_EXEC_PATH: + case OP_UTIME: + json.handleAsyncMsgFromRust(opId, ui8); + break; default: throw Error("bad opId"); } diff --git a/js/dispatch_json.ts b/js/dispatch_json.ts new file mode 100644 index 00000000000000..a5dbf0c45ee191 --- /dev/null +++ b/js/dispatch_json.ts @@ -0,0 +1,99 @@ +// 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>(); +let _nextPromiseId = 1; + +function nextPromiseId(): number { + return _nextPromiseId++; +} + +export function handleAsyncMsgFromRust(opId: number, resUi8: Uint8Array): void { + const resStr = new TextDecoder().decode(resUi8); + const res = JSON.parse(resStr) as JsonResponse; + const promiseId = res.promiseId!; + const promise = promiseTable.get(promiseId)!; + if (!promise) { + throw Error(`Async op ${opId} had bad promiseId: ${resStr}`); + } + promiseTable.delete(promiseId); + + if (res.err) { + let err = maybeError(res.err); + if (err) { + promise.reject(err); + } else { + promise.resolve(); + } + } else { + promise.resolve(res.ok!); + } +} + +export function sendSync( + opId: number, + args: object = {}, + zeroCopy?: Uint8Array +): Ok { + const argsStr = JSON.stringify(args); + const argsUi8 = new TextEncoder().encode(argsStr); + const resUi8 = core.dispatch(opId, argsUi8, zeroCopy); + if (!resUi8) { + return; + } + const resStr = new TextDecoder().decode(resUi8); + const res = JSON.parse(resStr) as JsonResponse; + util.assert(!res.promiseId); + if (res.err) { + const err = maybeError(res.err); + if (err != null) { + throw err; + } + } + return res.ok; +} + +export function sendAsync( + opId: number, + args: object = {}, + zeroCopy?: Uint8Array +): Promise { + const promiseId = nextPromiseId(); + args = Object.assign(args, { promiseId }); + const argsStr = JSON.stringify(args); + const argsUi8 = new TextEncoder().encode(argsStr); + const promise = util.createResolvable(); + promiseTable.set(promiseId, promise); + const r = core.dispatch(opId, argsUi8, zeroCopy); + util.assert(!r); + return promise; +} + +function maybeError(err: JsonError): null | DenoError { + if (err.kind === ErrorKind.NoError) { + return null; + } else { + return new DenoError(err.kind, err.message); + } +} diff --git a/js/os.ts b/js/os.ts index 59c44145dd2949..740371e3028d2c 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.handleAsyncMsgFromRust); // 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 89914b4cabef35..3215194f91d384 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,11 @@ export function utimeSync( atime: number | Date, mtime: number | Date ): void { - sendSync(...req(filename, atime, mtime)); + sendSync(OP_UTIME, { + filename, + atime: toSecondsFromEpoch(atime), + mtime: toSecondsFromEpoch(mtime) + }); } /** Changes the access and modification times of a file system object @@ -46,5 +35,9 @@ export async function utime( atime: number | Date, mtime: number | Date ): Promise { - await sendAsync(...req(filename, atime, mtime)); + await sendAsync(OP_UTIME, { + filename, + atime: toSecondsFromEpoch(atime), + mtime: toSecondsFromEpoch(mtime) + }); } From d67c33287fdc2c03e9efc25364185c6254792db4 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 21 Aug 2019 23:12:48 -0400 Subject: [PATCH 2/7] fix op dispatch/completed metrics --- cli/ops/dispatch_flatbuffers.rs | 9 +-------- cli/ops/dispatch_minimal.rs | 11 +++-------- cli/ops/mod.rs | 17 ++++++++++++++++- js/dispatch.ts | 4 ---- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cli/ops/dispatch_flatbuffers.rs b/cli/ops/dispatch_flatbuffers.rs index 151cfead809576..b9dd4d9fa2e9d2 100644 --- a/cli/ops/dispatch_flatbuffers.rs +++ b/cli/ops/dispatch_flatbuffers.rs @@ -63,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 @@ -105,7 +100,6 @@ pub fn dispatch( }, ) }; - state.metrics_op_completed(buf.len()); Ok(buf) }) .map_err(|err| panic!("unexpected error {:?}", err)), @@ -127,7 +121,6 @@ pub fn dispatch( ..Default::default() }, ); - state.metrics_op_completed(response_buf.len()); Op::Sync(response_buf) } } diff --git a/cli/ops/dispatch_minimal.rs b/cli/ops/dispatch_minimal.rs index 37ad568132cea2..22d0a92f87a7b8 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, ) -> 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/mod.rs b/cli/ops/mod.rs index a8f330a17e0de2..24013296076e64 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -67,5 +67,20 @@ pub fn dispatch( }; 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/js/dispatch.ts b/js/dispatch.ts index 1b476797b2ce4a..3496f979a90d28 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -22,10 +22,6 @@ export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void { case OP_READ: minimal.handleAsyncMsgFromRust(opId, ui8); break; - case OP_EXIT: - case OP_IS_TTY: - case OP_ENV: - case OP_EXEC_PATH: case OP_UTIME: json.handleAsyncMsgFromRust(opId, ui8); break; From aa4e39609a4aa695a45e539d1225fc1e8c95bc97 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Aug 2019 01:23:18 -0400 Subject: [PATCH 3/7] cleanup --- js/dispatch_json.ts | 71 +++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/js/dispatch_json.ts b/js/dispatch_json.ts index a5dbf0c45ee191..ae548c63abb2e2 100644 --- a/js/dispatch_json.ts +++ b/js/dispatch_json.ts @@ -29,25 +29,34 @@ function nextPromiseId(): number { return _nextPromiseId++; } -export function handleAsyncMsgFromRust(opId: number, resUi8: Uint8Array): void { - const resStr = new TextDecoder().decode(resUi8); - const res = JSON.parse(resStr) as JsonResponse; - const promiseId = res.promiseId!; - const promise = promiseTable.get(promiseId)!; +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 { + return new DenoError(err.kind, err.message); +} + +export function handleAsyncMsgFromRust(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: ${resStr}`); + throw Error(`Async op ${opId} had bad promiseId`); } - promiseTable.delete(promiseId); + promiseTable.delete(promiseId!); - if (res.err) { - let err = maybeError(res.err); - if (err) { - promise.reject(err); - } else { - promise.resolve(); - } + if (err) { + promise.reject(toDenoError(err)); + } else if (ok) { + promise.resolve(ok); } else { - promise.resolve(res.ok!); + util.unreachable(); } } @@ -56,22 +65,17 @@ export function sendSync( args: object = {}, zeroCopy?: Uint8Array ): Ok { - const argsStr = JSON.stringify(args); - const argsUi8 = new TextEncoder().encode(argsStr); - const resUi8 = core.dispatch(opId, argsUi8, zeroCopy); - if (!resUi8) { + const argsUi8 = encode(args); + const res = core.dispatch(opId, argsUi8, zeroCopy); + if (!res) { return; } - const resStr = new TextDecoder().decode(resUi8); - const res = JSON.parse(resStr) as JsonResponse; - util.assert(!res.promiseId); - if (res.err) { - const err = maybeError(res.err); - if (err != null) { - throw err; - } + const { ok, err, promiseId } = decode(res); + util.assert(!promiseId); + if (err) { + throw toDenoError(err); } - return res.ok; + return ok; } export function sendAsync( @@ -81,19 +85,10 @@ export function sendAsync( ): Promise { const promiseId = nextPromiseId(); args = Object.assign(args, { promiseId }); - const argsStr = JSON.stringify(args); - const argsUi8 = new TextEncoder().encode(argsStr); + const argsUi8 = encode(args); const promise = util.createResolvable(); promiseTable.set(promiseId, promise); const r = core.dispatch(opId, argsUi8, zeroCopy); util.assert(!r); return promise; } - -function maybeError(err: JsonError): null | DenoError { - if (err.kind === ErrorKind.NoError) { - return null; - } else { - return new DenoError(err.kind, err.message); - } -} From 6f158fe8481313d5934cb442396572850360c8d9 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Aug 2019 01:24:32 -0400 Subject: [PATCH 4/7] cleanup --- js/dispatch.ts | 8 ++++---- js/dispatch_flatbuffers.ts | 2 +- js/dispatch_json.ts | 2 +- js/dispatch_minimal.ts | 2 +- js/os.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/js/dispatch.ts b/js/dispatch.ts index 3496f979a90d28..0c5c59553fb4d5 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -13,17 +13,17 @@ 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.handleAsyncMsgFromRust(opId, ui8); + json.asyncMsgFromRust(opId, ui8); break; default: throw Error("bad opId"); diff --git a/js/dispatch_flatbuffers.ts b/js/dispatch_flatbuffers.ts index 87a01037cb720f..0e375dbdf548ad 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 index ae548c63abb2e2..e8c97616454ce4 100644 --- a/js/dispatch_json.ts +++ b/js/dispatch_json.ts @@ -43,7 +43,7 @@ function toDenoError(err: JsonError): DenoError { return new DenoError(err.kind, err.message); } -export function handleAsyncMsgFromRust(opId: number, res: Uint8Array): void { +export function asyncMsgFromRust(opId: number, res: Uint8Array): void { const { ok, err, promiseId } = decode(res); const promise = promiseTable.get(promiseId!)!; if (!promise) { diff --git a/js/dispatch_minimal.ts b/js/dispatch_minimal.ts index fc3fc61b9da97e..9a310fd22014d3 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 740371e3028d2c..f8938ab70350ad 100644 --- a/js/os.ts +++ b/js/os.ts @@ -81,7 +81,7 @@ export function start( preserveDenoNamespace = true, source?: string ): msg.StartRes { - core.setAsyncHandler(dispatch.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 From 0947ece4d625076ca3a73387f7ea939498c53623 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Aug 2019 12:22:16 -0400 Subject: [PATCH 5/7] cleanup --- cli/ops/os.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cli/ops/os.rs b/cli/ops/os.rs index af635140d67047..53ef63c6071685 100644 --- a/cli/ops/os.rs +++ b/cli/ops/os.rs @@ -11,6 +11,8 @@ 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::>(); 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()); @@ -114,7 +116,7 @@ pub fn op_exec_path( _zero_copy: Option, ) -> Result { state.check_env()?; - let current_exe = std::env::current_exe().unwrap(); + 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(); @@ -132,7 +134,7 @@ 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()) } @@ -142,12 +144,8 @@ pub fn op_env( _zero_copy: Option, ) -> Result { state.check_env()?; - // TODO(ry) can this be done with serde_json::Value::from_iter() ? - let mut vars = std::collections::HashMap::new(); - for (key, value) in std::env::vars() { - vars.insert(key, value); - } - Ok(JsonOp::Sync(json!(vars))) + let v = env::vars().collect::>(); + Ok(JsonOp::Sync(json!(v))) } #[derive(Deserialize)] From dabf778a16b738b3b1e3ccad8b05b877b5a0b51f Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Thu, 22 Aug 2019 22:40:44 -0400 Subject: [PATCH 6/7] add todo --- js/utime.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/utime.ts b/js/utime.ts index 3215194f91d384..c71710458bcf0b 100644 --- a/js/utime.ts +++ b/js/utime.ts @@ -19,6 +19,7 @@ export function utimeSync( ): void { sendSync(OP_UTIME, { filename, + // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple atime: toSecondsFromEpoch(atime), mtime: toSecondsFromEpoch(mtime) }); @@ -37,6 +38,7 @@ export async function utime( ): Promise { await sendAsync(OP_UTIME, { filename, + // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple atime: toSecondsFromEpoch(atime), mtime: toSecondsFromEpoch(mtime) }); From f292ae9fd6c696250d1a0e023682ec24ad83a399 Mon Sep 17 00:00:00 2001 From: romainprignon Date: Thu, 22 Aug 2019 17:15:07 +0200 Subject: [PATCH 7/7] feat: port metrics ops to json --- cli/msg.fbs | 12 ----------- cli/msg.rs | 14 ------------- cli/ops/dispatch_flatbuffers.rs | 2 -- cli/ops/metrics.rs | 36 +++++++++++---------------------- cli/ops/mod.rs | 4 ++++ js/dispatch.ts | 2 ++ js/metrics.ts | 27 +++---------------------- 7 files changed, 21 insertions(+), 76 deletions(-) diff --git a/cli/msg.fbs b/cli/msg.fbs index 3a40b80f570b49..ca96a765fc50c7 100644 --- a/cli/msg.fbs +++ b/cli/msg.fbs @@ -32,8 +32,6 @@ union Any { ListenRes, MakeTempDir, MakeTempDirRes, - Metrics, - MetricsRes, Mkdir, NewConn, Now, @@ -518,16 +516,6 @@ table NewConn { local_addr: string; } -table Metrics {} - -table MetricsRes { - ops_dispatched: uint64; - ops_completed: uint64; - bytes_sent_control: uint64; - bytes_sent_data: uint64; - bytes_received: uint64; -} - enum ProcessStdio: byte { Inherit, Piped, Null } table Run { diff --git a/cli/msg.rs b/cli/msg.rs index 51726b5726c029..db4c771f890b5d 100644 --- a/cli/msg.rs +++ b/cli/msg.rs @@ -1,22 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. #![allow(dead_code)] #![cfg_attr(feature = "cargo-clippy", allow(clippy::all, clippy::pedantic))] -use crate::state; use flatbuffers; -use std::sync::atomic::Ordering; // GN_OUT_DIR is set either by build.rs (for the Cargo build), or by // build_extra/rust/run.py (for the GN+Ninja build). include!(concat!(env!("GN_OUT_DIR"), "/gen/cli/msg_generated.rs")); - -impl<'a> From<&'a state::Metrics> for MetricsResArgs { - fn from(m: &'a state::Metrics) -> Self { - MetricsResArgs { - ops_dispatched: m.ops_dispatched.load(Ordering::SeqCst) as u64, - ops_completed: m.ops_completed.load(Ordering::SeqCst) as u64, - bytes_sent_control: m.bytes_sent_control.load(Ordering::SeqCst) as u64, - bytes_sent_data: m.bytes_sent_data.load(Ordering::SeqCst) as u64, - bytes_received: m.bytes_received.load(Ordering::SeqCst) as u64, - } - } -} diff --git a/cli/ops/dispatch_flatbuffers.rs b/cli/ops/dispatch_flatbuffers.rs index b9dd4d9fa2e9d2..6ae1c39b11e3be 100644 --- a/cli/ops/dispatch_flatbuffers.rs +++ b/cli/ops/dispatch_flatbuffers.rs @@ -15,7 +15,6 @@ use super::fs::{ op_make_temp_dir, op_mkdir, op_read_dir, op_read_link, op_remove, op_rename, 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_home_dir, op_set_env, op_start}; use super::performance::op_now; @@ -166,7 +165,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option { msg::Any::Link => Some(op_link), msg::Any::Listen => Some(op_listen), msg::Any::MakeTempDir => Some(op_make_temp_dir), - msg::Any::Metrics => Some(op_metrics), msg::Any::Mkdir => Some(op_mkdir), msg::Any::Now => Some(op_now), msg::Any::Open => Some(op_open), diff --git a/cli/ops/metrics.rs b/cli/ops/metrics.rs index 76f36c39048be0..6e0bde3d66f16a 100644 --- a/cli/ops/metrics.rs +++ b/cli/ops/metrics.rs @@ -1,31 +1,19 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_flatbuffers::serialize_response; -use super::utils::*; -use crate::msg; +use super::dispatch_json::{JsonOp, Value}; use crate::state::ThreadSafeState; use deno::*; -use flatbuffers::FlatBufferBuilder; +use std::sync::atomic::Ordering; pub fn op_metrics( state: &ThreadSafeState, - base: &msg::Base<'_>, - data: Option, -) -> CliOpResult { - assert!(data.is_none()); - let cmd_id = base.cmd_id(); - - let builder = &mut FlatBufferBuilder::new(); - let inner = msg::MetricsRes::create( - builder, - &msg::MetricsResArgs::from(&state.metrics), - ); - ok_buf(serialize_response( - cmd_id, - builder, - msg::BaseArgs { - inner: Some(inner.as_union_value()), - inner_type: msg::Any::MetricsRes, - ..Default::default() - }, - )) + _args: Value, + _zero_copy: Option, +) -> Result { + Ok(JsonOp::Sync(json!({ + "opsDispatched": state.metrics.ops_dispatched.load(Ordering::SeqCst) as u64, + "opsCompleted": state.metrics.ops_completed.load(Ordering::SeqCst) as u64, + "bytesSentControl": state.metrics.bytes_sent_control.load(Ordering::SeqCst) as u64, + "bytesSentData": state.metrics.bytes_sent_data.load(Ordering::SeqCst) as u64, + "bytesReceived": state.metrics.bytes_received.load(Ordering::SeqCst) as u64, + }))) } diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 24013296076e64..a8f54bce30973d 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -34,6 +34,7 @@ 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 const OP_METRICS: OpId = 8; pub fn dispatch( state: &ThreadSafeState, @@ -62,6 +63,9 @@ pub fn dispatch( OP_UTIME => { dispatch_json::dispatch(fs::op_utime, state, control, zero_copy) } + OP_METRICS => { + dispatch_json::dispatch(metrics::op_metrics, state, control, zero_copy) + } OP_FLATBUFFER => dispatch_flatbuffers::dispatch(state, control, zero_copy), _ => panic!("bad op_id"), }; diff --git a/js/dispatch.ts b/js/dispatch.ts index 0c5c59553fb4d5..1f670724d85d9f 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -12,6 +12,7 @@ export const OP_IS_TTY = 4; export const OP_ENV = 5; export const OP_EXEC_PATH = 6; export const OP_UTIME = 7; +export const OP_METRICS = 8; export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { switch (opId) { @@ -23,6 +24,7 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { minimal.asyncMsgFromRust(opId, ui8); break; case OP_UTIME: + case OP_METRICS: json.asyncMsgFromRust(opId, ui8); break; default: diff --git a/js/metrics.ts b/js/metrics.ts index e93e9528c7e856..3256d6811bf600 100644 --- a/js/metrics.ts +++ b/js/metrics.ts @@ -1,6 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { assert } from "./util"; -import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import * as dispatch from "./dispatch"; +import * as dispatchJson from "./dispatch_json"; export interface Metrics { opsDispatched: number; @@ -10,27 +10,6 @@ export interface Metrics { bytesReceived: number; } -function req(): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { - const builder = flatbuffers.createBuilder(); - const inner = msg.Metrics.createMetrics(builder); - return [builder, msg.Any.Metrics, inner]; -} - -function res(baseRes: null | msg.Base): Metrics { - assert(baseRes !== null); - assert(msg.Any.MetricsRes === baseRes!.innerType()); - const res = new msg.MetricsRes(); - assert(baseRes!.inner(res) !== null); - - return { - opsDispatched: res.opsDispatched().toFloat64(), - opsCompleted: res.opsCompleted().toFloat64(), - bytesSentControl: res.bytesSentControl().toFloat64(), - bytesSentData: res.bytesSentData().toFloat64(), - bytesReceived: res.bytesReceived().toFloat64() - }; -} - /** Receive metrics from the privileged side of Deno. * * > console.table(Deno.metrics()) @@ -45,5 +24,5 @@ function res(baseRes: null | msg.Base): Metrics { * └──────────────────┴────────┘ */ export function metrics(): Metrics { - return res(sendSync(...req())); + return dispatchJson.sendSync(dispatch.OP_METRICS); }