1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

introduce JSON serialization for ops (#2799)

Converts env(), exit(), execPath(), utime() and utimeSync() to use JSON
instead of flatbuffers.
This commit is contained in:
Ryan Dahl 2019-08-22 22:30:14 -07:00 committed by GitHub
parent 47c216317f
commit bc467b265f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 355 additions and 241 deletions

View file

@ -83,7 +83,9 @@ ts_sources = [
"../js/dir.ts",
"../js/dispatch.ts",
"../js/dispatch_flatbuffers.ts",
"../js/dispatch_json.ts",
"../js/dispatch_minimal.ts",
"../js/dom_file.ts",
"../js/dom_types.ts",
"../js/dom_util.ts",
"../js/error_stack.ts",
@ -91,12 +93,11 @@ ts_sources = [
"../js/event.ts",
"../js/event_target.ts",
"../js/fetch.ts",
"../js/format_error.ts",
"../js/dom_file.ts",
"../js/file_info.ts",
"../js/files.ts",
"../js/flatbuffers.ts",
"../js/form_data.ts",
"../js/format_error.ts",
"../js/get_random_values.ts",
"../js/globals.ts",
"../js/headers.ts",
@ -134,10 +135,10 @@ ts_sources = [
"../js/url_search_params.ts",
"../js/util.ts",
"../js/utime.ts",
"../js/version.ts",
"../js/window.ts",
"../js/workers.ts",
"../js/write_file.ts",
"../js/version.ts",
"../js/xeval.ts",
"../tsconfig.json",

View file

@ -13,6 +13,8 @@ extern crate indexmap;
#[cfg(unix)]
extern crate nix;
extern crate rand;
extern crate serde;
extern crate serde_derive;
extern crate url;
mod ansi;

View file

@ -12,9 +12,6 @@ union Any {
Cwd,
CwdRes,
Dial,
Environ,
EnvironRes,
Exit,
Fetch,
FetchSourceFile,
FetchSourceFileRes,
@ -29,8 +26,6 @@ union Any {
HostGetMessageRes,
HostGetWorkerClosed,
HostPostMessage,
IsTTY,
IsTTYRes,
Kill,
Link,
Listen,
@ -77,9 +72,6 @@ union Any {
Truncate,
HomeDir,
HomeDirRes,
ExecPath,
ExecPathRes,
Utime,
WorkerGetMessage,
WorkerGetMessageRes,
WorkerPostMessage,
@ -286,21 +278,11 @@ table GlobalTimerRes { }
table GlobalTimerStop { }
table Exit {
code: int;
}
table Environ {}
table SetEnv {
key: string;
value: string;
}
table EnvironRes {
map: [KeyValue];
}
table KeyValue {
key: string;
value: string;
@ -469,18 +451,6 @@ table HomeDirRes {
path: string;
}
table ExecPath {}
table ExecPathRes {
path: string;
}
table Utime {
filename: string;
atime: uint64;
mtime: uint64;
}
table Open {
filename: string;
perm: uint;
@ -600,14 +570,6 @@ table NowRes {
subsec_nanos: uint32;
}
table IsTTY {}
table IsTTYRes {
stdin: bool;
stdout: bool;
stderr: bool;
}
table Seek {
rid: uint32;
offset: int;

View file

@ -13,13 +13,11 @@ use super::files::{op_close, op_open, op_read, op_seek, op_write};
use super::fs::{
op_chdir, op_chmod, op_chown, op_copy_file, op_cwd, op_link,
op_make_temp_dir, op_mkdir, op_read_dir, op_read_link, op_remove, op_rename,
op_stat, op_symlink, op_truncate, op_utime,
op_stat, op_symlink, op_truncate,
};
use super::metrics::op_metrics;
use super::net::{op_accept, op_dial, op_listen, op_shutdown};
use super::os::{
op_env, op_exec_path, op_exit, op_home_dir, op_is_tty, op_set_env, op_start,
};
use super::os::{op_home_dir, op_set_env, op_start};
use super::performance::op_now;
use super::permissions::{op_permissions, op_revoke_permission};
use super::process::{op_kill, op_run, op_run_status};
@ -65,13 +63,8 @@ pub fn dispatch(
let op_result = op_func(state, &base, zero_copy);
let state = state.clone();
match op_result {
Ok(Op::Sync(buf)) => {
state.metrics_op_completed(buf.len());
Op::Sync(buf)
}
Ok(Op::Sync(buf)) => Op::Sync(buf),
Ok(Op::Async(fut)) => {
let result_fut = Box::new(
fut
@ -107,7 +100,6 @@ pub fn dispatch(
},
)
};
state.metrics_op_completed(buf.len());
Ok(buf)
})
.map_err(|err| panic!("unexpected error {:?}", err)),
@ -129,7 +121,6 @@ pub fn dispatch(
..Default::default()
},
);
state.metrics_op_completed(response_buf.len());
Op::Sync(response_buf)
}
}
@ -162,9 +153,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::CreateWorker => Some(op_create_worker),
msg::Any::Cwd => Some(op_cwd),
msg::Any::Dial => Some(op_dial),
msg::Any::Environ => Some(op_env),
msg::Any::ExecPath => Some(op_exec_path),
msg::Any::Exit => Some(op_exit),
msg::Any::Fetch => Some(op_fetch),
msg::Any::FetchSourceFile => Some(op_fetch_source_file),
msg::Any::FormatError => Some(op_format_error),
@ -174,7 +162,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::HostGetMessage => Some(op_host_get_message),
msg::Any::HostGetWorkerClosed => Some(op_host_get_worker_closed),
msg::Any::HostPostMessage => Some(op_host_post_message),
msg::Any::IsTTY => Some(op_is_tty),
msg::Any::Kill => Some(op_kill),
msg::Any::Link => Some(op_link),
msg::Any::Listen => Some(op_listen),
@ -203,7 +190,6 @@ pub fn op_selector_std(inner_type: msg::Any) -> Option<CliDispatchFn> {
msg::Any::Symlink => Some(op_symlink),
msg::Any::Truncate => Some(op_truncate),
msg::Any::HomeDir => Some(op_home_dir),
msg::Any::Utime => Some(op_utime),
msg::Any::Write => Some(op_write),
// TODO(ry) split these out so that only the appropriate Workers can access

113
cli/ops/dispatch_json.rs Normal file
View file

@ -0,0 +1,113 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::state::ThreadSafeState;
use crate::tokio_util;
use deno::*;
use futures::Future;
use futures::Poll;
pub use serde_derive::Deserialize;
use serde_json::json;
pub use serde_json::Value;
pub type AsyncJsonOp = Box<dyn Future<Item = Value, Error = ErrBox> + Send>;
pub enum JsonOp {
Sync(Value),
Async(AsyncJsonOp),
}
fn json_err(err: ErrBox) -> Value {
use crate::deno_error::GetErrorKind;
json!({
"message": err.to_string(),
"kind": err.kind() as u32,
})
}
pub type Dispatcher = fn(
state: &ThreadSafeState,
args: Value,
zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox>;
fn serialize_result(
promise_id: Option<u64>,
result: Result<Value, ErrBox>,
) -> Buf {
let value = match result {
Ok(v) => json!({ "ok": v, "promiseId": promise_id }),
Err(err) => json!({ "err": json_err(err), "promiseId": promise_id }),
};
let vec = serde_json::to_vec(&value).unwrap();
vec.into_boxed_slice()
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct AsyncArgs {
promise_id: Option<u64>,
}
pub fn dispatch(
d: Dispatcher,
state: &ThreadSafeState,
control: &[u8],
zero_copy: Option<PinnedBuf>,
) -> CoreOp {
let async_args: AsyncArgs = serde_json::from_slice(control).unwrap();
let promise_id = async_args.promise_id;
let is_sync = promise_id.is_none();
let result = serde_json::from_slice(control)
.map_err(ErrBox::from)
.and_then(move |args| d(state, args, zero_copy));
match result {
Ok(JsonOp::Sync(sync_value)) => {
assert!(promise_id.is_none());
CoreOp::Sync(serialize_result(promise_id, Ok(sync_value)))
}
Ok(JsonOp::Async(fut)) => {
assert!(promise_id.is_some());
let fut2 = Box::new(fut.then(move |result| -> Result<Buf, ()> {
Ok(serialize_result(promise_id, result))
}));
CoreOp::Async(fut2)
}
Err(sync_err) => {
let buf = serialize_result(promise_id, Err(sync_err));
if is_sync {
CoreOp::Sync(buf)
} else {
CoreOp::Async(Box::new(futures::future::ok(buf)))
}
}
}
}
// This is just type conversion. Implement From trait?
// See https://github.com/tokio-rs/tokio/blob/ffd73a64e7ec497622b7f939e38017afe7124dc4/tokio-fs/src/lib.rs#L76-L85
fn convert_blocking_json<F>(f: F) -> Poll<Value, ErrBox>
where
F: FnOnce() -> Result<Value, ErrBox>,
{
use futures::Async::*;
match tokio_threadpool::blocking(f) {
Ok(Ready(Ok(v))) => Ok(Ready(v)),
Ok(Ready(Err(err))) => Err(err),
Ok(NotReady) => Ok(NotReady),
Err(err) => panic!("blocking error {}", err),
}
}
pub fn blocking_json<F>(is_sync: bool, f: F) -> Result<JsonOp, ErrBox>
where
F: 'static + Send + FnOnce() -> Result<Value, ErrBox>,
{
if is_sync {
Ok(JsonOp::Sync(f()?))
} else {
Ok(JsonOp::Async(Box::new(futures::sync::oneshot::spawn(
tokio_util::poll_fn(move || convert_blocking_json(f)),
&tokio_executor::DefaultExecutor::current(),
))))
}
}

View file

@ -74,19 +74,15 @@ fn test_parse_min_record() {
pub fn dispatch(
d: Dispatcher,
state: &ThreadSafeState,
_state: &ThreadSafeState,
control: &[u8],
zero_copy: Option<PinnedBuf>,
) -> CoreOp {
let mut record = parse_min_record(control).unwrap();
let is_sync = record.promise_id == 0;
// TODO(ry) Currently there aren't any sync minimal ops. This is just a sanity
// check. Remove later.
assert!(!is_sync);
let state = state.clone();
let rid = record.arg;
let min_op = d(rid, zero_copy);
@ -102,10 +98,9 @@ pub fn dispatch(
record.result = -1;
}
}
let buf: Buf = record.into();
state.metrics_op_completed(buf.len());
Ok(buf)
Ok(record.into())
}));
if is_sync {
Op::Sync(fut.wait().unwrap())
} else {

View file

@ -1,5 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use super::dispatch_flatbuffers::serialize_response;
use super::dispatch_json::{blocking_json, Deserialize, JsonOp, Value};
use super::utils::*;
use crate::deno_error::DenoError;
use crate::deno_error::ErrorKind;
@ -13,7 +14,6 @@ use std::convert::From;
use std::fs;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
use utime;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
@ -456,24 +456,27 @@ pub fn op_make_temp_dir(
})
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Utime {
promise_id: Option<u64>,
filename: String,
atime: u64,
mtime: u64,
}
pub fn op_utime(
state: &ThreadSafeState,
base: &msg::Base<'_>,
data: Option<PinnedBuf>,
) -> CliOpResult {
assert!(data.is_none());
let inner = base.inner_as_utime().unwrap();
let filename = String::from(inner.filename().unwrap());
let atime = inner.atime();
let mtime = inner.mtime();
state.check_write(&filename)?;
blocking(base.sync(), move || {
debug!("op_utimes {} {} {}", filename, atime, mtime);
utime::set_file_times(filename, atime, mtime)?;
Ok(empty_buf())
args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
let args: Utime = serde_json::from_value(args)?;
state.check_write(&args.filename)?;
let is_sync = args.promise_id.is_none();
blocking_json(is_sync, move || {
debug!("op_utimes {} {} {}", args.filename, args.atime, args.mtime);
utime::set_file_times(args.filename, args.atime, args.mtime)?;
Ok(json!({}))
})
}

View file

@ -4,6 +4,7 @@ use deno::*;
mod compiler;
mod dispatch_flatbuffers;
mod dispatch_json;
mod dispatch_minimal;
mod errors;
mod fetch;
@ -23,9 +24,16 @@ mod timers;
mod utils;
mod workers;
// Warning! These values are duplicated in the TypeScript code (js/dispatch.ts),
// update with care.
pub const OP_FLATBUFFER: OpId = 44;
pub const OP_READ: OpId = 1;
pub const OP_WRITE: OpId = 2;
pub const OP_EXIT: OpId = 3;
pub const OP_IS_TTY: OpId = 4;
pub const OP_ENV: OpId = 5;
pub const OP_EXEC_PATH: OpId = 6;
pub const OP_UTIME: OpId = 7;
pub fn dispatch(
state: &ThreadSafeState,
@ -43,10 +51,36 @@ pub fn dispatch(
OP_WRITE => {
dispatch_minimal::dispatch(io::op_write, state, control, zero_copy)
}
OP_EXIT => dispatch_json::dispatch(os::op_exit, state, control, zero_copy),
OP_IS_TTY => {
dispatch_json::dispatch(os::op_is_tty, state, control, zero_copy)
}
OP_ENV => dispatch_json::dispatch(os::op_env, state, control, zero_copy),
OP_EXEC_PATH => {
dispatch_json::dispatch(os::op_exec_path, state, control, zero_copy)
}
OP_UTIME => {
dispatch_json::dispatch(fs::op_utime, state, control, zero_copy)
}
OP_FLATBUFFER => dispatch_flatbuffers::dispatch(state, control, zero_copy),
_ => panic!("bad op_id"),
};
state.metrics_op_dispatched(bytes_sent_control, bytes_sent_zero_copy);
op
match op {
Op::Sync(buf) => {
state.metrics_op_completed(buf.len());
Op::Sync(buf)
}
Op::Async(fut) => {
use crate::futures::Future;
let state = state.clone();
let result_fut = Box::new(fut.map(move |buf: Buf| {
state.clone().metrics_op_completed(buf.len());
buf
}));
Op::Async(result_fut)
}
}
}

View file

@ -1,16 +1,18 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use super::dispatch_flatbuffers::serialize_response;
use super::dispatch_json::{Deserialize, JsonOp, Value};
use super::utils::*;
use crate::ansi;
use crate::fs as deno_fs;
use crate::msg;
use crate::msg_util;
use crate::state::ThreadSafeState;
use crate::version;
use atty;
use deno::*;
use flatbuffers::FlatBufferBuilder;
use log;
use std::collections::HashMap;
use std::env;
use url::Url;
pub fn op_start(
@ -25,7 +27,7 @@ pub fn op_start(
let argv = state.argv.iter().map(String::as_str).collect::<Vec<_>>();
let argv_off = builder.create_vector_of_strings(argv.as_slice());
let cwd_path = std::env::current_dir().unwrap();
let cwd_path = env::current_dir().unwrap();
let cwd_off =
builder.create_string(deno_fs::normalize_path(cwd_path.as_ref()).as_ref());
@ -110,32 +112,16 @@ pub fn op_home_dir(
pub fn op_exec_path(
state: &ThreadSafeState,
base: &msg::Base<'_>,
data: Option<PinnedBuf>,
) -> CliOpResult {
assert!(data.is_none());
let cmd_id = base.cmd_id();
_args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
state.check_env()?;
let builder = &mut FlatBufferBuilder::new();
let current_exe = std::env::current_exe().unwrap();
// Now apply URL parser to current exe to get fully resolved path, otherwise we might get
// `./` and `../` bits in `exec_path`
let current_exe = env::current_exe().unwrap();
// Now apply URL parser to current exe to get fully resolved path, otherwise
// we might get `./` and `../` bits in `exec_path`
let exe_url = Url::from_file_path(current_exe).unwrap();
let path = exe_url.to_file_path().unwrap().to_str().unwrap().to_owned();
let path = Some(builder.create_string(&path));
let inner = msg::ExecPathRes::create(builder, &msg::ExecPathResArgs { path });
ok_buf(serialize_response(
cmd_id,
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::ExecPathRes,
..Default::default()
},
))
let path = exe_url.to_file_path().unwrap();
Ok(JsonOp::Sync(json!(path)))
}
pub fn op_set_env(
@ -148,71 +134,42 @@ pub fn op_set_env(
let key = inner.key().unwrap();
let value = inner.value().unwrap();
state.check_env()?;
std::env::set_var(key, value);
env::set_var(key, value);
ok_buf(empty_buf())
}
pub fn op_env(
state: &ThreadSafeState,
base: &msg::Base<'_>,
data: Option<PinnedBuf>,
) -> CliOpResult {
assert!(data.is_none());
let cmd_id = base.cmd_id();
_args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
state.check_env()?;
let v = env::vars().collect::<HashMap<String, String>>();
Ok(JsonOp::Sync(json!(v)))
}
let builder = &mut FlatBufferBuilder::new();
let vars: Vec<_> = std::env::vars()
.map(|(key, value)| msg_util::serialize_key_value(builder, &key, &value))
.collect();
let tables = builder.create_vector(&vars);
let inner = msg::EnvironRes::create(
builder,
&msg::EnvironResArgs { map: Some(tables) },
);
let response_buf = serialize_response(
cmd_id,
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::EnvironRes,
..Default::default()
},
);
ok_buf(response_buf)
#[derive(Deserialize)]
struct Exit {
code: i32,
}
pub fn op_exit(
_state: &ThreadSafeState,
base: &msg::Base<'_>,
_data: Option<PinnedBuf>,
) -> CliOpResult {
let inner = base.inner_as_exit().unwrap();
std::process::exit(inner.code())
_s: &ThreadSafeState,
args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
let args: Exit = serde_json::from_value(args)?;
std::process::exit(args.code)
}
pub fn op_is_tty(
_state: &ThreadSafeState,
base: &msg::Base<'_>,
_data: Option<PinnedBuf>,
) -> CliOpResult {
let builder = &mut FlatBufferBuilder::new();
let inner = msg::IsTTYRes::create(
builder,
&msg::IsTTYResArgs {
stdin: atty::is(atty::Stream::Stdin),
stdout: atty::is(atty::Stream::Stdout),
stderr: atty::is(atty::Stream::Stderr),
},
);
ok_buf(serialize_response(
base.cmd_id(),
builder,
msg::BaseArgs {
inner: Some(inner.as_union_value()),
inner_type: msg::Any::IsTTYRes,
..Default::default()
},
))
_s: &ThreadSafeState,
_args: Value,
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
Ok(JsonOp::Sync(json!({
"stdin": atty::is(atty::Stream::Stdin),
"stdout": atty::is(atty::Stream::Stdout),
"stderr": atty::is(atty::Stream::Stderr),
})))
}

View file

@ -19,7 +19,7 @@ pub fn empty_buf() -> Buf {
// This is just type conversion. Implement From trait?
// See https://github.com/tokio-rs/tokio/blob/ffd73a64e7ec497622b7f939e38017afe7124dc4/tokio-fs/src/lib.rs#L76-L85
fn convert_blocking<F>(f: F) -> Poll<Buf, ErrBox>
pub fn convert_blocking<F>(f: F) -> Poll<Buf, ErrBox>
where
F: FnOnce() -> Result<Buf, ErrBox>,
{

View file

@ -1,20 +1,29 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as minimal from "./dispatch_minimal";
import * as flatbuffers from "./dispatch_flatbuffers";
import * as json from "./dispatch_json";
// These consts are shared with Rust. Update with care.
export const OP_FLATBUFFER = 44;
export const OP_READ = 1;
export const OP_WRITE = 2;
export const OP_EXIT = 3;
export const OP_IS_TTY = 4;
export const OP_ENV = 5;
export const OP_EXEC_PATH = 6;
export const OP_UTIME = 7;
export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
switch (opId) {
case OP_FLATBUFFER:
flatbuffers.handleAsyncMsgFromRust(opId, ui8);
flatbuffers.asyncMsgFromRust(opId, ui8);
break;
case OP_WRITE:
case OP_READ:
minimal.handleAsyncMsgFromRust(opId, ui8);
minimal.asyncMsgFromRust(opId, ui8);
break;
case OP_UTIME:
json.asyncMsgFromRust(opId, ui8);
break;
default:
throw Error("bad opId");

View file

@ -19,7 +19,7 @@ interface FlatbufferRecord {
base: msg.Base;
}
export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
let { promiseId, base } = flatbufferRecordFromBuf(ui8);
const promise = promiseTable.get(promiseId);
util.assert(promise != null, `Expecting promise in table. ${promiseId}`);

94
js/dispatch_json.ts Normal file
View file

@ -0,0 +1,94 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
// Do not add flatbuffer dependencies to this module.
// TODO(ry) Currently ErrorKind enum is defined in FlatBuffers. Therefore
// we must still reference the msg_generated.ts. This should be removed!
import { ErrorKind } from "gen/cli/msg_generated";
import * as util from "./util";
import { TextEncoder, TextDecoder } from "./text_encoding";
import { core } from "./core";
import { DenoError } from "./errors";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Ok = any;
interface JsonError {
kind: ErrorKind;
message: string;
}
interface JsonResponse {
ok?: Ok;
err?: JsonError;
promiseId?: number; // only present in async mesasges.
}
const promiseTable = new Map<number, util.Resolvable<number>>();
let _nextPromiseId = 1;
function nextPromiseId(): number {
return _nextPromiseId++;
}
function decode(ui8: Uint8Array): JsonResponse {
const s = new TextDecoder().decode(ui8);
return JSON.parse(s) as JsonResponse;
}
function encode(args: object): Uint8Array {
const s = JSON.stringify(args);
return new TextEncoder().encode(s);
}
function toDenoError(err: JsonError): DenoError<ErrorKind> {
return new DenoError(err.kind, err.message);
}
export function asyncMsgFromRust(opId: number, res: Uint8Array): void {
const { ok, err, promiseId } = decode(res);
const promise = promiseTable.get(promiseId!)!;
if (!promise) {
throw Error(`Async op ${opId} had bad promiseId`);
}
promiseTable.delete(promiseId!);
if (err) {
promise.reject(toDenoError(err));
} else if (ok) {
promise.resolve(ok);
} else {
util.unreachable();
}
}
export function sendSync(
opId: number,
args: object = {},
zeroCopy?: Uint8Array
): Ok {
const argsUi8 = encode(args);
const res = core.dispatch(opId, argsUi8, zeroCopy);
if (!res) {
return;
}
const { ok, err, promiseId } = decode(res);
util.assert(!promiseId);
if (err) {
throw toDenoError(err);
}
return ok;
}
export function sendAsync(
opId: number,
args: object = {},
zeroCopy?: Uint8Array
): Promise<Ok> {
const promiseId = nextPromiseId();
args = Object.assign(args, { promiseId });
const argsUi8 = encode(args);
const promise = util.createResolvable<Ok>();
promiseTable.set(promiseId, promise);
const r = core.dispatch(opId, argsUi8, zeroCopy);
util.assert(!r);
return promise;
}

View file

@ -40,7 +40,7 @@ const scratchBytes = new Uint8Array(
);
util.assert(scratchBytes.byteLength === scratch32.length * 4);
export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void {
export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
const buf32 = new Int32Array(ui8.buffer, ui8.byteOffset, ui8.byteLength / 4);
const record = recordFromBufMinimal(opId, buf32);
const { promiseId, result } = record;

View file

@ -1,7 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { core } from "./core";
import { handleAsyncMsgFromRust } from "./dispatch";
import * as dispatch from "./dispatch";
import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers";
import * as dispatchJson from "./dispatch_json";
import { assert } from "./util";
import * as util from "./util";
import { window } from "./window";
@ -23,21 +24,12 @@ function setGlobals(pid_: number, noColor_: boolean): void {
* console.log(Deno.isTTY().stdout);
*/
export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } {
const builder = flatbuffers.createBuilder();
const inner = msg.IsTTY.createIsTTY(builder);
const baseRes = sendSync(builder, msg.Any.IsTTY, inner)!;
assert(msg.Any.IsTTYRes === baseRes.innerType());
const res = new msg.IsTTYRes();
assert(baseRes.inner(res) != null);
return { stdin: res.stdin(), stdout: res.stdout(), stderr: res.stderr() };
return dispatchJson.sendSync(dispatch.OP_IS_TTY);
}
/** Exit the Deno process with optional exit code. */
export function exit(exitCode = 0): never {
const builder = flatbuffers.createBuilder();
const inner = msg.Exit.createExit(builder, exitCode);
sendSync(builder, msg.Any.Exit, inner);
export function exit(code = 0): never {
dispatchJson.sendSync(dispatch.OP_EXIT, { code });
return util.unreachable();
}
@ -49,22 +41,6 @@ function setEnv(key: string, value: string): void {
sendSync(builder, msg.Any.SetEnv, inner);
}
function createEnv(inner: msg.EnvironRes): { [index: string]: string } {
const env: { [index: string]: string } = {};
for (let i = 0; i < inner.mapLength(); i++) {
const item = inner.map(i)!;
env[item.key()!] = item.value()!;
}
return new Proxy(env, {
set(obj, prop: string, value: string): boolean {
setEnv(prop, value);
return Reflect.set(obj, prop, value);
}
});
}
/** Returns a snapshot of the environment variables at invocation. Mutating a
* property in the object will set that variable in the environment for
* the process. The environment object will only accept `string`s
@ -77,19 +53,13 @@ function createEnv(inner: msg.EnvironRes): { [index: string]: string } {
* console.log(myEnv.TEST_VAR == newEnv.TEST_VAR);
*/
export function env(): { [index: string]: string } {
/* Ideally we could write
const res = sendSync({
command: msg.Command.ENV,
const env = dispatchJson.sendSync(dispatch.OP_ENV);
return new Proxy(env, {
set(obj, prop: string, value: string): boolean {
setEnv(prop, value);
return Reflect.set(obj, prop, value);
}
});
*/
const builder = flatbuffers.createBuilder();
const inner = msg.Environ.createEnviron(builder);
const baseRes = sendSync(builder, msg.Any.Environ, inner)!;
assert(msg.Any.EnvironRes === baseRes.innerType());
const res = new msg.EnvironRes();
assert(baseRes.inner(res) != null);
// TypeScript cannot track assertion above, therefore not null assertion
return createEnv(res);
}
/** Send to the privileged side that we have setup and are ready. */
@ -111,7 +81,7 @@ export function start(
preserveDenoNamespace = true,
source?: string
): msg.StartRes {
core.setAsyncHandler(handleAsyncMsgFromRust);
core.setAsyncHandler(dispatch.asyncMsgFromRust);
// First we send an empty `Start` message to let the privileged side know we
// are ready. The response should be a `StartRes` message containing the CLI
@ -163,12 +133,5 @@ export function homeDir(): string {
* Requires the `--allow-env` flag.
*/
export function execPath(): string {
const builder = flatbuffers.createBuilder();
const inner = msg.ExecPath.createExecPath(builder);
const baseRes = sendSync(builder, msg.Any.ExecPath, inner)!;
assert(msg.Any.ExecPathRes === baseRes.innerType());
const res = new msg.ExecPathRes();
assert(baseRes.inner(res) != null);
const path = res.path()!;
return path;
return dispatchJson.sendSync(dispatch.OP_EXEC_PATH);
}

View file

@ -1,24 +1,9 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers";
import * as util from "./util";
import { sendSync, sendAsync } from "./dispatch_json";
import { OP_UTIME } from "./dispatch";
function req(
filename: string,
atime: number | Date,
mtime: number | Date
): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
const atimeSec = atime instanceof Date ? Math.floor(+atime / 1000) : atime;
const mtimeSec = mtime instanceof Date ? Math.floor(+mtime / 1000) : mtime;
const builder = flatbuffers.createBuilder();
const filename_ = builder.createString(filename);
const atimeParts = util.splitNumberToParts(atimeSec);
const atimeMS_ = builder.createLong(atimeParts[0], atimeParts[1]);
const mtimeParts = util.splitNumberToParts(mtimeSec);
const mtimeMS_ = builder.createLong(mtimeParts[0], mtimeParts[1]);
const inner = msg.Utime.createUtime(builder, filename_, atimeMS_, mtimeMS_);
return [builder, msg.Any.Utime, inner];
function toSecondsFromEpoch(v: number | Date): number {
return v instanceof Date ? v.valueOf() / 1000 : v;
}
/** Synchronously changes the access and modification times of a file system
@ -32,7 +17,12 @@ export function utimeSync(
atime: number | Date,
mtime: number | Date
): void {
sendSync(...req(filename, atime, mtime));
sendSync(OP_UTIME, {
filename,
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
atime: toSecondsFromEpoch(atime),
mtime: toSecondsFromEpoch(mtime)
});
}
/** Changes the access and modification times of a file system object
@ -46,5 +36,10 @@ export async function utime(
atime: number | Date,
mtime: number | Date
): Promise<void> {
await sendAsync(...req(filename, atime, mtime));
await sendAsync(OP_UTIME, {
filename,
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
atime: toSecondsFromEpoch(atime),
mtime: toSecondsFromEpoch(mtime)
});
}