From 2235dd795d3cc6c24ff1bdd1bbdcd110b4b0bdfc Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sat, 24 Aug 2019 13:20:48 -0700 Subject: [PATCH] Revert json ops (#2814) * Revert "port more ops to JSON (#2809)" This reverts commit 137f33733d365026903d40e7cde6e34ac6c36dcf. * Revert "port ops to JSON: compiler, errors, fetch, files (#2804)" This reverts commit 79f82cf10ed1dbf91346994250d7311a4d74377a. * Revert "Port rest of os ops to JSON (#2802)" This reverts commit 5b2baa5c990fbeae747e952c5dcd7a5369e950b1. --- cli/deno_error.rs | 17 - cli/main.rs | 1 + cli/msg.fbs | 333 ++++++++++++++++++ cli/msg.rs | 14 + cli/msg_util.rs | 124 +++++++ cli/ops/compiler.rs | 90 +++-- cli/ops/dispatch_flatbuffers.rs | 56 ++- cli/ops/errors.rs | 96 +++-- cli/ops/fetch.rs | 87 ++--- cli/ops/files.rs | 98 +++--- cli/ops/metrics.rs | 36 +- cli/ops/mod.rs | 180 ---------- cli/ops/net.rs | 167 +++++---- cli/ops/os.rs | 128 +++++-- cli/ops/performance.rs | 34 +- cli/ops/permissions.rs | 62 ++-- cli/ops/process.rs | 175 +++++---- cli/ops/random.rs | 17 +- cli/ops/repl.rs | 86 +++-- cli/ops/resources.rs | 50 ++- cli/ops/timers.rs | 59 +++- cli/ops/workers.rs | 217 +++++++----- js/compiler.ts | 56 ++- js/dispatch.ts | 52 +-- js/error_stack.ts | 51 ++- js/fetch.ts | 94 +++-- js/files.ts | 52 ++- js/format_error.ts | 18 +- js/get_random_values.ts | 18 +- js/main.ts | 24 +- js/metrics.ts | 27 +- js/net.ts | 46 ++- js/os.ts | 63 ++-- js/performance.ts | 18 +- js/permissions.ts | 42 ++- js/process.ts | 132 +++---- js/repl.ts | 30 +- js/resources.ts | 20 +- js/timers.ts | 14 +- js/workers.ts | 85 ++++- tests/error_004_missing_module.ts.out | 8 +- tests/error_005_missing_dynamic_import.ts.out | 8 +- tests/error_006_import_ext_failure.ts.out | 8 +- tests/error_011_bad_module_specifier.ts.out | 11 +- ...or_012_bad_dynamic_import_specifier.ts.out | 9 +- 45 files changed, 1968 insertions(+), 1045 deletions(-) create mode 100644 cli/msg_util.rs diff --git a/cli/deno_error.rs b/cli/deno_error.rs index 3b7dbcde80..e024a396c6 100644 --- a/cli/deno_error.rs +++ b/cli/deno_error.rs @@ -205,18 +205,6 @@ impl GetErrorKind for ReadlineError { } } -impl GetErrorKind for serde_json::error::Error { - fn kind(&self) -> ErrorKind { - use serde_json::error::*; - match self.classify() { - Category::Io => ErrorKind::InvalidInput, - Category::Syntax => ErrorKind::InvalidInput, - Category::Data => ErrorKind::InvalidData, - Category::Eof => ErrorKind::UnexpectedEof, - } - } -} - #[cfg(unix)] mod unix { use super::{ErrorKind, GetErrorKind}; @@ -263,11 +251,6 @@ impl GetErrorKind for dyn AnyError { .or_else(|| self.downcast_ref::().map(Get::kind)) .or_else(|| self.downcast_ref::().map(Get::kind)) .or_else(|| self.downcast_ref::().map(Get::kind)) - .or_else(|| { - self - .downcast_ref::() - .map(Get::kind) - }) .or_else(|| unix_error_kind(self)) .unwrap_or_else(|| { panic!("Can't get ErrorKind for {:?}", self); diff --git a/cli/main.rs b/cli/main.rs index a601e68af3..2e82b8ee86 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -32,6 +32,7 @@ mod http_body; mod http_util; mod import_map; pub mod msg; +pub mod msg_util; pub mod ops; pub mod permissions; mod progress; diff --git a/cli/msg.fbs b/cli/msg.fbs index a7359c5272..3a40b80f57 100644 --- a/cli/msg.fbs +++ b/cli/msg.fbs @@ -1,14 +1,48 @@ union Any { + Accept, + ApplySourceMap, + Cache, Chdir, Chmod, Chown, + Close, CopyFile, + CreateWorker, + CreateWorkerRes, Cwd, CwdRes, + Dial, + Fetch, + FetchSourceFile, + FetchSourceFileRes, + FetchRes, + FormatError, + FormatErrorRes, + GetRandomValues, + GlobalTimer, + GlobalTimerRes, + GlobalTimerStop, + HostGetMessage, + HostGetMessageRes, + HostGetWorkerClosed, + HostPostMessage, + Kill, Link, + Listen, + ListenRes, MakeTempDir, MakeTempDirRes, + Metrics, + MetricsRes, Mkdir, + NewConn, + Now, + NowRes, + Open, + OpenRes, + PermissionRevoke, + Permissions, + PermissionsRes, Read, ReadDir, ReadDirRes, @@ -17,11 +51,30 @@ union Any { ReadlinkRes, Remove, Rename, + ReplReadline, + ReplReadlineRes, + ReplStart, + ReplStartRes, + Resources, + ResourcesRes, + Run, + RunRes, + RunStatus, + RunStatusRes, Seek, + SetEnv, + Shutdown, + Start, + StartRes, Stat, StatRes, Symlink, Truncate, + HomeDir, + HomeDirRes, + WorkerGetMessage, + WorkerGetMessageRes, + WorkerPostMessage, Write, WriteRes, } @@ -114,6 +167,25 @@ table Base { inner: Any; } +table Start { + unused: int8; +} + +table StartRes { + cwd: string; + pid: uint32; + argv: [string]; + main_module: string; // Absolute URL. + debug_flag: bool; + deps_flag: bool; + types_flag: bool; + version_flag: bool; + deno_version: string; + v8_version: string; + no_color: bool; + xeval_delim: string; +} + table FormatError { error: string; } @@ -122,15 +194,138 @@ table FormatErrorRes { error: string; } +// Create worker as host +table CreateWorker { + specifier: string; + include_deno_namespace: bool; + has_source_code: bool; + source_code: string; +} + +table CreateWorkerRes { + rid: uint32; +} + +table HostGetWorkerClosed { + rid: uint32; +} + +// Get message from guest worker as host +table HostGetMessage { + rid: uint32; +} + +table HostGetMessageRes { + data: [ubyte]; +} + +// Post message to guest worker as host +table HostPostMessage { + rid: uint32; + // data passed thru the zero-copy data parameter. +} + +// Get message from host as guest worker +table WorkerGetMessage { + unused: int8; +} + +table WorkerGetMessageRes { + data: [ubyte]; +} + +// Post message to host as guest worker +table WorkerPostMessage { + // data passed thru the zero-copy data parameter. +} + +table FetchSourceFile { + specifier: string; + referrer: string; +} + +table FetchSourceFileRes { + // If it's a non-http module, moduleName and filename will be the same. + // For http modules, module_name is its resolved http URL, and filename + // is the location of the locally downloaded source code. + module_name: string; + filename: string; + media_type: MediaType; + data: [ubyte]; +} + +table ApplySourceMap { + filename: string; + line: int; + column: int; +} + +table Cache { + extension: string; + module_id: string; + contents: string; +} + table Chdir { directory: string; } +table GlobalTimer { + timeout: int; +} + +table GlobalTimerRes { } + +table GlobalTimerStop { } + +table SetEnv { + key: string; + value: string; +} + table KeyValue { key: string; value: string; } +table Permissions {} + +table PermissionRevoke { + permission: string; +} + +table PermissionsRes { + run: bool; + read: bool; + write: bool; + net: bool; + env: bool; + hrtime: bool; +} + +// Note this represents The WHOLE header of an http message, not just the key +// value pairs. That means it includes method and url for Requests and status +// for responses. This is why it is singular "Header" instead of "Headers". +table HttpHeader { + is_request: bool; + // Request only: + method: string; + url: string; + // Response only: + status: uint16; + // Both: + fields: [KeyValue]; +} + +table Fetch { + header: HttpHeader; +} + +table FetchRes { + header: HttpHeader; + body_rid: uint32; +} + table MakeTempDir { dir: string; prefix: string; @@ -189,6 +384,35 @@ table ReadlinkRes { path: string; } +table ReplStart { + history_file: string; + // TODO add config +} + +table ReplStartRes { + rid: uint32; +} + +table ReplReadline { + rid: uint32; + prompt: string; +} + +table ReplReadlineRes { + line: string; +} + +table Resources {} + +table Resource { + rid: uint32; + repr: string; +} + +table ResourcesRes { + resources: [Resource]; +} + table Symlink { oldname: string; newname: string; @@ -221,6 +445,22 @@ table Truncate { len: uint; } +table HomeDir {} + +table HomeDirRes { + path: string; +} + +table Open { + filename: string; + perm: uint; + mode: string; +} + +table OpenRes { + rid: uint32; +} + table Read { rid: uint32; // (ptr, len) is passed as second parameter to Deno.core.send(). @@ -239,10 +479,103 @@ table WriteRes { nbyte: uint; } +table Close { + rid: uint32; +} + +table Kill { + pid: int32; + signo: int32; +} + +table Shutdown { + rid: uint32; + how: uint; +} + +table Listen { + network: string; + address: string; +} + +table ListenRes { + rid: uint32; +} + +table Accept { + rid: uint32; +} + +table Dial { + network: string; + address: string; +} + +// Response to Accept and Dial. +table NewConn { + rid: uint32; + remote_addr: string; + local_addr: string; +} + +table Metrics {} + +table MetricsRes { + ops_dispatched: uint64; + ops_completed: uint64; + bytes_sent_control: uint64; + bytes_sent_data: uint64; + bytes_received: uint64; +} + +enum ProcessStdio: byte { Inherit, Piped, Null } + +table Run { + args: [string]; + cwd: string; + env: [KeyValue]; + stdin: ProcessStdio; + stdout: ProcessStdio; + stderr: ProcessStdio; + stdin_rid: uint32; + stdout_rid: uint32; + stderr_rid: uint32; +} + +table RunRes { + rid: uint32; + pid: uint32; + // The following stdio rids are only valid if "Piped" was specified for the + // corresponding stdio stream. The caller MUST issue a close op for all valid + // stdio streams. + stdin_rid: uint32; + stdout_rid: uint32; + stderr_rid: uint32; +} + +table RunStatus { + rid: uint32; +} + +table RunStatusRes { + got_signal: bool; + exit_code: int; + exit_signal: int; +} + +table Now {} + +table NowRes { + seconds: uint64; + subsec_nanos: uint32; +} + table Seek { rid: uint32; offset: int; whence: uint; } +table GetRandomValues {} + root_type Base; diff --git a/cli/msg.rs b/cli/msg.rs index db4c771f89..51726b5726 100644 --- a/cli/msg.rs +++ b/cli/msg.rs @@ -1,8 +1,22 @@ // 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/msg_util.rs b/cli/msg_util.rs new file mode 100644 index 0000000000..e37a91f3a6 --- /dev/null +++ b/cli/msg_util.rs @@ -0,0 +1,124 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +// Helpers for serialization. +use crate::msg; +use deno::ErrBox; +use flatbuffers; +use http::header::HeaderName; +use http::uri::Uri; +use http::Method; +use hyper::header::HeaderMap; +use hyper::header::HeaderValue; +use hyper::Body; +use hyper::Request; +use hyper::Response; +use std::str::FromStr; + +type Headers = HeaderMap; + +pub fn serialize_key_value<'bldr>( + builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, + key: &str, + value: &str, +) -> flatbuffers::WIPOffset> { + let key = builder.create_string(&key); + let value = builder.create_string(&value); + msg::KeyValue::create( + builder, + &msg::KeyValueArgs { + key: Some(key), + value: Some(value), + }, + ) +} + +pub fn serialize_request_header<'bldr>( + builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, + r: &Request, +) -> flatbuffers::WIPOffset> { + let method = builder.create_string(r.method().as_str()); + let url = builder.create_string(r.uri().to_string().as_ref()); + + let mut fields = Vec::new(); + for (key, val) in r.headers().iter() { + let kv = serialize_key_value(builder, key.as_ref(), val.to_str().unwrap()); + fields.push(kv); + } + let fields = builder.create_vector(fields.as_ref()); + + msg::HttpHeader::create( + builder, + &msg::HttpHeaderArgs { + is_request: true, + method: Some(method), + url: Some(url), + fields: Some(fields), + ..Default::default() + }, + ) +} + +pub fn serialize_fields<'bldr>( + builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, + headers: &Headers, +) -> flatbuffers::WIPOffset< + flatbuffers::Vector< + 'bldr, + flatbuffers::ForwardsUOffset>, + >, +> { + let mut fields = Vec::new(); + for (key, val) in headers.iter() { + let kv = serialize_key_value(builder, key.as_ref(), val.to_str().unwrap()); + fields.push(kv); + } + builder.create_vector(fields.as_ref()) +} + +// Not to be confused with serialize_response which has nothing to do with HTTP. +pub fn serialize_http_response<'bldr>( + builder: &mut flatbuffers::FlatBufferBuilder<'bldr>, + r: &Response, +) -> flatbuffers::WIPOffset> { + let status = r.status().as_u16(); + let fields = serialize_fields(builder, r.headers()); + msg::HttpHeader::create( + builder, + &msg::HttpHeaderArgs { + is_request: false, + status, + fields: Some(fields), + ..Default::default() + }, + ) +} + +pub fn deserialize_request( + header_msg: msg::HttpHeader<'_>, + body: Body, +) -> Result, ErrBox> { + let mut r = Request::new(body); + + assert!(header_msg.is_request()); + + let u = header_msg.url().unwrap(); + let u = Uri::from_str(u).map_err(ErrBox::from)?; + *r.uri_mut() = u; + + if let Some(method) = header_msg.method() { + let method = Method::from_str(method).unwrap(); + *r.method_mut() = method; + } + + if let Some(fields) = header_msg.fields() { + let headers = r.headers_mut(); + for i in 0..fields.len() { + let kv = fields.get(i); + let key = kv.key().unwrap(); + let name = HeaderName::from_bytes(key.as_bytes()).unwrap(); + let value = kv.value().unwrap(); + let v = HeaderValue::from_str(value).unwrap(); + headers.insert(name, v); + } + } + Ok(r) +} diff --git a/cli/ops/compiler.rs b/cli/ops/compiler.rs index 39d3a6d7f4..efdcd2c9b1 100644 --- a/cli/ops/compiler.rs +++ b/cli/ops/compiler.rs @@ -1,68 +1,86 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::deno_error; +use crate::msg; use crate::state::ThreadSafeState; use crate::tokio_util; use deno::*; - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct CacheArgs { - module_id: String, - contents: String, - extension: String, -} +use flatbuffers::FlatBufferBuilder; +use futures::Future; pub fn op_cache( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: CacheArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_cache().unwrap(); + let extension = inner.extension().unwrap(); + // TODO: rename to something with 'url' + let module_id = inner.module_id().unwrap(); + let contents = inner.contents().unwrap(); - let module_specifier = ModuleSpecifier::resolve_url(&args.module_id) + let module_specifier = ModuleSpecifier::resolve_url(module_id) .expect("Should be valid module specifier"); state.ts_compiler.cache_compiler_output( &module_specifier, - &args.extension, - &args.contents, + extension, + contents, )?; - Ok(JsonOp::Sync(json!({}))) -} - -#[derive(Deserialize)] -struct FetchSourceFileArgs { - specifier: String, - referrer: String, + ok_buf(empty_buf()) } pub fn op_fetch_source_file( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: FetchSourceFileArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if !base.sync() { + return Err(deno_error::no_async_support()); + } + assert!(data.is_none()); + let inner = base.inner_as_fetch_source_file().unwrap(); + let cmd_id = base.cmd_id(); + let specifier = inner.specifier().unwrap(); + let referrer = inner.referrer().unwrap(); // TODO(ry) Maybe a security hole. Only the compiler worker should have access // to this. Need a test to demonstrate the hole. let is_dyn_import = false; let resolved_specifier = - state.resolve(&args.specifier, &args.referrer, false, is_dyn_import)?; + state.resolve(specifier, referrer, false, is_dyn_import)?; let fut = state .file_fetcher - .fetch_source_file_async(&resolved_specifier); + .fetch_source_file_async(&resolved_specifier) + .and_then(move |out| { + let builder = &mut FlatBufferBuilder::new(); + let data_off = builder.create_vector(out.source_code.as_slice()); + let msg_args = msg::FetchSourceFileResArgs { + module_name: Some(builder.create_string(&out.url.to_string())), + filename: Some(builder.create_string(&out.filename.to_str().unwrap())), + media_type: out.media_type, + data: Some(data_off), + }; + let inner = msg::FetchSourceFileRes::create(builder, &msg_args); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::FetchSourceFileRes, + ..Default::default() + }, + )) + }); // WARNING: Here we use tokio_util::block_on() which starts a new Tokio // runtime for executing the future. This is so we don't inadvernently run // out of threads in the main runtime. - let out = tokio_util::block_on(fut)?; - Ok(JsonOp::Sync(json!({ - "moduleName": out.url.to_string(), - "filename": out.filename.to_str().unwrap(), - "mediaType": out.media_type as i32, - "sourceCode": String::from_utf8(out.source_code).unwrap(), - }))) + let result_buf = tokio_util::block_on(fut)?; + Ok(Op::Sync(result_buf)) } diff --git a/cli/ops/dispatch_flatbuffers.rs b/cli/ops/dispatch_flatbuffers.rs index bd01587510..b9dd4d9fa2 100644 --- a/cli/ops/dispatch_flatbuffers.rs +++ b/cli/ops/dispatch_flatbuffers.rs @@ -6,12 +6,29 @@ use deno::*; use flatbuffers::FlatBufferBuilder; use hyper::rt::Future; -use super::files::{op_read, op_write}; +use super::compiler::{op_cache, op_fetch_source_file}; +use super::errors::{op_apply_source_map, op_format_error}; +use super::fetch::op_fetch; +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, }; +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; +use super::permissions::{op_permissions, op_revoke_permission}; +use super::process::{op_kill, op_run, op_run_status}; +use super::random::op_get_random_values; +use super::repl::{op_repl_readline, op_repl_start}; +use super::resources::op_resources; +use super::timers::{op_global_timer, op_global_timer_stop}; +use super::workers::{ + op_create_worker, op_host_get_message, op_host_get_worker_closed, + op_host_post_message, op_worker_get_message, op_worker_post_message, +}; type CliDispatchFn = fn( state: &ThreadSafeState, @@ -125,24 +142,61 @@ pub fn serialize_response( /// Standard ops set for most isolates pub fn op_selector_std(inner_type: msg::Any) -> Option { match inner_type { + msg::Any::Accept => Some(op_accept), + msg::Any::ApplySourceMap => Some(op_apply_source_map), + msg::Any::Cache => Some(op_cache), msg::Any::Chdir => Some(op_chdir), msg::Any::Chmod => Some(op_chmod), msg::Any::Chown => Some(op_chown), + msg::Any::Close => Some(op_close), msg::Any::CopyFile => Some(op_copy_file), + msg::Any::CreateWorker => Some(op_create_worker), msg::Any::Cwd => Some(op_cwd), + msg::Any::Dial => Some(op_dial), + msg::Any::Fetch => Some(op_fetch), + msg::Any::FetchSourceFile => Some(op_fetch_source_file), + msg::Any::FormatError => Some(op_format_error), + msg::Any::GetRandomValues => Some(op_get_random_values), + msg::Any::GlobalTimer => Some(op_global_timer), + msg::Any::GlobalTimerStop => Some(op_global_timer_stop), + 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::Kill => Some(op_kill), 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), + msg::Any::PermissionRevoke => Some(op_revoke_permission), + msg::Any::Permissions => Some(op_permissions), msg::Any::Read => Some(op_read), msg::Any::ReadDir => Some(op_read_dir), msg::Any::Readlink => Some(op_read_link), msg::Any::Remove => Some(op_remove), msg::Any::Rename => Some(op_rename), + msg::Any::ReplReadline => Some(op_repl_readline), + msg::Any::ReplStart => Some(op_repl_start), + msg::Any::Resources => Some(op_resources), + msg::Any::Run => Some(op_run), + msg::Any::RunStatus => Some(op_run_status), + msg::Any::Seek => Some(op_seek), + msg::Any::SetEnv => Some(op_set_env), + msg::Any::Shutdown => Some(op_shutdown), + msg::Any::Start => Some(op_start), msg::Any::Stat => Some(op_stat), msg::Any::Symlink => Some(op_symlink), msg::Any::Truncate => Some(op_truncate), + msg::Any::HomeDir => Some(op_home_dir), msg::Any::Write => Some(op_write), + // TODO(ry) split these out so that only the appropriate Workers can access + // them. + msg::Any::WorkerGetMessage => Some(op_worker_get_message), + msg::Any::WorkerPostMessage => Some(op_worker_post_message), + _ => None, } } diff --git a/cli/ops/errors.rs b/cli/ops/errors.rs index cd21a38807..a27f3656ee 100644 --- a/cli/ops/errors.rs +++ b/cli/ops/errors.rs @@ -1,56 +1,88 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::deno_error; use crate::fmt_errors::JSError; +use crate::msg; use crate::source_maps::get_orig_position; use crate::source_maps::CachedMaps; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; use std::collections::HashMap; -#[derive(Deserialize)] -struct FormatErrorArgs { - error: String, -} - pub fn op_format_error( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: FormatErrorArgs = serde_json::from_value(args)?; - let error = JSError::from_json(&args.error, &state.ts_compiler); + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_format_error().unwrap(); + let json_str = inner.error().unwrap(); + let error = JSError::from_json(json_str, &state.ts_compiler); + let error_string = error.to_string(); - Ok(JsonOp::Sync(json!({ - "error": error.to_string(), - }))) -} + let mut builder = FlatBufferBuilder::new(); + let new_error = builder.create_string(&error_string); -#[derive(Deserialize)] -struct ApplySourceMap { - filename: String, - line: i32, - column: i32, + let inner = msg::FormatErrorRes::create( + &mut builder, + &msg::FormatErrorResArgs { + error: Some(new_error), + }, + ); + + let response_buf = serialize_response( + base.cmd_id(), + &mut builder, + msg::BaseArgs { + inner_type: msg::Any::FormatErrorRes, + inner: Some(inner.as_union_value()), + ..Default::default() + }, + ); + + ok_buf(response_buf) } pub fn op_apply_source_map( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: ApplySourceMap = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if !base.sync() { + return Err(deno_error::no_async_support()); + } + assert!(data.is_none()); + let inner = base.inner_as_apply_source_map().unwrap(); + let cmd_id = base.cmd_id(); + let filename = inner.filename().unwrap(); + let line = inner.line(); + let column = inner.column(); let mut mappings_map: CachedMaps = HashMap::new(); let (orig_filename, orig_line, orig_column) = get_orig_position( - args.filename, - args.line.into(), - args.column.into(), + filename.to_owned(), + line.into(), + column.into(), &mut mappings_map, &state.ts_compiler, ); - Ok(JsonOp::Sync(json!({ - "filename": orig_filename.to_string(), - "line": orig_line as u32, - "column": orig_column as u32, - }))) + let builder = &mut FlatBufferBuilder::new(); + let msg_args = msg::ApplySourceMapArgs { + filename: Some(builder.create_string(&orig_filename)), + line: orig_line as i32, + column: orig_column as i32, + }; + let res_inner = msg::ApplySourceMap::create(builder, &msg_args); + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(res_inner.as_union_value()), + inner_type: msg::Any::ApplySourceMap, + ..Default::default() + }, + )) } diff --git a/cli/ops/fetch.rs b/cli/ops/fetch.rs index e2ab81a81a..7661eb6e9d 100644 --- a/cli/ops/fetch.rs +++ b/cli/ops/fetch.rs @@ -1,57 +1,38 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::CliOpResult; use crate::http_util; +use crate::msg; +use crate::msg_util; use crate::resources; use crate::state::ThreadSafeState; use deno::*; -use http::header::HeaderName; -use http::uri::Uri; -use http::Method; +use flatbuffers::FlatBufferBuilder; use hyper; -use hyper::header::HeaderValue; use hyper::rt::Future; -use hyper::Request; use std; use std::convert::From; -use std::str::FromStr; - -#[derive(Deserialize)] -struct FetchArgs { - method: Option, - url: String, - headers: Vec<(String, String)>, -} pub fn op_fetch( state: &ThreadSafeState, - args: Value, + base: &msg::Base<'_>, data: Option, -) -> Result { - let args: FetchArgs = serde_json::from_value(args)?; - let url = args.url; +) -> CliOpResult { + let inner = base.inner_as_fetch().unwrap(); + let cmd_id = base.cmd_id(); + + let header = inner.header().unwrap(); + assert!(header.is_request()); + let url = header.url().unwrap(); let body = match data { None => hyper::Body::empty(), Some(buf) => hyper::Body::from(Vec::from(&*buf)), }; - let mut req = Request::new(body); - let uri = Uri::from_str(&url).map_err(ErrBox::from)?; - *req.uri_mut() = uri; + let req = msg_util::deserialize_request(header, body)?; - if let Some(method) = args.method { - let method = Method::from_str(&method).unwrap(); - *req.method_mut() = method; - } - - let headers = req.headers_mut(); - for header_pair in args.headers { - let name = HeaderName::from_bytes(header_pair.0.as_bytes()).unwrap(); - let v = HeaderValue::from_str(&header_pair.1).unwrap(); - headers.insert(name, v); - } - - let url_ = url::Url::parse(&url).map_err(ErrBox::from)?; + let url_ = url::Url::parse(url).map_err(ErrBox::from)?; state.check_net_url(&url_)?; let client = http_util::get_client(); @@ -61,22 +42,32 @@ pub fn op_fetch( .request(req) .map_err(ErrBox::from) .and_then(move |res| { - let status = res.status().as_u16(); - let mut res_headers = Vec::new(); - for (key, val) in res.headers().iter() { - res_headers.push((key.to_string(), val.to_str().unwrap().to_owned())); - } + let builder = &mut FlatBufferBuilder::new(); + let header_off = msg_util::serialize_http_response(builder, &res); let body = res.into_body(); let body_resource = resources::add_hyper_body(body); + let inner = msg::FetchRes::create( + builder, + &msg::FetchResArgs { + header: Some(header_off), + body_rid: body_resource.rid, + }, + ); - let json_res = json!({ - "bodyRid": body_resource.rid, - "status": status, - "headers": res_headers - }); - - futures::future::ok(json_res) + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::FetchRes, + ..Default::default() + }, + )) }); - - Ok(JsonOp::Async(Box::new(future))) + if base.sync() { + let result_buf = future.wait()?; + Ok(Op::Sync(result_buf)) + } else { + Ok(Op::Async(Box::new(future))) + } } diff --git a/cli/ops/files.rs b/cli/ops/files.rs index c02a69b9c6..023bd65f92 100644 --- a/cli/ops/files.rs +++ b/cli/ops/files.rs @@ -1,6 +1,5 @@ // 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::deno_error; use crate::fs as deno_fs; @@ -15,22 +14,17 @@ use std; use std::convert::From; use tokio; -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct OpenArgs { - promise_id: Option, - filename: String, - mode: String, -} - pub fn op_open( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: OpenArgs = serde_json::from_value(args)?; - let (filename, filename_) = deno_fs::resolve_from_cwd(&args.filename)?; - let mode = args.mode.as_ref(); + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_open().unwrap(); + let (filename, filename_) = + deno_fs::resolve_from_cwd(inner.filename().unwrap())?; + let mode = inner.mode().unwrap(); let mut open_options = tokio::fs::OpenOptions::new(); @@ -81,39 +75,44 @@ pub fn op_open( } } - let is_sync = args.promise_id.is_none(); let op = open_options.open(filename).map_err(ErrBox::from).and_then( move |fs_file| { let resource = resources::add_fs_file(fs_file); - futures::future::ok(json!(resource.rid)) + let builder = &mut FlatBufferBuilder::new(); + let inner = + msg::OpenRes::create(builder, &msg::OpenResArgs { rid: resource.rid }); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::OpenRes, + ..Default::default() + }, + )) }, ); - - if is_sync { + if base.sync() { let buf = op.wait()?; - Ok(JsonOp::Sync(buf)) + Ok(Op::Sync(buf)) } else { - Ok(JsonOp::Async(Box::new(op))) + Ok(Op::Async(Box::new(op))) } } -#[derive(Deserialize)] -struct CloseArgs { - rid: i32, -} - pub fn op_close( _state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: CloseArgs = serde_json::from_value(args)?; - - match resources::lookup(args.rid as u32) { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_close().unwrap(); + let rid = inner.rid(); + match resources::lookup(rid) { None => Err(deno_error::bad_resource()), Some(resource) => { resource.close(); - Ok(JsonOp::Sync(json!({}))) + ok_buf(empty_buf()) } } } @@ -203,32 +202,27 @@ pub fn op_write( } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct SeekArgs { - promise_id: Option, - rid: i32, - offset: i32, - whence: i32, -} - pub fn op_seek( _state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: SeekArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_seek().unwrap(); + let rid = inner.rid(); + let offset = inner.offset(); + let whence = inner.whence(); - match resources::lookup(args.rid as u32) { + match resources::lookup(rid) { None => Err(deno_error::bad_resource()), Some(resource) => { - let op = resources::seek(resource, args.offset, args.whence as u32) - .and_then(move |_| futures::future::ok(json!({}))); - if args.promise_id.is_none() { + let op = resources::seek(resource, offset, whence) + .and_then(move |_| Ok(empty_buf())); + if base.sync() { let buf = op.wait()?; - Ok(JsonOp::Sync(buf)) + Ok(Op::Sync(buf)) } else { - Ok(JsonOp::Async(Box::new(op))) + Ok(Op::Async(Box::new(op))) } } } diff --git a/cli/ops/metrics.rs b/cli/ops/metrics.rs index e1a23f6c81..76f36c3904 100644 --- a/cli/ops/metrics.rs +++ b/cli/ops/metrics.rs @@ -1,21 +1,31 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::msg; use crate::state::ThreadSafeState; use deno::*; -use std::sync::atomic::Ordering; +use flatbuffers::FlatBufferBuilder; pub fn op_metrics( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { - let m = &state.metrics; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); - Ok(JsonOp::Sync(json!({ - "opsDispatched": m.ops_dispatched.load(Ordering::SeqCst) as u64, - "opsCompleted": m.ops_completed.load(Ordering::SeqCst) as u64, - "bytesSentControl": m.bytes_sent_control.load(Ordering::SeqCst) as u64, - "bytesSentData": m.bytes_sent_data.load(Ordering::SeqCst) as u64, - "bytesReceived": m.bytes_received.load(Ordering::SeqCst) as u64 - }))) + 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() + }, + )) } diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 4636754c9b..2401329607 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -34,40 +34,6 @@ 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_SET_ENV: OpId = 8; -pub const OP_HOME_DIR: OpId = 9; -pub const OP_START: OpId = 10; -pub const OP_APPLY_SOURCE_MAP: OpId = 11; -pub const OP_FORMAT_ERROR: OpId = 12; -pub const OP_CACHE: OpId = 13; -pub const OP_FETCH_SOURCE_FILE: OpId = 14; -pub const OP_OPEN: OpId = 15; -pub const OP_CLOSE: OpId = 16; -pub const OP_SEEK: OpId = 17; -pub const OP_FETCH: OpId = 18; -pub const OP_METRICS: OpId = 19; -pub const OP_REPL_START: OpId = 20; -pub const OP_REPL_READLINE: OpId = 21; -pub const OP_ACCEPT: OpId = 22; -pub const OP_DIAL: OpId = 23; -pub const OP_SHUTDOWN: OpId = 24; -pub const OP_LISTEN: OpId = 25; -pub const OP_RESOURCES: OpId = 26; -pub const OP_GET_RANDOM_VALUES: OpId = 27; -pub const OP_GLOBAL_TIMER_STOP: OpId = 28; -pub const OP_GLOBAL_TIMER: OpId = 29; -pub const OP_NOW: OpId = 30; -pub const OP_PERMISSIONS: OpId = 31; -pub const OP_REVOKE_PERMISSION: OpId = 32; -pub const OP_CREATE_WORKER: OpId = 33; -pub const OP_HOST_GET_WORKER_CLOSED: OpId = 34; -pub const OP_HOST_POST_MESSAGE: OpId = 35; -pub const OP_HOST_GET_MESSAGE: OpId = 36; -pub const OP_WORKER_POST_MESSAGE: OpId = 37; -pub const OP_WORKER_GET_MESSAGE: OpId = 38; -pub const OP_RUN: OpId = 39; -pub const OP_RUN_STATUS: OpId = 40; -pub const OP_KILL: OpId = 41; pub fn dispatch( state: &ThreadSafeState, @@ -93,155 +59,9 @@ pub fn dispatch( OP_EXEC_PATH => { dispatch_json::dispatch(os::op_exec_path, state, control, zero_copy) } - OP_HOME_DIR => { - dispatch_json::dispatch(os::op_home_dir, state, control, zero_copy) - } OP_UTIME => { dispatch_json::dispatch(fs::op_utime, state, control, zero_copy) } - OP_SET_ENV => { - dispatch_json::dispatch(os::op_set_env, state, control, zero_copy) - } - OP_START => { - dispatch_json::dispatch(os::op_start, state, control, zero_copy) - } - OP_APPLY_SOURCE_MAP => dispatch_json::dispatch( - errors::op_apply_source_map, - state, - control, - zero_copy, - ), - OP_FORMAT_ERROR => dispatch_json::dispatch( - errors::op_format_error, - state, - control, - zero_copy, - ), - OP_CACHE => { - dispatch_json::dispatch(compiler::op_cache, state, control, zero_copy) - } - OP_FETCH_SOURCE_FILE => dispatch_json::dispatch( - compiler::op_fetch_source_file, - state, - control, - zero_copy, - ), - OP_OPEN => { - dispatch_json::dispatch(files::op_open, state, control, zero_copy) - } - OP_CLOSE => { - dispatch_json::dispatch(files::op_close, state, control, zero_copy) - } - OP_SEEK => { - dispatch_json::dispatch(files::op_seek, state, control, zero_copy) - } - OP_METRICS => { - dispatch_json::dispatch(metrics::op_metrics, state, control, zero_copy) - } - OP_FETCH => { - dispatch_json::dispatch(fetch::op_fetch, state, control, zero_copy) - } - OP_REPL_START => { - dispatch_json::dispatch(repl::op_repl_start, state, control, zero_copy) - } - OP_REPL_READLINE => { - dispatch_json::dispatch(repl::op_repl_readline, state, control, zero_copy) - } - OP_ACCEPT => { - dispatch_json::dispatch(net::op_accept, state, control, zero_copy) - } - OP_DIAL => dispatch_json::dispatch(net::op_dial, state, control, zero_copy), - OP_SHUTDOWN => { - dispatch_json::dispatch(net::op_shutdown, state, control, zero_copy) - } - OP_LISTEN => { - dispatch_json::dispatch(net::op_listen, state, control, zero_copy) - } - OP_RESOURCES => dispatch_json::dispatch( - resources::op_resources, - state, - control, - zero_copy, - ), - OP_GET_RANDOM_VALUES => dispatch_json::dispatch( - random::op_get_random_values, - state, - control, - zero_copy, - ), - OP_GLOBAL_TIMER_STOP => dispatch_json::dispatch( - timers::op_global_timer_stop, - state, - control, - zero_copy, - ), - OP_GLOBAL_TIMER => dispatch_json::dispatch( - timers::op_global_timer, - state, - control, - zero_copy, - ), - OP_NOW => { - dispatch_json::dispatch(performance::op_now, state, control, zero_copy) - } - OP_PERMISSIONS => dispatch_json::dispatch( - permissions::op_permissions, - state, - control, - zero_copy, - ), - OP_REVOKE_PERMISSION => dispatch_json::dispatch( - permissions::op_revoke_permission, - state, - control, - zero_copy, - ), - OP_CREATE_WORKER => dispatch_json::dispatch( - workers::op_create_worker, - state, - control, - zero_copy, - ), - OP_HOST_GET_WORKER_CLOSED => dispatch_json::dispatch( - workers::op_host_get_worker_closed, - state, - control, - zero_copy, - ), - OP_HOST_POST_MESSAGE => dispatch_json::dispatch( - workers::op_host_post_message, - state, - control, - zero_copy, - ), - OP_HOST_GET_MESSAGE => dispatch_json::dispatch( - workers::op_host_get_message, - state, - control, - zero_copy, - ), - // TODO: make sure these two ops are only accessible to appropriate Workers - OP_WORKER_POST_MESSAGE => dispatch_json::dispatch( - workers::op_worker_post_message, - state, - control, - zero_copy, - ), - OP_WORKER_GET_MESSAGE => dispatch_json::dispatch( - workers::op_worker_get_message, - state, - control, - zero_copy, - ), - OP_RUN => { - dispatch_json::dispatch(process::op_run, state, control, zero_copy) - } - OP_RUN_STATUS => { - dispatch_json::dispatch(process::op_run_status, state, control, zero_copy) - } - OP_KILL => { - dispatch_json::dispatch(process::op_kill, state, control, zero_copy) - } OP_FLATBUFFER => dispatch_flatbuffers::dispatch(state, control, zero_copy), _ => panic!("bad op_id"), }; diff --git a/cli/ops/net.rs b/cli/ops/net.rs index 650127fadc..5ce5624922 100644 --- a/cli/ops/net.rs +++ b/cli/ops/net.rs @@ -1,12 +1,15 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; use crate::deno_error; +use crate::msg; use crate::resolve_addr::resolve_addr; use crate::resources; use crate::resources::Resource; use crate::state::ThreadSafeState; use crate::tokio_util; use deno::*; +use flatbuffers::FlatBufferBuilder; use futures::Future; use std; use std::convert::From; @@ -15,18 +18,15 @@ use tokio; use tokio::net::TcpListener; use tokio::net::TcpStream; -#[derive(Deserialize)] -struct AcceptArgs { - rid: i32, -} - pub fn op_accept( _state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: AcceptArgs = serde_json::from_value(args)?; - let server_rid = args.rid as u32; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_accept().unwrap(); + let server_rid = inner.rid(); match resources::lookup(server_rid) { None => Err(deno_error::bad_resource()), @@ -34,65 +34,55 @@ pub fn op_accept( let op = tokio_util::accept(server_resource) .map_err(ErrBox::from) .and_then(move |(tcp_stream, _socket_addr)| { - let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); - futures::future::ok(json!({ - "rid": tcp_stream_resource.rid - })) + new_conn(cmd_id, tcp_stream) }); - - Ok(JsonOp::Async(Box::new(op))) + if base.sync() { + let buf = op.wait()?; + Ok(Op::Sync(buf)) + } else { + Ok(Op::Async(Box::new(op))) + } } } } -#[derive(Deserialize)] -struct DialArgs { - network: String, - address: String, -} - pub fn op_dial( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: DialArgs = serde_json::from_value(args)?; - let network = args.network; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_dial().unwrap(); + let network = inner.network().unwrap(); assert_eq!(network, "tcp"); // TODO Support others. - let address = args.address; + let address = inner.address().unwrap(); state.check_net(&address)?; - let op = resolve_addr(&address).and_then(move |addr| { - TcpStream::connect(&addr).map_err(ErrBox::from).and_then( - move |tcp_stream| { - let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); - futures::future::ok(json!({ - "rid": tcp_stream_resource.rid - })) - }, - ) + let op = resolve_addr(address).and_then(move |addr| { + TcpStream::connect(&addr) + .map_err(ErrBox::from) + .and_then(move |tcp_stream| new_conn(cmd_id, tcp_stream)) }); - - Ok(JsonOp::Async(Box::new(op))) -} - -#[derive(Deserialize)] -struct ShutdownArgs { - rid: i32, - how: i32, + if base.sync() { + let buf = op.wait()?; + Ok(Op::Sync(buf)) + } else { + Ok(Op::Async(Box::new(op))) + } } pub fn op_shutdown( _state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: ShutdownArgs = serde_json::from_value(args)?; - - let rid = args.rid; - let how = args.how; - match resources::lookup(rid as u32) { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_shutdown().unwrap(); + let rid = inner.rid(); + let how = inner.how(); + match resources::lookup(rid) { None => Err(deno_error::bad_resource()), Some(mut resource) => { let shutdown_mode = match how { @@ -100,36 +90,67 @@ pub fn op_shutdown( 1 => Shutdown::Write, _ => unimplemented!(), }; - - // Use UFCS for disambiguation - Resource::shutdown(&mut resource, shutdown_mode)?; - Ok(JsonOp::Sync(json!({}))) + blocking(base.sync(), move || { + // Use UFCS for disambiguation + Resource::shutdown(&mut resource, shutdown_mode)?; + Ok(empty_buf()) + }) } } } -#[derive(Deserialize)] -struct ListenArgs { - network: String, - address: String, -} - pub fn op_listen( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: ListenArgs = serde_json::from_value(args)?; - - let network = args.network; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_listen().unwrap(); + let network = inner.network().unwrap(); assert_eq!(network, "tcp"); - let address = args.address; + let address = inner.address().unwrap(); state.check_net(&address)?; - let addr = resolve_addr(&address).wait()?; + let addr = resolve_addr(address).wait()?; let listener = TcpListener::bind(&addr)?; let resource = resources::add_tcp_listener(listener); - Ok(JsonOp::Sync(json!(resource.rid))) + let builder = &mut FlatBufferBuilder::new(); + let inner = + msg::ListenRes::create(builder, &msg::ListenResArgs { rid: resource.rid }); + let response_buf = serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ListenRes, + ..Default::default() + }, + ); + ok_buf(response_buf) +} + +fn new_conn(cmd_id: u32, tcp_stream: TcpStream) -> Result { + let tcp_stream_resource = resources::add_tcp_stream(tcp_stream); + // TODO forward socket_addr to client. + + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::NewConn::create( + builder, + &msg::NewConnArgs { + rid: tcp_stream_resource.rid, + ..Default::default() + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::NewConn, + ..Default::default() + }, + )) } diff --git a/cli/ops/os.rs b/cli/ops/os.rs index afb87539f4..53ef63c607 100644 --- a/cli/ops/os.rs +++ b/cli/ops/os.rs @@ -1,11 +1,15 @@ // 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::state::ThreadSafeState; use crate::version; use atty; use deno::*; +use flatbuffers::FlatBufferBuilder; use log; use std::collections::HashMap; use std::env; @@ -13,38 +17,97 @@ use url::Url; pub fn op_start( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { - Ok(JsonOp::Sync(json!({ - "cwd": deno_fs::normalize_path(&env::current_dir().unwrap()), - "pid": std::process::id(), - "argv": state.argv, - "mainModule": state.main_module().map(|x| x.as_str().to_string()), - "debugFlag": state - .flags - .log_level - .map_or(false, |l| l == log::Level::Debug), - "versionFlag": state.flags.version, - "v8Version": version::v8(), - "denoVersion": version::DENO, - "noColor": !ansi::use_color(), - "xevalDelim": state.flags.xeval_delim.clone(), - }))) + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let mut builder = FlatBufferBuilder::new(); + + let state = state; + let argv = state.argv.iter().map(String::as_str).collect::>(); + let argv_off = builder.create_vector_of_strings(argv.as_slice()); + + let cwd_path = env::current_dir().unwrap(); + let cwd_off = + builder.create_string(deno_fs::normalize_path(cwd_path.as_ref()).as_ref()); + + let v8_version = version::v8(); + let v8_version_off = builder.create_string(v8_version); + + let deno_version = version::DENO; + let deno_version_off = builder.create_string(deno_version); + + let main_module = state + .main_module() + .map(|m| builder.create_string(&m.to_string())); + + let xeval_delim = state + .flags + .xeval_delim + .clone() + .map(|m| builder.create_string(&m)); + + let debug_flag = state + .flags + .log_level + .map_or(false, |l| l == log::Level::Debug); + + let inner = msg::StartRes::create( + &mut builder, + &msg::StartResArgs { + cwd: Some(cwd_off), + pid: std::process::id(), + argv: Some(argv_off), + main_module, + debug_flag, + version_flag: state.flags.version, + v8_version: Some(v8_version_off), + deno_version: Some(deno_version_off), + no_color: !ansi::use_color(), + xeval_delim, + ..Default::default() + }, + ); + + ok_buf(serialize_response( + base.cmd_id(), + &mut builder, + msg::BaseArgs { + inner_type: msg::Any::StartRes, + inner: Some(inner.as_union_value()), + ..Default::default() + }, + )) } pub fn op_home_dir( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + state.check_env()?; + + let builder = &mut FlatBufferBuilder::new(); let path = dirs::home_dir() .unwrap_or_default() .into_os_string() .into_string() .unwrap_or_default(); - Ok(JsonOp::Sync(json!(path))) + let path = Some(builder.create_string(&path)); + let inner = msg::HomeDirRes::create(builder, &msg::HomeDirResArgs { path }); + + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::HomeDirRes, + ..Default::default() + }, + )) } pub fn op_exec_path( @@ -61,21 +124,18 @@ pub fn op_exec_path( Ok(JsonOp::Sync(json!(path))) } -#[derive(Deserialize)] -struct SetEnv { - key: String, - value: String, -} - pub fn op_set_env( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: SetEnv = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_set_env().unwrap(); + let key = inner.key().unwrap(); + let value = inner.value().unwrap(); state.check_env()?; - env::set_var(args.key, args.value); - Ok(JsonOp::Sync(json!({}))) + env::set_var(key, value); + ok_buf(empty_buf()) } pub fn op_env( diff --git a/cli/ops/performance.rs b/cli/ops/performance.rs index 090fc33232..94f6dbc387 100644 --- a/cli/ops/performance.rs +++ b/cli/ops/performance.rs @@ -1,7 +1,10 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::msg; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; // Returns a milliseconds and nanoseconds subsec // since the start time of the deno runtime. @@ -9,9 +12,10 @@ use deno::*; // nanoseconds are rounded on 2ms. pub fn op_now( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); let seconds = state.start_time.elapsed().as_secs(); let mut subsec_nanos = state.start_time.elapsed().subsec_nanos(); let reduced_time_precision = 2_000_000; // 2ms in nanoseconds @@ -23,8 +27,22 @@ pub fn op_now( subsec_nanos -= subsec_nanos % reduced_time_precision } - Ok(JsonOp::Sync(json!({ - "seconds": seconds, - "subsecNanos": subsec_nanos, - }))) + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::NowRes::create( + builder, + &msg::NowResArgs { + seconds, + subsec_nanos, + }, + ); + + ok_buf(serialize_response( + base.cmd_id(), + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::NowRes, + ..Default::default() + }, + )) } diff --git a/cli/ops/permissions.rs b/cli/ops/permissions.rs index 5d14f39be5..6249581fb0 100644 --- a/cli/ops/permissions.rs +++ b/cli/ops/permissions.rs @@ -1,35 +1,50 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::msg; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; pub fn op_permissions( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { - Ok(JsonOp::Sync(json!({ - "run": state.permissions.allows_run(), - "read": state.permissions.allows_read(), - "write": state.permissions.allows_write(), - "net": state.permissions.allows_net(), - "env": state.permissions.allows_env(), - "hrtime": state.permissions.allows_hrtime(), - }))) -} - -#[derive(Deserialize)] -struct RevokePermissionArgs { - permission: String, + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::PermissionsRes::create( + builder, + &msg::PermissionsResArgs { + run: state.permissions.allows_run(), + read: state.permissions.allows_read(), + write: state.permissions.allows_write(), + net: state.permissions.allows_net(), + env: state.permissions.allows_env(), + hrtime: state.permissions.allows_hrtime(), + }, + ); + let response_buf = serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::PermissionsRes, + ..Default::default() + }, + ); + ok_buf(response_buf) } pub fn op_revoke_permission( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: RevokePermissionArgs = serde_json::from_value(args)?; - let permission = args.permission.as_ref(); + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_permission_revoke().unwrap(); + let permission = inner.permission().unwrap(); match permission { "run" => state.permissions.revoke_run(), "read" => state.permissions.revoke_read(), @@ -39,6 +54,5 @@ pub fn op_revoke_permission( "hrtime" => state.permissions.revoke_hrtime(), _ => Ok(()), }?; - - Ok(JsonOp::Sync(json!({}))) + ok_buf(empty_buf()) } diff --git a/cli/ops/process.rs b/cli/ops/process.rs index 8dff53c6e9..d7b326d143 100644 --- a/cli/ops/process.rs +++ b/cli/ops/process.rs @@ -1,9 +1,13 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::*; +use crate::deno_error; +use crate::msg; use crate::resources; use crate::signal::kill; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; use futures; use futures::Future; use std; @@ -14,72 +18,63 @@ use tokio_process::CommandExt; #[cfg(unix)] use std::os::unix::process::ExitStatusExt; -fn subprocess_stdio_map(s: &str) -> std::process::Stdio { - match s { - "inherit" => std::process::Stdio::inherit(), - "piped" => std::process::Stdio::piped(), - "null" => std::process::Stdio::null(), - _ => unreachable!(), +fn subprocess_stdio_map(v: msg::ProcessStdio) -> std::process::Stdio { + match v { + msg::ProcessStdio::Inherit => std::process::Stdio::inherit(), + msg::ProcessStdio::Piped => std::process::Stdio::piped(), + msg::ProcessStdio::Null => std::process::Stdio::null(), } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct RunArgs { - args: Vec, - cwd: Option, - env: Vec<(String, String)>, - stdin: String, - stdout: String, - stderr: String, - stdin_rid: u32, - stdout_rid: u32, - stderr_rid: u32, -} - pub fn op_run( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let run_args: RunArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if !base.sync() { + return Err(deno_error::no_async_support()); + } + let cmd_id = base.cmd_id(); state.check_run()?; - let args = run_args.args; - let env = run_args.env; - let cwd = run_args.cwd; + assert!(data.is_none()); + let inner = base.inner_as_run().unwrap(); + let args = inner.args().unwrap(); + let env = inner.env().unwrap(); + let cwd = inner.cwd(); - let mut c = Command::new(args.get(0).unwrap()); + let mut c = Command::new(args.get(0)); (1..args.len()).for_each(|i| { - let arg = args.get(i).unwrap(); + let arg = args.get(i); c.arg(arg); }); cwd.map(|d| c.current_dir(d)); - for (key, value) in &env { - c.env(key, value); - } + (0..env.len()).for_each(|i| { + let entry = env.get(i); + c.env(entry.key().unwrap(), entry.value().unwrap()); + }); // TODO: make this work with other resources, eg. sockets - let stdin_rid = run_args.stdin_rid; + let stdin_rid = inner.stdin_rid(); if stdin_rid > 0 { c.stdin(resources::get_file(stdin_rid)?); } else { - c.stdin(subprocess_stdio_map(run_args.stdin.as_ref())); + c.stdin(subprocess_stdio_map(inner.stdin())); } - let stdout_rid = run_args.stdout_rid; + let stdout_rid = inner.stdout_rid(); if stdout_rid > 0 { c.stdout(resources::get_file(stdout_rid)?); } else { - c.stdout(subprocess_stdio_map(run_args.stdout.as_ref())); + c.stdout(subprocess_stdio_map(inner.stdout())); } - let stderr_rid = run_args.stderr_rid; + let stderr_rid = inner.stderr_rid(); if stderr_rid > 0 { c.stderr(resources::get_file(stderr_rid)?); } else { - c.stderr(subprocess_stdio_map(run_args.stderr.as_ref())); + c.stderr(subprocess_stdio_map(inner.stderr())); } // Spawn the command. @@ -88,28 +83,44 @@ pub fn op_run( let pid = child.id(); let resources = resources::add_child(child); - Ok(JsonOp::Sync(json!({ - "rid": resources.child_rid, - "pid": pid, - "stdinRid": resources.stdin_rid, - "stdoutRid": resources.stdout_rid, - "stderrRid": resources.stderr_rid, - }))) -} + let mut res_args = msg::RunResArgs { + rid: resources.child_rid, + pid, + ..Default::default() + }; -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct RunStatusArgs { - rid: i32, + if let Some(stdin_rid) = resources.stdin_rid { + res_args.stdin_rid = stdin_rid; + } + if let Some(stdout_rid) = resources.stdout_rid { + res_args.stdout_rid = stdout_rid; + } + if let Some(stderr_rid) = resources.stderr_rid { + res_args.stderr_rid = stderr_rid; + } + + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::RunRes::create(builder, &res_args); + Ok(Op::Sync(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::RunRes, + ..Default::default() + }, + ))) } pub fn op_run_status( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: RunStatusArgs = serde_json::from_value(args)?; - let rid = args.rid as u32; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_run_status().unwrap(); + let rid = inner.rid(); state.check_run()?; @@ -128,30 +139,44 @@ pub fn op_run_status( .expect("Should have either an exit code or a signal."); let got_signal = signal.is_some(); - futures::future::ok(json!({ - "gotSignal": got_signal, - "exitCode": code.unwrap_or(-1), - "exitSignal": signal.unwrap_or(-1), - })) + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::RunStatusRes::create( + builder, + &msg::RunStatusResArgs { + got_signal, + exit_code: code.unwrap_or(-1), + exit_signal: signal.unwrap_or(-1), + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::RunStatusRes, + ..Default::default() + }, + )) }); - - Ok(JsonOp::Async(Box::new(future))) -} - -#[derive(Deserialize)] -struct KillArgs { - pid: i32, - signo: i32, + if base.sync() { + let buf = future.wait()?; + Ok(Op::Sync(buf)) + } else { + Ok(Op::Async(Box::new(future))) + } } pub fn op_kill( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { state.check_run()?; - let args: KillArgs = serde_json::from_value(args)?; - kill(args.pid, args.signo)?; - Ok(JsonOp::Sync(json!({}))) + assert!(data.is_none()); + let inner = base.inner_as_kill().unwrap(); + let pid = inner.pid(); + let signo = inner.signo(); + kill(pid, signo)?; + ok_buf(empty_buf()) } diff --git a/cli/ops/random.rs b/cli/ops/random.rs index 7470eab402..0c302a0808 100644 --- a/cli/ops/random.rs +++ b/cli/ops/random.rs @@ -1,5 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{JsonOp, Value}; +use super::utils::*; +use crate::msg; use crate::state::ThreadSafeState; use deno::*; use rand::thread_rng; @@ -7,18 +8,16 @@ use rand::Rng; pub fn op_get_random_values( state: &ThreadSafeState, - _args: Value, - zero_copy: Option, -) -> Result { - assert!(zero_copy.is_some()); - + _base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { if let Some(ref seeded_rng) = state.seeded_rng { let mut rng = seeded_rng.lock().unwrap(); - rng.fill(&mut zero_copy.unwrap()[..]); + rng.fill(&mut data.unwrap()[..]); } else { let mut rng = thread_rng(); - rng.fill(&mut zero_copy.unwrap()[..]); + rng.fill(&mut data.unwrap()[..]); } - Ok(JsonOp::Sync(json!({}))) + ok_buf(empty_buf()) } diff --git a/cli/ops/repl.rs b/cli/ops/repl.rs index 7ab7509dea..affe787391 100644 --- a/cli/ops/repl.rs +++ b/cli/ops/repl.rs @@ -1,50 +1,78 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::blocking; +use super::utils::ok_buf; +use super::utils::CliOpResult; +use crate::msg; use crate::repl; use crate::resources; use crate::state::ThreadSafeState; use deno::*; - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct ReplStartArgs { - history_file: String, -} +use flatbuffers::FlatBufferBuilder; pub fn op_repl_start( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: ReplStartArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_repl_start().unwrap(); + let cmd_id = base.cmd_id(); + let history_file = String::from(inner.history_file().unwrap()); - debug!("op_repl_start {}", args.history_file); - let history_path = repl::history_path(&state.dir, &args.history_file); + debug!("op_repl_start {}", history_file); + let history_path = repl::history_path(&state.dir, &history_file); let repl = repl::Repl::new(history_path); let resource = resources::add_repl(repl); - Ok(JsonOp::Sync(json!(resource.rid))) -} - -#[derive(Deserialize)] -struct ReplReadlineArgs { - rid: i32, - prompt: String, + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::ReplStartRes::create( + builder, + &msg::ReplStartResArgs { rid: resource.rid }, + ); + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ReplStartRes, + ..Default::default() + }, + )) } pub fn op_repl_readline( _state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: ReplReadlineArgs = serde_json::from_value(args)?; - let rid = args.rid; - let prompt = args.prompt; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let inner = base.inner_as_repl_readline().unwrap(); + let cmd_id = base.cmd_id(); + let rid = inner.rid(); + let prompt = inner.prompt().unwrap().to_owned(); debug!("op_repl_readline {} {}", rid, prompt); - blocking_json(false, move || { - let repl = resources::get_repl(rid as u32)?; + blocking(base.sync(), move || { + let repl = resources::get_repl(rid)?; let line = repl.lock().unwrap().readline(&prompt)?; - Ok(json!(line)) + + let builder = &mut FlatBufferBuilder::new(); + let line_off = builder.create_string(&line); + let inner = msg::ReplReadlineRes::create( + builder, + &msg::ReplReadlineResArgs { + line: Some(line_off), + }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ReplReadlineRes, + ..Default::default() + }, + )) }) } diff --git a/cli/ops/resources.rs b/cli/ops/resources.rs index dafd01d08c..975d94490b 100644 --- a/cli/ops/resources.rs +++ b/cli/ops/resources.rs @@ -1,14 +1,54 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::ok_buf; +use super::utils::CliOpResult; +use crate::msg; use crate::resources::table_entries; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; pub fn op_resources( _state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + + let builder = &mut FlatBufferBuilder::new(); let serialized_resources = table_entries(); - Ok(JsonOp::Sync(json!(serialized_resources))) + + let res: Vec<_> = serialized_resources + .iter() + .map(|(key, value)| { + let repr = builder.create_string(value); + + msg::Resource::create( + builder, + &msg::ResourceArgs { + rid: *key, + repr: Some(repr), + }, + ) + }) + .collect(); + + let resources = builder.create_vector(&res); + let inner = msg::ResourcesRes::create( + builder, + &msg::ResourcesResArgs { + resources: Some(resources), + }, + ); + + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::ResourcesRes, + ..Default::default() + }, + )) } diff --git a/cli/ops/timers.rs b/cli/ops/timers.rs index 46217a1882..550d91f2c4 100644 --- a/cli/ops/timers.rs +++ b/cli/ops/timers.rs @@ -1,7 +1,12 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::empty_buf; +use super::utils::CliOpResult; +use crate::deno_error; +use crate::msg; use crate::state::ThreadSafeState; use deno::*; +use flatbuffers::FlatBufferBuilder; use futures::Future; use std; use std::time::Duration; @@ -9,34 +14,50 @@ use std::time::Instant; pub fn op_global_timer_stop( state: &ThreadSafeState, - _args: Value, - _zero_copy: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if !base.sync() { + return Err(deno_error::no_async_support()); + } + assert!(data.is_none()); let state = state; let mut t = state.global_timer.lock().unwrap(); t.cancel(); - Ok(JsonOp::Sync(json!({}))) -} - -#[derive(Deserialize)] -struct GlobalTimerArgs { - timeout: u64, + Ok(Op::Sync(empty_buf())) } pub fn op_global_timer( state: &ThreadSafeState, - args: Value, - _zero_copy: Option, -) -> Result { - let args: GlobalTimerArgs = serde_json::from_value(args)?; - let val = args.timeout; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if base.sync() { + return Err(deno_error::no_sync_support()); + } + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_global_timer().unwrap(); + let val = inner.timeout(); + assert!(val >= 0); let state = state; let mut t = state.global_timer.lock().unwrap(); let deadline = Instant::now() + Duration::from_millis(val as u64); - let f = t - .new_timeout(deadline) - .then(move |_| futures::future::ok(json!({}))); + let f = t.new_timeout(deadline); - Ok(JsonOp::Async(Box::new(f))) + Ok(Op::Async(Box::new(f.then(move |_| { + let builder = &mut FlatBufferBuilder::new(); + let inner = + msg::GlobalTimerRes::create(builder, &msg::GlobalTimerResArgs {}); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::GlobalTimerRes, + ..Default::default() + }, + )) + })))) } diff --git a/cli/ops/workers.rs b/cli/ops/workers.rs index 4eeecd068c..1eb11420fd 100644 --- a/cli/ops/workers.rs +++ b/cli/ops/workers.rs @@ -1,12 +1,17 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use super::dispatch_json::{Deserialize, JsonOp, Value}; +use super::dispatch_flatbuffers::serialize_response; +use super::utils::ok_buf; +use super::utils::CliOpResult; +use crate::deno_error; use crate::deno_error::DenoError; use crate::deno_error::ErrorKind; +use crate::msg; use crate::resources; use crate::startup_data; use crate::state::ThreadSafeState; use crate::worker::Worker; use deno::*; +use flatbuffers::FlatBufferBuilder; use futures; use futures::Async; use futures::Future; @@ -34,32 +39,48 @@ impl Future for GetMessageFuture { /// Get message from host as guest worker pub fn op_worker_get_message( state: &ThreadSafeState, - _args: Value, - _data: Option, -) -> Result { + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if base.sync() { + return Err(deno_error::no_sync_support()); + } + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let op = GetMessageFuture { state: state.clone(), }; + let op = op.map_err(move |_| -> ErrBox { unimplemented!() }); + let op = op.and_then(move |maybe_buf| -> Result { + debug!("op_worker_get_message"); + let builder = &mut FlatBufferBuilder::new(); - let op = op - .map_err(move |_| -> ErrBox { unimplemented!() }) - .and_then(move |maybe_buf| { - debug!("op_worker_get_message"); - - futures::future::ok(json!({ - "data": maybe_buf.map(|buf| buf.to_owned()) - })) - }); - - Ok(JsonOp::Async(Box::new(op))) + let data = maybe_buf.as_ref().map(|buf| builder.create_vector(buf)); + let inner = msg::WorkerGetMessageRes::create( + builder, + &msg::WorkerGetMessageResArgs { data }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::WorkerGetMessageRes, + ..Default::default() + }, + )) + }); + Ok(Op::Async(Box::new(op))) } /// Post message to host as guest worker pub fn op_worker_post_message( state: &ThreadSafeState, - _args: Value, + base: &msg::Base<'_>, data: Option, -) -> Result { +) -> CliOpResult { + let cmd_id = base.cmd_id(); let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice(); let tx = { @@ -69,34 +90,33 @@ pub fn op_worker_post_message( tx.send(d) .wait() .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?; + let builder = &mut FlatBufferBuilder::new(); - Ok(JsonOp::Sync(json!({}))) -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct CreateWorkerArgs { - specifier: String, - include_deno_namespace: bool, - has_source_code: bool, - source_code: String, + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + ..Default::default() + }, + )) } /// Create worker as the host pub fn op_create_worker( state: &ThreadSafeState, - args: Value, - _data: Option, -) -> Result { - let args: CreateWorkerArgs = serde_json::from_value(args)?; - - let specifier = args.specifier.as_ref(); + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_create_worker().unwrap(); + let specifier = inner.specifier().unwrap(); // Only include deno namespace if requested AND current worker // has included namespace (to avoid escalation). let include_deno_namespace = - args.include_deno_namespace && state.include_deno_namespace; - let has_source_code = args.has_source_code; - let source_code = args.source_code; + inner.include_deno_namespace() && state.include_deno_namespace; + let has_source_code = inner.has_source_code(); + let source_code = inner.source_code().unwrap(); let parent_state = state.clone(); @@ -130,13 +150,24 @@ pub fn op_create_worker( let exec_cb = move |worker: Worker| { let mut workers_tl = parent_state.workers.lock().unwrap(); workers_tl.insert(rid, worker.shared()); - json!(rid) + let builder = &mut FlatBufferBuilder::new(); + let msg_inner = + msg::CreateWorkerRes::create(builder, &msg::CreateWorkerResArgs { rid }); + serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(msg_inner.as_union_value()), + inner_type: msg::Any::CreateWorkerRes, + ..Default::default() + }, + ) }; // Has provided source code, execute immediately. if has_source_code { worker.execute(&source_code).unwrap(); - return Ok(JsonOp::Sync(exec_cb(worker))); + return ok_buf(exec_cb(worker)); } let op = worker @@ -144,23 +175,22 @@ pub fn op_create_worker( .and_then(move |()| Ok(exec_cb(worker))); let result = op.wait()?; - Ok(JsonOp::Sync(result)) -} - -#[derive(Deserialize)] -struct HostGetWorkerClosedArgs { - rid: i32, + Ok(Op::Sync(result)) } /// Return when the worker closes pub fn op_host_get_worker_closed( state: &ThreadSafeState, - args: Value, - _data: Option, -) -> Result { - let args: HostGetWorkerClosedArgs = serde_json::from_value(args)?; - - let rid = args.rid as u32; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if base.sync() { + return Err(deno_error::no_sync_support()); + } + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_host_get_worker_closed().unwrap(); + let rid = inner.rid(); let state = state.clone(); let shared_worker_future = { @@ -169,58 +199,79 @@ pub fn op_host_get_worker_closed( worker.clone() }; - let op = Box::new( - shared_worker_future.then(move |_result| futures::future::ok(json!({}))), - ); + let op = Box::new(shared_worker_future.then(move |_result| { + let builder = &mut FlatBufferBuilder::new(); - Ok(JsonOp::Async(Box::new(op))) -} - -#[derive(Deserialize)] -struct HostGetMessageArgs { - rid: i32, + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + ..Default::default() + }, + )) + })); + Ok(Op::Async(Box::new(op))) } /// Get message from guest worker as host pub fn op_host_get_message( _state: &ThreadSafeState, - args: Value, - _data: Option, -) -> Result { - let args: HostGetMessageArgs = serde_json::from_value(args)?; + base: &msg::Base<'_>, + data: Option, +) -> CliOpResult { + if base.sync() { + return Err(deno_error::no_sync_support()); + } + assert!(data.is_none()); + let cmd_id = base.cmd_id(); + let inner = base.inner_as_host_get_message().unwrap(); + let rid = inner.rid(); - let rid = args.rid as u32; - let op = resources::get_message_from_worker(rid) - .map_err(move |_| -> ErrBox { unimplemented!() }) - .and_then(move |maybe_buf| { - futures::future::ok(json!({ - "data": maybe_buf.map(|buf| buf.to_owned()) - })) - }); + let op = resources::get_message_from_worker(rid); + let op = op.map_err(move |_| -> ErrBox { unimplemented!() }); + let op = op.and_then(move |maybe_buf| -> Result { + let builder = &mut FlatBufferBuilder::new(); - Ok(JsonOp::Async(Box::new(op))) -} - -#[derive(Deserialize)] -struct HostPostMessageArgs { - rid: i32, + let data = maybe_buf.as_ref().map(|buf| builder.create_vector(buf)); + let msg_inner = msg::HostGetMessageRes::create( + builder, + &msg::HostGetMessageResArgs { data }, + ); + Ok(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + inner: Some(msg_inner.as_union_value()), + inner_type: msg::Any::HostGetMessageRes, + ..Default::default() + }, + )) + }); + Ok(Op::Async(Box::new(op))) } /// Post message to guest worker as host pub fn op_host_post_message( _state: &ThreadSafeState, - args: Value, + base: &msg::Base<'_>, data: Option, -) -> Result { - let args: HostPostMessageArgs = serde_json::from_value(args)?; - - let rid = args.rid as u32; +) -> CliOpResult { + let cmd_id = base.cmd_id(); + let inner = base.inner_as_host_post_message().unwrap(); + let rid = inner.rid(); let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice(); resources::post_message_to_worker(rid, d) .wait() .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?; + let builder = &mut FlatBufferBuilder::new(); - Ok(JsonOp::Sync(json!({}))) + ok_buf(serialize_response( + cmd_id, + builder, + msg::BaseArgs { + ..Default::default() + }, + )) } diff --git a/js/compiler.ts b/js/compiler.ts index 5399d59ad3..7519c51153 100644 --- a/js/compiler.ts +++ b/js/compiler.ts @@ -7,11 +7,9 @@ import { Console } from "./console"; import { core } from "./core"; import { Diagnostic, fromTypeScriptDiagnostic } from "./diagnostics"; import { cwd } from "./dir"; -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; -import { msg } from "./dispatch_flatbuffers"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; import * as os from "./os"; -import { TextEncoder } from "./text_encoding"; +import { TextDecoder, TextEncoder } from "./text_encoding"; import { getMappedModuleName, parseTypeDirectives } from "./type_directives"; import { assert, notImplemented } from "./util"; import * as util from "./util"; @@ -123,15 +121,35 @@ interface EmitResult { /** Ops to Rust to resolve and fetch a modules meta data. */ function fetchSourceFile(specifier: string, referrer: string): SourceFile { - util.log("compiler.fetchSourceFile", { specifier, referrer }); - const res = sendSync(dispatch.OP_FETCH_SOURCE_FILE, { - specifier, - referrer - }); - + util.log("fetchSourceFile", { specifier, referrer }); + // Send FetchSourceFile message + const builder = flatbuffers.createBuilder(); + const specifier_ = builder.createString(specifier); + const referrer_ = builder.createString(referrer); + const inner = msg.FetchSourceFile.createFetchSourceFile( + builder, + specifier_, + referrer_ + ); + const baseRes = sendSync(builder, msg.Any.FetchSourceFile, inner); + assert(baseRes != null); + assert( + msg.Any.FetchSourceFileRes === baseRes!.innerType(), + `base.innerType() unexpectedly is ${baseRes!.innerType()}` + ); + const fetchSourceFileRes = new msg.FetchSourceFileRes(); + assert(baseRes!.inner(fetchSourceFileRes) != null); + const dataArray = fetchSourceFileRes.dataArray(); + const decoder = new TextDecoder(); + const sourceCode = dataArray ? decoder.decode(dataArray) : undefined; + // flatbuffers returns `null` for an empty value, this does not fit well with + // idiomatic TypeScript under strict null checks, so converting to `undefined` return { - ...res, - typeDirectives: parseTypeDirectives(res.sourceCode) + moduleName: fetchSourceFileRes.moduleName() || undefined, + filename: fetchSourceFileRes.filename() || undefined, + mediaType: fetchSourceFileRes.mediaType(), + sourceCode, + typeDirectives: parseTypeDirectives(sourceCode) }; } @@ -153,7 +171,19 @@ function humanFileSize(bytes: number): string { /** Ops to rest for caching source map and compiled js */ function cache(extension: string, moduleId: string, contents: string): void { - sendSync(dispatch.OP_CACHE, { extension, moduleId, contents }); + util.log("cache", extension, moduleId); + const builder = flatbuffers.createBuilder(); + const extension_ = builder.createString(extension); + const moduleId_ = builder.createString(moduleId); + const contents_ = builder.createString(contents); + const inner = msg.Cache.createCache( + builder, + extension_, + moduleId_, + contents_ + ); + const baseRes = sendSync(builder, msg.Any.Cache, inner); + assert(baseRes == null); } const encoder = new TextEncoder(); diff --git a/js/dispatch.ts b/js/dispatch.ts index 6c7551441a..0c5c59553f 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -12,40 +12,6 @@ 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_SET_ENV = 8; -export const OP_HOME_DIR = 9; -export const OP_START = 10; -export const OP_APPLY_SOURCE_MAP = 11; -export const OP_FORMAT_ERROR = 12; -export const OP_CACHE = 13; -export const OP_FETCH_SOURCE_FILE = 14; -export const OP_OPEN = 15; -export const OP_CLOSE = 16; -export const OP_SEEK = 17; -export const OP_FETCH = 18; -export const OP_METRICS = 19; -export const OP_REPL_START = 20; -export const OP_REPL_READLINE = 21; -export const OP_ACCEPT = 22; -export const OP_DIAL = 23; -export const OP_SHUTDOWN = 24; -export const OP_LISTEN = 25; -export const OP_RESOURCES = 26; -export const OP_GET_RANDOM_VALUES = 27; -export const OP_GLOBAL_TIMER_STOP = 28; -export const OP_GLOBAL_TIMER = 29; -export const OP_NOW = 30; -export const OP_PERMISSIONS = 31; -export const OP_REVOKE_PERMISSION = 32; -export const OP_CREATE_WORKER = 33; -export const OP_HOST_GET_WORKER_CLOSED = 34; -export const OP_HOST_POST_MESSAGE = 35; -export const OP_HOST_GET_MESSAGE = 36; -export const OP_WORKER_POST_MESSAGE = 37; -export const OP_WORKER_GET_MESSAGE = 38; -export const OP_RUN = 39; -export const OP_RUN_STATUS = 40; -export const OP_KILL = 41; export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { switch (opId) { @@ -56,26 +22,10 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { case OP_READ: minimal.asyncMsgFromRust(opId, ui8); break; - case OP_EXIT: - case OP_IS_TTY: - case OP_ENV: - case OP_EXEC_PATH: case OP_UTIME: - case OP_OPEN: - case OP_SEEK: - case OP_FETCH: - case OP_REPL_START: - case OP_REPL_READLINE: - case OP_ACCEPT: - case OP_DIAL: - case OP_GLOBAL_TIMER: - case OP_HOST_GET_WORKER_CLOSED: - case OP_HOST_GET_MESSAGE: - case OP_WORKER_GET_MESSAGE: - case OP_RUN_STATUS: json.asyncMsgFromRust(opId, ui8); break; default: - throw Error("bad async opId"); + throw Error("bad opId"); } } diff --git a/js/error_stack.ts b/js/error_stack.ts index 7d41b9cf4a..003717a72d 100644 --- a/js/error_stack.ts +++ b/js/error_stack.ts @@ -1,8 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Some of the code here is adapted directly from V8 and licensed under a BSD // style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8 -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; + +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { assert } from "./util"; export interface Location { @@ -17,6 +17,40 @@ export interface Location { column: number; } +function req( + filename: string, + line: number, + column: number +): [flatbuffers.Builder, msg.Any.ApplySourceMap, flatbuffers.Offset] { + const builder = flatbuffers.createBuilder(); + const filename_ = builder.createString(filename); + const inner = msg.ApplySourceMap.createApplySourceMap( + builder, + filename_, + // On this side, line/column are 1 based, but in the source maps, they are + // 0 based, so we have to convert back and forth + line - 1, + column - 1 + ); + return [builder, msg.Any.ApplySourceMap, inner]; +} + +function res(baseRes: msg.Base | null): Location { + assert(baseRes != null); + assert(baseRes!.innerType() === msg.Any.ApplySourceMap); + const res = new msg.ApplySourceMap(); + assert(baseRes!.inner(res) != null); + const filename = res.filename()!; + assert(filename != null); + return { + filename, + // On this side, line/column are 1 based, but in the source maps, they are + // 0 based, so we have to convert back and forth + line: res.line() + 1, + column: res.column() + 1 + }; +} + /** Given a current location in a module, lookup the source location and * return it. * @@ -41,18 +75,7 @@ export interface Location { */ export function applySourceMap(location: Location): Location { const { filename, line, column } = location; - // On this side, line/column are 1 based, but in the source maps, they are - // 0 based, so we have to convert back and forth - const res = sendSync(dispatch.OP_APPLY_SOURCE_MAP, { - filename, - line: line - 1, - column: column - 1 - }); - return { - filename: res.filename, - line: res.line + 1, - column: res.column + 1 - }; + return res(sendSync(...req(filename, line, column))); } /** Mutate the call site so that it returns the location, instead of its diff --git a/js/fetch.ts b/js/fetch.ts index 317239630a..505da218c3 100644 --- a/js/fetch.ts +++ b/js/fetch.ts @@ -1,5 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { assert, createResolvable, notImplemented, isTypedArray } from "./util"; +import { sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers"; import * as domTypes from "./dom_types"; import { TextDecoder, TextEncoder } from "./text_encoding"; import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob"; @@ -9,8 +10,6 @@ import { read, close } from "./files"; import { Buffer } from "./buffer"; import { FormData } from "./form_data"; import { URLSearchParams } from "./url_search_params"; -import * as dispatch from "./dispatch"; -import { sendAsync } from "./dispatch_json"; function getHeaderValueParams(value: string): Map { const params = new Map(); @@ -321,35 +320,67 @@ export class Response implements domTypes.Response { } } -interface FetchResponse { - bodyRid: number; - status: number; - headers: Array<[string, string]>; +function msgHttpRequest( + builder: flatbuffers.Builder, + url: string, + method: null | string, + headers: null | domTypes.Headers +): flatbuffers.Offset { + const methodOffset = !method ? 0 : builder.createString(method); + let fieldsOffset: flatbuffers.Offset = 0; + const urlOffset = builder.createString(url); + if (headers) { + const kvOffsets: flatbuffers.Offset[] = []; + for (const [key, val] of headers.entries()) { + const keyOffset = builder.createString(key); + const valOffset = builder.createString(val); + kvOffsets.push( + msg.KeyValue.createKeyValue(builder, keyOffset, valOffset) + ); + } + fieldsOffset = msg.HttpHeader.createFieldsVector(builder, kvOffsets); + } else { + } + return msg.HttpHeader.createHttpHeader( + builder, + true, + methodOffset, + urlOffset, + 0, + fieldsOffset + ); } -async function sendFetchReq( +function deserializeHeaderFields(m: msg.HttpHeader): Array<[string, string]> { + const out: Array<[string, string]> = []; + for (let i = 0; i < m.fieldsLength(); i++) { + const item = m.fields(i)!; + out.push([item.key()!, item.value()!]); + } + return out; +} + +async function getFetchRes( url: string, method: string | null, headers: domTypes.Headers | null, body: ArrayBufferView | undefined -): Promise { - let headerArray: Array<[string, string]> = []; - if (headers) { - headerArray = Array.from(headers.entries()); - } +): Promise { + // Send Fetch message + const builder = flatbuffers.createBuilder(); + const headerOff = msgHttpRequest(builder, url, method, headers); + const resBase = await sendAsync( + builder, + msg.Any.Fetch, + msg.Fetch.createFetch(builder, headerOff), + body + ); - let zeroCopy = undefined; - if (body) { - zeroCopy = new Uint8Array(body.buffer, body.byteOffset, body.byteLength); - } - - const args = { - method, - url, - headers: headerArray - }; - - return (await sendAsync(dispatch.OP_FETCH, args, zeroCopy)) as FetchResponse; + // Decode FetchRes + assert(msg.Any.FetchRes === resBase.innerType()); + const inner = new msg.FetchRes(); + assert(resBase.inner(inner) != null); + return inner; } /** Fetch a resource from the network. */ @@ -417,13 +448,20 @@ export async function fetch( } while (remRedirectCount) { - const fetchResponse = await sendFetchReq(url, method, headers, body); + const inner = await getFetchRes(url, method, headers, body); + + const header = inner.header()!; + const bodyRid = inner.bodyRid(); + assert(!header.isRequest()); + const status = header.status(); + + const headersList = deserializeHeaderFields(header); const response = new Response( url, - fetchResponse.status, - fetchResponse.headers, - fetchResponse.bodyRid, + status, + headersList, + bodyRid, redirected ); if ([301, 302, 303, 307, 308].includes(response.status)) { diff --git a/js/files.ts b/js/files.ts index 4eff17aac2..6f0e523c96 100644 --- a/js/files.ts +++ b/js/files.ts @@ -12,22 +12,37 @@ import { } from "./io"; import { sendAsyncMinimal } from "./dispatch_minimal"; import { assert } from "./util"; -import * as dispatch from "./dispatch"; -import { - sendSync as sendSyncJson, - sendAsync as sendAsyncJson -} from "./dispatch_json"; -import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import { sendAsync, sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { OP_READ, OP_WRITE } from "./dispatch"; +function reqOpen( + filename: string, + mode: OpenMode +): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { + const builder = flatbuffers.createBuilder(); + const filename_ = builder.createString(filename); + const mode_ = builder.createString(mode); + const inner = msg.Open.createOpen(builder, filename_, 0, mode_); + return [builder, msg.Any.Open, inner]; +} + +function resOpen(baseRes: null | msg.Base): File { + assert(baseRes != null); + assert(msg.Any.OpenRes === baseRes!.innerType()); + const res = new msg.OpenRes(); + assert(baseRes!.inner(res) != null); + const rid = res.rid(); + // eslint-disable-next-line @typescript-eslint/no-use-before-define + return new File(rid); +} + /** Open a file and return an instance of the `File` object * synchronously. * * const file = Deno.openSync("/foo/bar.txt"); */ export function openSync(filename: string, mode: OpenMode = "r"): File { - const rid = sendSyncJson(dispatch.OP_OPEN, { filename, mode }); - return new File(rid); + return resOpen(sendSync(...reqOpen(filename, mode))); } /** Open a file and return an instance of the `File` object. @@ -40,8 +55,7 @@ export async function open( filename: string, mode: OpenMode = "r" ): Promise { - const rid = await sendAsyncJson(dispatch.OP_OPEN, { filename, mode }); - return new File(rid); + return resOpen(await sendAsync(...reqOpen(filename, mode))); } function reqRead( @@ -151,13 +165,23 @@ export async function write(rid: number, p: Uint8Array): Promise { } } +function reqSeek( + rid: number, + offset: number, + whence: SeekMode +): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { + const builder = flatbuffers.createBuilder(); + const inner = msg.Seek.createSeek(builder, rid, offset, whence); + return [builder, msg.Any.Seek, inner]; +} + /** Seek a file ID synchronously to the given offset under mode given by `whence`. * * const file = Deno.openSync("/foo/bar.txt"); * Deno.seekSync(file.rid, 0, 0); */ export function seekSync(rid: number, offset: number, whence: SeekMode): void { - sendSyncJson(dispatch.OP_SEEK, { rid, offset, whence }); + sendSync(...reqSeek(rid, offset, whence)); } /** Seek a file ID to the given offset under mode given by `whence`. @@ -172,12 +196,14 @@ export async function seek( offset: number, whence: SeekMode ): Promise { - await sendAsyncJson(dispatch.OP_SEEK, { rid, offset, whence }); + await sendAsync(...reqSeek(rid, offset, whence)); } /** Close the file ID. */ export function close(rid: number): void { - sendSyncJson(dispatch.OP_CLOSE, { rid }); + const builder = flatbuffers.createBuilder(); + const inner = msg.Close.createClose(builder, rid); + sendSync(builder, msg.Any.Close, inner); } /** The Deno abstraction for reading and writing files. */ diff --git a/js/format_error.ts b/js/format_error.ts index dde0f6a583..6670b05e2c 100644 --- a/js/format_error.ts +++ b/js/format_error.ts @@ -1,9 +1,17 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import { assert } from "./util"; -// TODO(bartlomieju): move to `repl.ts`? export function formatError(errString: string): string { - const res = sendSync(dispatch.OP_FORMAT_ERROR, { error: errString }); - return res.error; + const builder = flatbuffers.createBuilder(); + const errString_ = builder.createString(errString); + const offset = msg.FormatError.createFormatError(builder, errString_); + const baseRes = sendSync(builder, msg.Any.FormatError, offset); + assert(baseRes != null); + assert(msg.Any.FormatErrorRes === baseRes!.innerType()); + const formatErrorResMsg = new msg.FormatErrorRes(); + assert(baseRes!.inner(formatErrorResMsg) != null); + const formattedError = formatErrorResMsg.error(); + assert(formatError != null); + return formattedError!; } diff --git a/js/get_random_values.ts b/js/get_random_values.ts index 154e77f753..d5c0828c55 100644 --- a/js/get_random_values.ts +++ b/js/get_random_values.ts @@ -1,8 +1,15 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { assert } from "./util"; +function req( + typedArray: ArrayBufferView +): [flatbuffers.Builder, msg.Any, flatbuffers.Offset, ArrayBufferView] { + const builder = flatbuffers.createBuilder(); + const inner = msg.GetRandomValues.createGetRandomValues(builder); + return [builder, msg.Any.GetRandomValues, inner, typedArray]; +} + /** Synchronously collects cryptographically secure random values. The * underlying CSPRNG in use is Rust's `rand::rngs::ThreadRng`. * @@ -21,11 +28,6 @@ export function getRandomValues< >(typedArray: T): T { assert(typedArray !== null, "Input must not be null"); assert(typedArray.length <= 65536, "Input must not be longer than 65536"); - const ui8 = new Uint8Array( - typedArray.buffer, - typedArray.byteOffset, - typedArray.byteLength - ); - sendSync(dispatch.OP_GET_RANDOM_VALUES, {}, ui8); + sendSync(...req(typedArray as ArrayBufferView)); return typedArray; } diff --git a/js/main.ts b/js/main.ts index 25e27b69fe..769f522a63 100644 --- a/js/main.ts +++ b/js/main.ts @@ -22,12 +22,12 @@ export default function denoMain( preserveDenoNamespace: boolean = true, name?: string ): void { - const s = os.start(preserveDenoNamespace, name); + const startResMsg = os.start(preserveDenoNamespace, name); - setVersions(s.denoVersion, s.v8Version); + setVersions(startResMsg.denoVersion()!, startResMsg.v8Version()!); // handle `--version` - if (s.versionFlag) { + if (startResMsg.versionFlag()) { console.log("deno:", deno.version.deno); console.log("v8:", deno.version.v8); console.log("typescript:", deno.version.typescript); @@ -36,22 +36,24 @@ export default function denoMain( setPrepareStackTrace(Error); - if (s.mainModule) { - assert(s.mainModule.length > 0); - setLocation(s.mainModule); + const mainModule = startResMsg.mainModule(); + if (mainModule) { + assert(mainModule.length > 0); + setLocation(mainModule); } - log("cwd", s.cwd); + const cwd = startResMsg.cwd(); + log("cwd", cwd); - for (let i = 1; i < s.argv.length; i++) { - args.push(s.argv[i]); + for (let i = 1; i < startResMsg.argvLength(); i++) { + args.push(startResMsg.argv(i)); } log("args", args); Object.freeze(args); if (window["_xevalWrapper"] !== undefined) { - xevalMain(window["_xevalWrapper"] as XevalFunc, s.xevalDelim); - } else if (!s.mainModule) { + xevalMain(window["_xevalWrapper"] as XevalFunc, startResMsg.xevalDelim()); + } else if (!mainModule) { replLoop(); } } diff --git a/js/metrics.ts b/js/metrics.ts index 48e3102e52..e93e9528c7 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 * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +import { assert } from "./util"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; export interface Metrics { opsDispatched: number; @@ -10,6 +10,27 @@ 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()) @@ -24,5 +45,5 @@ export interface Metrics { * └──────────────────┴────────┘ */ export function metrics(): Metrics { - return sendSync(dispatch.OP_METRICS); + return res(sendSync(...req())); } diff --git a/js/net.ts b/js/net.ts index b478ae6132..9c3bfbba50 100644 --- a/js/net.ts +++ b/js/net.ts @@ -1,9 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { EOF, Reader, Writer, Closer } from "./io"; -import { notImplemented } from "./util"; +import { assert, notImplemented } from "./util"; +import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { read, write, close } from "./files"; -import * as dispatch from "./dispatch"; -import { sendSync, sendAsync } from "./dispatch_json"; export type Network = "tcp"; // TODO support other types: @@ -37,7 +36,10 @@ enum ShutdownMode { } function shutdown(rid: number, how: ShutdownMode): void { - sendSync(dispatch.OP_SHUTDOWN, { rid, how }); + const builder = flatbuffers.createBuilder(); + const inner = msg.Shutdown.createShutdown(builder, rid, how); + const baseRes = sendSync(builder, msg.Any.Shutdown, inner); + assert(baseRes == null); } class ConnImpl implements Conn { @@ -78,9 +80,14 @@ class ListenerImpl implements Listener { constructor(readonly rid: number) {} async accept(): Promise { - const res = await sendAsync(dispatch.OP_ACCEPT, { rid: this.rid }); - // TODO(bartlomieju): add remoteAddr and localAddr on Rust side - return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); + const builder = flatbuffers.createBuilder(); + const inner = msg.Accept.createAccept(builder, this.rid); + const baseRes = await sendAsync(builder, msg.Any.Accept, inner); + assert(baseRes != null); + assert(msg.Any.NewConn === baseRes!.innerType()); + const res = new msg.NewConn(); + assert(baseRes!.inner(res) != null); + return new ConnImpl(res.rid(), res.remoteAddr()!, res.localAddr()!); } close(): void { @@ -136,8 +143,16 @@ export interface Conn extends Reader, Writer, Closer { * See `dial()` for a description of the network and address parameters. */ export function listen(network: Network, address: string): Listener { - const rid = sendSync(dispatch.OP_LISTEN, { network, address }); - return new ListenerImpl(rid); + const builder = flatbuffers.createBuilder(); + const network_ = builder.createString(network); + const address_ = builder.createString(address); + const inner = msg.Listen.createListen(builder, network_, address_); + const baseRes = sendSync(builder, msg.Any.Listen, inner); + assert(baseRes != null); + assert(msg.Any.ListenRes === baseRes!.innerType()); + const res = new msg.ListenRes(); + assert(baseRes!.inner(res) != null); + return new ListenerImpl(res.rid()); } /** Dial connects to the address on the named network. @@ -168,9 +183,16 @@ export function listen(network: Network, address: string): Listener { * dial("tcp", ":80") */ export async function dial(network: Network, address: string): Promise { - const res = await sendAsync(dispatch.OP_DIAL, { network, address }); - // TODO(bartlomieju): add remoteAddr and localAddr on Rust side - return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); + const builder = flatbuffers.createBuilder(); + const network_ = builder.createString(network); + const address_ = builder.createString(address); + const inner = msg.Dial.createDial(builder, network_, address_); + const baseRes = await sendAsync(builder, msg.Any.Dial, inner); + assert(baseRes != null); + assert(msg.Any.NewConn === baseRes!.innerType()); + const res = new msg.NewConn(); + assert(baseRes!.inner(res) != null); + return new ConnImpl(res.rid(), res.remoteAddr()!, res.localAddr()!); } /** **RESERVED** */ diff --git a/js/os.ts b/js/os.ts index c44c278255..f8938ab703 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 * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +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,17 +24,21 @@ function setGlobals(pid_: number, noColor_: boolean): void { * console.log(Deno.isTTY().stdout); */ export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } { - return sendSync(dispatch.OP_IS_TTY); + return dispatchJson.sendSync(dispatch.OP_IS_TTY); } /** Exit the Deno process with optional exit code. */ export function exit(code = 0): never { - sendSync(dispatch.OP_EXIT, { code }); + dispatchJson.sendSync(dispatch.OP_EXIT, { code }); return util.unreachable(); } function setEnv(key: string, value: string): void { - sendSync(dispatch.OP_SET_ENV, { key, value }); + const builder = flatbuffers.createBuilder(); + const key_ = builder.createString(key); + const value_ = builder.createString(value); + const inner = msg.SetEnv.createSetEnv(builder, key_, value_); + sendSync(builder, msg.Any.SetEnv, inner); } /** Returns a snapshot of the environment variables at invocation. Mutating a @@ -48,7 +53,7 @@ function setEnv(key: string, value: string): void { * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); */ export function env(): { [index: string]: string } { - const env = sendSync(dispatch.OP_ENV); + const env = dispatchJson.sendSync(dispatch.OP_ENV); return new Proxy(env, { set(obj, prop: string, value: string): boolean { setEnv(prop, value); @@ -57,35 +62,35 @@ export function env(): { [index: string]: string } { }); } -interface Start { - cwd: string; - pid: number; - argv: string[]; - mainModule: string; // Absolute URL. - debugFlag: boolean; - depsFlag: boolean; - typesFlag: boolean; - versionFlag: boolean; - denoVersion: string; - v8Version: string; - noColor: boolean; - xevalDelim: string; +/** Send to the privileged side that we have setup and are ready. */ +function sendStart(): msg.StartRes { + const builder = flatbuffers.createBuilder(); + const startOffset = msg.Start.createStart(builder, 0 /* unused */); + const baseRes = sendSync(builder, msg.Any.Start, startOffset); + assert(baseRes != null); + assert(msg.Any.StartRes === baseRes!.innerType()); + const startResMsg = new msg.StartRes(); + assert(baseRes!.inner(startResMsg) != null); + return startResMsg; } // This function bootstraps an environment within Deno, it is shared both by // the runtime and the compiler environments. // @internal -export function start(preserveDenoNamespace = true, source?: string): Start { +export function start( + preserveDenoNamespace = true, + source?: string +): msg.StartRes { 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 // args and other info. - const s = sendSync(dispatch.OP_START); + const startResMsg = sendStart(); - util.setLogDebug(s.debugFlag, source); + util.setLogDebug(startResMsg.debugFlag(), source); - setGlobals(s.pid, s.noColor); + setGlobals(startResMsg.pid(), startResMsg.noColor()); if (preserveDenoNamespace) { util.immutableDefine(window, "Deno", window.Deno); @@ -100,7 +105,7 @@ export function start(preserveDenoNamespace = true, source?: string): Start { assert(window.Deno === undefined); } - return s; + return startResMsg; } /** @@ -108,10 +113,18 @@ export function start(preserveDenoNamespace = true, source?: string): Start { * Requires the `--allow-env` flag. */ export function homeDir(): string { - const path = sendSync(dispatch.OP_HOME_DIR); + const builder = flatbuffers.createBuilder(); + const inner = msg.HomeDir.createHomeDir(builder); + const baseRes = sendSync(builder, msg.Any.HomeDir, inner)!; + assert(msg.Any.HomeDirRes === baseRes.innerType()); + const res = new msg.HomeDirRes(); + assert(baseRes.inner(res) != null); + const path = res.path(); + if (!path) { throw new Error("Could not get home directory."); } + return path; } @@ -120,5 +133,5 @@ export function homeDir(): string { * Requires the `--allow-env` flag. */ export function execPath(): string { - return sendSync(dispatch.OP_EXEC_PATH); + return dispatchJson.sendSync(dispatch.OP_EXEC_PATH); } diff --git a/js/performance.ts b/js/performance.ts index d2f339c467..7aaa7ae45a 100644 --- a/js/performance.ts +++ b/js/performance.ts @@ -1,11 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; - -interface NowResponse { - seconds: number; - subsecNanos: number; -} +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import { assert } from "./util"; export class Performance { /** Returns a current time from Deno's start in milliseconds. @@ -16,7 +11,12 @@ export class Performance { * console.log(`${t} ms since start!`); */ now(): number { - const res = sendSync(dispatch.OP_NOW) as NowResponse; - return res.seconds * 1e3 + res.subsecNanos / 1e6; + const builder = flatbuffers.createBuilder(); + const inner = msg.Now.createNow(builder); + const baseRes = sendSync(builder, msg.Any.Now, inner)!; + assert(msg.Any.NowRes === baseRes.innerType()); + const res = new msg.NowRes(); + assert(baseRes.inner(res) != null); + return res.seconds().toFloat64() * 1e3 + res.subsecNanos() / 1e6; } } diff --git a/js/permissions.ts b/js/permissions.ts index bc969f3a83..822ae8cbd3 100644 --- a/js/permissions.ts +++ b/js/permissions.ts @@ -1,6 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import { assert } from "./util"; /** Permissions as granted by the caller */ export interface Permissions { @@ -15,6 +15,23 @@ export interface Permissions { export type Permission = keyof Permissions; +function getReq(): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { + const builder = flatbuffers.createBuilder(); + const inner = msg.Permissions.createPermissions(builder); + return [builder, msg.Any.Permissions, inner]; +} + +function createPermissions(inner: msg.PermissionsRes): Permissions { + return { + read: inner.read(), + write: inner.write(), + net: inner.net(), + env: inner.env(), + run: inner.run(), + hrtime: inner.hrtime() + }; +} + /** Inspect granted permissions for the current program. * * if (Deno.permissions().read) { @@ -23,7 +40,24 @@ export type Permission = keyof Permissions; * } */ export function permissions(): Permissions { - return sendSync(dispatch.OP_PERMISSIONS) as Permissions; + const baseRes = sendSync(...getReq())!; + assert(msg.Any.PermissionsRes === baseRes.innerType()); + const res = new msg.PermissionsRes(); + assert(baseRes.inner(res) != null); + // TypeScript cannot track assertion above, therefore not null assertion + return createPermissions(res); +} + +function revokeReq( + permission: string +): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { + const builder = flatbuffers.createBuilder(); + const permission_ = builder.createString(permission); + const inner = msg.PermissionRevoke.createPermissionRevoke( + builder, + permission_ + ); + return [builder, msg.Any.PermissionRevoke, inner]; } /** Revoke a permission. When the permission was already revoked nothing changes @@ -35,5 +69,5 @@ export function permissions(): Permissions { * Deno.readFile("example.test"); // -> error or permission prompt */ export function revokePermission(permission: Permission): void { - sendSync(dispatch.OP_REVOKE_PERMISSION, { permission }); + sendSync(...revokeReq(permission)); } diff --git a/js/process.ts b/js/process.ts index dd4f701035..b2b6d47348 100644 --- a/js/process.ts +++ b/js/process.ts @@ -1,6 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json"; -import * as dispatch from "./dispatch"; +import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers"; + import { File, close } from "./files"; import { ReadCloser, WriteCloser } from "./io"; import { readAll } from "./buffer"; @@ -31,22 +31,21 @@ export interface RunOptions { stdin?: ProcessStdio | number; } -interface RunStatusResponse { - gotSignal: boolean; - exitCode: number; - exitSignal: number; -} - async function runStatus(rid: number): Promise { - const res = (await sendAsync(dispatch.OP_RUN_STATUS, { - rid - })) as RunStatusResponse; + const builder = flatbuffers.createBuilder(); + const inner = msg.RunStatus.createRunStatus(builder, rid); - if (res.gotSignal) { - const signal = res.exitSignal; + const baseRes = await sendAsync(builder, msg.Any.RunStatus, inner); + assert(baseRes != null); + assert(msg.Any.RunStatusRes === baseRes!.innerType()); + const res = new msg.RunStatusRes(); + assert(baseRes!.inner(res) != null); + + if (res.gotSignal()) { + const signal = res.exitSignal(); return { signal, success: false }; } else { - const code = res.exitCode; + const code = res.exitCode(); return { code, success: code === 0 }; } } @@ -57,7 +56,9 @@ async function runStatus(rid: number): Promise { * Requires the `--allow-run` flag. */ export function kill(pid: number, signo: number): void { - sendSync(dispatch.OP_KILL, { pid, signo }); + const builder = flatbuffers.createBuilder(); + const inner = msg.Kill.createKill(builder, pid, signo); + sendSync(builder, msg.Any.Kill, inner); } export class Process { @@ -68,20 +69,20 @@ export class Process { readonly stderr?: ReadCloser; // @internal - constructor(res: RunResponse) { - this.rid = res.rid; - this.pid = res.pid; + constructor(res: msg.RunRes) { + this.rid = res.rid(); + this.pid = res.pid(); - if (res.stdinRid && res.stdinRid > 0) { - this.stdin = new File(res.stdinRid); + if (res.stdinRid() > 0) { + this.stdin = new File(res.stdinRid()); } - if (res.stdoutRid && res.stdoutRid > 0) { - this.stdout = new File(res.stdoutRid); + if (res.stdoutRid() > 0) { + this.stdout = new File(res.stdoutRid()); } - if (res.stderrRid && res.stderrRid > 0) { - this.stderr = new File(res.stderrRid); + if (res.stderrRid() > 0) { + this.stderr = new File(res.stderrRid()); } } @@ -134,13 +135,14 @@ export interface ProcessStatus { signal?: number; // TODO: Make this a string, e.g. 'SIGTERM'. } -// TODO: this method is only used to validate proper option, probably can be renamed -function stdioMap(s: string): string { +function stdioMap(s: ProcessStdio): msg.ProcessStdio { switch (s) { case "inherit": + return msg.ProcessStdio.Inherit; case "piped": + return msg.ProcessStdio.Piped; case "null": - return s; + return msg.ProcessStdio.Null; default: return unreachable(); } @@ -150,13 +152,6 @@ function isRid(arg: unknown): arg is number { return !isNaN(arg as number); } -interface RunResponse { - rid: number; - pid: number; - stdinRid: number | null; - stdoutRid: number | null; - stderrRid: number | null; -} /** * Spawns new subprocess. * @@ -171,56 +166,71 @@ interface RunResponse { * they can be set to either `ProcessStdio` or `rid` of open file. */ export function run(opt: RunOptions): Process { - assert(opt.args.length > 0); - let env: Array<[string, string]> = []; + const builder = flatbuffers.createBuilder(); + const argsOffset = msg.Run.createArgsVector( + builder, + opt.args.map((a): number => builder.createString(a)) + ); + const cwdOffset = opt.cwd == null ? 0 : builder.createString(opt.cwd); + const kvOffset: flatbuffers.Offset[] = []; if (opt.env) { - env = Array.from(Object.entries(opt.env)); + for (const [key, val] of Object.entries(opt.env)) { + const keyOffset = builder.createString(key); + const valOffset = builder.createString(String(val)); + kvOffset.push(msg.KeyValue.createKeyValue(builder, keyOffset, valOffset)); + } } + const envOffset = msg.Run.createEnvVector(builder, kvOffset); - let stdin = stdioMap("inherit"); - let stdout = stdioMap("inherit"); - let stderr = stdioMap("inherit"); - let stdinRid = 0; - let stdoutRid = 0; - let stderrRid = 0; + let stdInOffset = stdioMap("inherit"); + let stdOutOffset = stdioMap("inherit"); + let stdErrOffset = stdioMap("inherit"); + let stdinRidOffset = 0; + let stdoutRidOffset = 0; + let stderrRidOffset = 0; if (opt.stdin) { if (isRid(opt.stdin)) { - stdinRid = opt.stdin; + stdinRidOffset = opt.stdin; } else { - stdin = stdioMap(opt.stdin); + stdInOffset = stdioMap(opt.stdin); } } if (opt.stdout) { if (isRid(opt.stdout)) { - stdoutRid = opt.stdout; + stdoutRidOffset = opt.stdout; } else { - stdout = stdioMap(opt.stdout); + stdOutOffset = stdioMap(opt.stdout); } } if (opt.stderr) { if (isRid(opt.stderr)) { - stderrRid = opt.stderr; + stderrRidOffset = opt.stderr; } else { - stderr = stdioMap(opt.stderr); + stdErrOffset = stdioMap(opt.stderr); } } - const req = { - args: opt.args.map(String), - cwd: opt.cwd, - env, - stdin, - stdout, - stderr, - stdinRid, - stdoutRid, - stderrRid - }; + const inner = msg.Run.createRun( + builder, + argsOffset, + cwdOffset, + envOffset, + stdInOffset, + stdOutOffset, + stdErrOffset, + stdinRidOffset, + stdoutRidOffset, + stderrRidOffset + ); + const baseRes = sendSync(builder, msg.Any.Run, inner); + assert(baseRes != null); + assert(msg.Any.RunRes === baseRes!.innerType()); + const res = new msg.RunRes(); + assert(baseRes!.inner(res) != null); - const res = sendSync(dispatch.OP_RUN, req) as RunResponse; return new Process(res); } diff --git a/js/repl.ts b/js/repl.ts index ac67006576..c971e44200 100644 --- a/js/repl.ts +++ b/js/repl.ts @@ -1,12 +1,12 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { assert } from "./util"; import { close } from "./files"; +import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { exit } from "./os"; import { window } from "./window"; import { core } from "./core"; import { formatError } from "./format_error"; import { stringifyArgs } from "./console"; -import * as dispatch from "./dispatch"; -import { sendSync, sendAsync } from "./dispatch_json"; /** * REPL logging. @@ -43,12 +43,34 @@ const replCommands = { }; function startRepl(historyFile: string): number { - return sendSync(dispatch.OP_REPL_START, { historyFile }); + const builder = flatbuffers.createBuilder(); + const historyFile_ = builder.createString(historyFile); + const inner = msg.ReplStart.createReplStart(builder, historyFile_); + + const baseRes = sendSync(builder, msg.Any.ReplStart, inner); + assert(baseRes != null); + assert(msg.Any.ReplStartRes === baseRes!.innerType()); + const innerRes = new msg.ReplStartRes(); + assert(baseRes!.inner(innerRes) != null); + const rid = innerRes.rid(); + return rid; } // @internal export async function readline(rid: number, prompt: string): Promise { - return sendAsync(dispatch.OP_REPL_READLINE, { rid, prompt }); + const builder = flatbuffers.createBuilder(); + const prompt_ = builder.createString(prompt); + const inner = msg.ReplReadline.createReplReadline(builder, rid, prompt_); + + const baseRes = await sendAsync(builder, msg.Any.ReplReadline, inner); + + assert(baseRes != null); + assert(msg.Any.ReplReadlineRes === baseRes!.innerType()); + const innerRes = new msg.ReplReadlineRes(); + assert(baseRes!.inner(innerRes) != null); + const line = innerRes.line(); + assert(line !== null); + return line || ""; } // Error messages that allow users to continue input diff --git a/js/resources.ts b/js/resources.ts index 6e2ec202b7..49093fab1a 100644 --- a/js/resources.ts +++ b/js/resources.ts @@ -1,6 +1,6 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch"; -import { sendSync } from "./dispatch_json"; +import { assert } from "./util"; +import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; export interface ResourceMap { [rid: number]: string; @@ -10,10 +10,20 @@ export interface ResourceMap { * representation. */ export function resources(): ResourceMap { - const res = sendSync(dispatch.OP_RESOURCES) as Array<[number, string]>; + const builder = flatbuffers.createBuilder(); + const inner = msg.Resource.createResource(builder, 0, 0); + const baseRes = sendSync(builder, msg.Any.Resources, inner); + assert(baseRes !== null); + assert(msg.Any.ResourcesRes === baseRes!.innerType()); + const res = new msg.ResourcesRes(); + assert(baseRes!.inner(res) !== null); + const resources: ResourceMap = {}; - for (const resourceTuple of res) { - resources[resourceTuple[0]] = resourceTuple[1]; + + for (let i = 0; i < res.resourcesLength(); i++) { + const item = res.resources(i)!; + resources[item.rid()!] = item.repr()!; } + return resources; } diff --git a/js/timers.ts b/js/timers.ts index 079e779c45..cb0fd531c6 100644 --- a/js/timers.ts +++ b/js/timers.ts @@ -1,8 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { assert } from "./util"; +import { sendAsync, sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; import { window } from "./window"; -import * as dispatch from "./dispatch"; -import { sendSync, sendAsync } from "./dispatch_json"; interface Timer { id: number; @@ -38,8 +37,11 @@ function getTime(): number { } function clearGlobalTimeout(): void { + const builder = flatbuffers.createBuilder(); + const inner = msg.GlobalTimerStop.createGlobalTimerStop(builder); globalTimeoutDue = null; - sendSync(dispatch.OP_GLOBAL_TIMER_STOP); + let res = sendSync(builder, msg.Any.GlobalTimerStop, inner); + assert(res == null); } async function setGlobalTimeout(due: number, now: number): Promise { @@ -50,8 +52,12 @@ async function setGlobalTimeout(due: number, now: number): Promise { assert(timeout >= 0); // Send message to the backend. + const builder = flatbuffers.createBuilder(); + msg.GlobalTimer.startGlobalTimer(builder); + msg.GlobalTimer.addTimeout(builder, timeout); + const inner = msg.GlobalTimer.endGlobalTimer(builder); globalTimeoutDue = due; - await sendAsync(dispatch.OP_GLOBAL_TIMER, { timeout }); + await sendAsync(builder, msg.Any.GlobalTimer, inner); // eslint-disable-next-line @typescript-eslint/no-use-before-define fireTimers(); } diff --git a/js/workers.ts b/js/workers.ts index 7bcbe6279a..e59e853c50 100644 --- a/js/workers.ts +++ b/js/workers.ts @@ -1,8 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. /* eslint-disable @typescript-eslint/no-explicit-any */ -import * as dispatch from "./dispatch"; -import { sendAsync, sendSync } from "./dispatch_json"; -import { log } from "./util"; +import { sendAsync, sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import { assert, log } from "./util"; import { TextDecoder, TextEncoder } from "./text_encoding"; import { window } from "./window"; import { blobURLMap } from "./url"; @@ -27,28 +26,61 @@ function createWorker( hasSourceCode: boolean, sourceCode: Uint8Array ): number { - return sendSync(dispatch.OP_CREATE_WORKER, { - specifier, + const builder = flatbuffers.createBuilder(); + const specifier_ = builder.createString(specifier); + const sourceCode_ = builder.createString(sourceCode); + const inner = msg.CreateWorker.createCreateWorker( + builder, + specifier_, includeDenoNamespace, hasSourceCode, - sourceCode: new TextDecoder().decode(sourceCode) - }); + sourceCode_ + ); + const baseRes = sendSync(builder, msg.Any.CreateWorker, inner); + assert(baseRes != null); + assert( + msg.Any.CreateWorkerRes === baseRes!.innerType(), + `base.innerType() unexpectedly is ${baseRes!.innerType()}` + ); + const res = new msg.CreateWorkerRes(); + assert(baseRes!.inner(res) != null); + return res.rid(); } async function hostGetWorkerClosed(rid: number): Promise { - await sendAsync(dispatch.OP_HOST_GET_WORKER_CLOSED, { rid }); + const builder = flatbuffers.createBuilder(); + const inner = msg.HostGetWorkerClosed.createHostGetWorkerClosed(builder, rid); + await sendAsync(builder, msg.Any.HostGetWorkerClosed, inner); } function hostPostMessage(rid: number, data: any): void { const dataIntArray = encodeMessage(data); - sendSync(dispatch.OP_HOST_POST_MESSAGE, { rid }, dataIntArray); + const builder = flatbuffers.createBuilder(); + const inner = msg.HostPostMessage.createHostPostMessage(builder, rid); + const baseRes = sendSync( + builder, + msg.Any.HostPostMessage, + inner, + dataIntArray + ); + assert(baseRes != null); } async function hostGetMessage(rid: number): Promise { - const res = await sendAsync(dispatch.OP_HOST_GET_MESSAGE, { rid }); + const builder = flatbuffers.createBuilder(); + const inner = msg.HostGetMessage.createHostGetMessage(builder, rid); + const baseRes = await sendAsync(builder, msg.Any.HostGetMessage, inner); + assert(baseRes != null); + assert( + msg.Any.HostGetMessageRes === baseRes!.innerType(), + `base.innerType() unexpectedly is ${baseRes!.innerType()}` + ); + const res = new msg.HostGetMessageRes(); + assert(baseRes!.inner(res) != null); - if (res.data != null) { - return decodeMessage(new Uint8Array(res.data)); + const dataArray = res.dataArray(); + if (dataArray != null) { + return decodeMessage(dataArray); } else { return null; } @@ -59,15 +91,36 @@ export let onmessage: (e: { data: any }) => void = (): void => {}; export function postMessage(data: any): void { const dataIntArray = encodeMessage(data); - sendSync(dispatch.OP_WORKER_POST_MESSAGE, {}, dataIntArray); + const builder = flatbuffers.createBuilder(); + const inner = msg.WorkerPostMessage.createWorkerPostMessage(builder); + const baseRes = sendSync( + builder, + msg.Any.WorkerPostMessage, + inner, + dataIntArray + ); + assert(baseRes != null); } export async function getMessage(): Promise { log("getMessage"); - const res = await sendAsync(dispatch.OP_WORKER_GET_MESSAGE); + const builder = flatbuffers.createBuilder(); + const inner = msg.WorkerGetMessage.createWorkerGetMessage( + builder, + 0 /* unused */ + ); + const baseRes = await sendAsync(builder, msg.Any.WorkerGetMessage, inner); + assert(baseRes != null); + assert( + msg.Any.WorkerGetMessageRes === baseRes!.innerType(), + `base.innerType() unexpectedly is ${baseRes!.innerType()}` + ); + const res = new msg.WorkerGetMessageRes(); + assert(baseRes!.inner(res) != null); - if (res.data != null) { - return decodeMessage(new Uint8Array(res.data)); + const dataArray = res.dataArray(); + if (dataArray != null) { + return decodeMessage(dataArray); } else { return null; } diff --git a/tests/error_004_missing_module.ts.out b/tests/error_004_missing_module.ts.out index e8d6727396..cb0f3bc5a5 100644 --- a/tests/error_004_missing_module.ts.out +++ b/tests/error_004_missing_module.ts.out @@ -1,12 +1,12 @@ [WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" -[WILDCARD] js/dispatch_json.ts:[WILDCARD] +[WILDCARD] js/dispatch_flatbuffers.ts:[WILDCARD] at DenoError (js/errors.ts:[WILDCARD]) - at toDenoError (js/dispatch_json.ts:[WILDCARD]) - at sendSync$1 (js/dispatch_json.ts:[WILDCARD]) + at maybeError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at maybeThrowError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at sendSync (js/dispatch_flatbuffers.ts:[WILDCARD]) at fetchSourceFile (js/compiler.ts:[WILDCARD]) at _resolveModule (js/compiler.ts:[WILDCARD]) at js/compiler.ts:[WILDCARD] at resolveModuleNames (js/compiler.ts:[WILDCARD]) at resolveModuleNamesWorker ([WILDCARD]typescript.js:[WILDCARD]) at resolveModuleNamesReusingOldState ([WILDCARD]typescript.js:[WILDCARD]) - at processImportedModules ([WILDCARD]typescript.js:[WILDCARD]) diff --git a/tests/error_005_missing_dynamic_import.ts.out b/tests/error_005_missing_dynamic_import.ts.out index 2f39dc853d..fbc25f7010 100644 --- a/tests/error_005_missing_dynamic_import.ts.out +++ b/tests/error_005_missing_dynamic_import.ts.out @@ -1,11 +1,11 @@ [WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/bad-module.ts" -[WILDCARD] js/dispatch_json.ts:[WILDCARD] +[WILDCARD] js/dispatch_flatbuffers.ts:[WILDCARD] at DenoError (js/errors.ts:[WILDCARD]) - at toDenoError (js/dispatch_json.ts:[WILDCARD]) - at sendSync$1 (js/dispatch_json.ts:[WILDCARD]) + at maybeError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at maybeThrowError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at sendSync (js/dispatch_flatbuffers.ts:[WILDCARD]) at fetchSourceFile (js/compiler.ts:[WILDCARD]) at _resolveModule (js/compiler.ts:[WILDCARD]) at js/compiler.ts:[WILDCARD] at resolveModuleNamesWorker ([WILDCARD]) at resolveModuleNamesReusingOldState ([WILDCARD]typescript.js:[WILDCARD]) - at processImportedModules ([WILDCARD]typescript.js:[WILDCARD]) diff --git a/tests/error_006_import_ext_failure.ts.out b/tests/error_006_import_ext_failure.ts.out index b381fa96d2..cb7ccee85b 100644 --- a/tests/error_006_import_ext_failure.ts.out +++ b/tests/error_006_import_ext_failure.ts.out @@ -1,11 +1,11 @@ [WILDCARD]error: Uncaught NotFound: Cannot resolve module "[WILDCARD]/non-existent" -[WILDCARD] js/dispatch_json.ts:[WILDCARD] +[WILDCARD] js/dispatch_flatbuffers.ts:[WILDCARD] at DenoError (js/errors.ts:[WILDCARD]) - at toDenoError (js/dispatch_json.ts:[WILDCARD]) - at sendSync$1 (js/dispatch_json.ts:[WILDCARD]) + at maybeError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at maybeThrowError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at sendSync (js/dispatch_flatbuffers.ts:[WILDCARD]) at fetchSourceFile (js/compiler.ts:[WILDCARD]) at _resolveModule (js/compiler.ts:[WILDCARD]) at js/compiler.ts:[WILDCARD] at resolveModuleNamesWorker ([WILDCARD]) at resolveModuleNamesReusingOldState ([WILDCARD]typescript.js:[WILDCARD]) - at processImportedModules ([WILDCARD]typescript.js:[WILDCARD]) diff --git a/tests/error_011_bad_module_specifier.ts.out b/tests/error_011_bad_module_specifier.ts.out index 1d8e8c97e4..1590f4e153 100644 --- a/tests/error_011_bad_module_specifier.ts.out +++ b/tests/error_011_bad_module_specifier.ts.out @@ -1,11 +1,12 @@ [WILDCARD]error: Uncaught ImportPrefixMissing: relative import path "bad-module.ts" not prefixed with / or ./ or ../ -[WILDCARD] js/dispatch_json.ts:[WILDCARD] +[WILDCARD] js/dispatch_flatbuffers.ts:[WILDCARD] at DenoError (js/errors.ts:[WILDCARD]) - at toDenoError (js/dispatch_json.ts:[WILDCARD]) - at sendSync$1 (js/dispatch_json.ts:[WILDCARD]) + at maybeError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at maybeThrowError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at sendSync (js/dispatch_flatbuffers.ts:[WILDCARD]) at fetchSourceFile (js/compiler.ts:[WILDCARD]) at _resolveModule (js/compiler.ts:[WILDCARD]) at js/compiler.ts:[WILDCARD] - at resolveModuleNamesWorker ([WILDCARD]) + at resolveModuleNames (js/compiler.ts:[WILDCARD]) + at resolveModuleNamesWorker ([WILDCARD]typescript.js:[WILDCARD]) at resolveModuleNamesReusingOldState ([WILDCARD]typescript.js:[WILDCARD]) - at processImportedModules ([WILDCARD]typescript.js:[WILDCARD]) diff --git a/tests/error_012_bad_dynamic_import_specifier.ts.out b/tests/error_012_bad_dynamic_import_specifier.ts.out index 1d8e8c97e4..ec5cba6f7d 100644 --- a/tests/error_012_bad_dynamic_import_specifier.ts.out +++ b/tests/error_012_bad_dynamic_import_specifier.ts.out @@ -1,11 +1,12 @@ [WILDCARD]error: Uncaught ImportPrefixMissing: relative import path "bad-module.ts" not prefixed with / or ./ or ../ -[WILDCARD] js/dispatch_json.ts:[WILDCARD] +[WILDCARD] js/dispatch_flatbuffers.ts:[WILDCARD] at DenoError (js/errors.ts:[WILDCARD]) - at toDenoError (js/dispatch_json.ts:[WILDCARD]) - at sendSync$1 (js/dispatch_json.ts:[WILDCARD]) + at maybeError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at maybeThrowError (js/dispatch_flatbuffers.ts:[WILDCARD]) + at sendSync (js/dispatch_flatbuffers.ts:[WILDCARD]) at fetchSourceFile (js/compiler.ts:[WILDCARD]) at _resolveModule (js/compiler.ts:[WILDCARD]) at js/compiler.ts:[WILDCARD] + at resolveModuleNames (js/compiler.ts:[WILDCARD]) at resolveModuleNamesWorker ([WILDCARD]) at resolveModuleNamesReusingOldState ([WILDCARD]typescript.js:[WILDCARD]) - at processImportedModules ([WILDCARD]typescript.js:[WILDCARD])