diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/dispatch.ts | 15 | ||||
-rw-r--r-- | js/dispatch_flatbuffers.ts | 2 | ||||
-rw-r--r-- | js/dispatch_json.ts | 94 | ||||
-rw-r--r-- | js/dispatch_minimal.ts | 2 | ||||
-rw-r--r-- | js/os.ts | 63 | ||||
-rw-r--r-- | js/utime.ts | 37 |
6 files changed, 137 insertions, 76 deletions
diff --git a/js/dispatch.ts b/js/dispatch.ts index 423469d38..0c5c59553 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -1,20 +1,29 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import * as minimal from "./dispatch_minimal"; import * as flatbuffers from "./dispatch_flatbuffers"; +import * as json from "./dispatch_json"; // These consts are shared with Rust. Update with care. export const OP_FLATBUFFER = 44; export const OP_READ = 1; export const OP_WRITE = 2; +export const OP_EXIT = 3; +export const OP_IS_TTY = 4; +export const OP_ENV = 5; +export const OP_EXEC_PATH = 6; +export const OP_UTIME = 7; -export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void { +export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { switch (opId) { case OP_FLATBUFFER: - flatbuffers.handleAsyncMsgFromRust(opId, ui8); + flatbuffers.asyncMsgFromRust(opId, ui8); break; case OP_WRITE: case OP_READ: - minimal.handleAsyncMsgFromRust(opId, ui8); + minimal.asyncMsgFromRust(opId, ui8); + break; + case OP_UTIME: + json.asyncMsgFromRust(opId, ui8); break; default: throw Error("bad opId"); diff --git a/js/dispatch_flatbuffers.ts b/js/dispatch_flatbuffers.ts index 87a01037c..0e375dbdf 100644 --- a/js/dispatch_flatbuffers.ts +++ b/js/dispatch_flatbuffers.ts @@ -19,7 +19,7 @@ interface FlatbufferRecord { base: msg.Base; } -export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void { +export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { let { promiseId, base } = flatbufferRecordFromBuf(ui8); const promise = promiseTable.get(promiseId); util.assert(promise != null, `Expecting promise in table. ${promiseId}`); diff --git a/js/dispatch_json.ts b/js/dispatch_json.ts new file mode 100644 index 000000000..e8c976164 --- /dev/null +++ b/js/dispatch_json.ts @@ -0,0 +1,94 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +// Do not add flatbuffer dependencies to this module. +// TODO(ry) Currently ErrorKind enum is defined in FlatBuffers. Therefore +// we must still reference the msg_generated.ts. This should be removed! +import { ErrorKind } from "gen/cli/msg_generated"; +import * as util from "./util"; +import { TextEncoder, TextDecoder } from "./text_encoding"; +import { core } from "./core"; +import { DenoError } from "./errors"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Ok = any; + +interface JsonError { + kind: ErrorKind; + message: string; +} + +interface JsonResponse { + ok?: Ok; + err?: JsonError; + promiseId?: number; // only present in async mesasges. +} + +const promiseTable = new Map<number, util.Resolvable<number>>(); +let _nextPromiseId = 1; + +function nextPromiseId(): number { + return _nextPromiseId++; +} + +function decode(ui8: Uint8Array): JsonResponse { + const s = new TextDecoder().decode(ui8); + return JSON.parse(s) as JsonResponse; +} + +function encode(args: object): Uint8Array { + const s = JSON.stringify(args); + return new TextEncoder().encode(s); +} + +function toDenoError(err: JsonError): DenoError<ErrorKind> { + return new DenoError(err.kind, err.message); +} + +export function asyncMsgFromRust(opId: number, res: Uint8Array): void { + const { ok, err, promiseId } = decode(res); + const promise = promiseTable.get(promiseId!)!; + if (!promise) { + throw Error(`Async op ${opId} had bad promiseId`); + } + promiseTable.delete(promiseId!); + + if (err) { + promise.reject(toDenoError(err)); + } else if (ok) { + promise.resolve(ok); + } else { + util.unreachable(); + } +} + +export function sendSync( + opId: number, + args: object = {}, + zeroCopy?: Uint8Array +): Ok { + const argsUi8 = encode(args); + const res = core.dispatch(opId, argsUi8, zeroCopy); + if (!res) { + return; + } + const { ok, err, promiseId } = decode(res); + util.assert(!promiseId); + if (err) { + throw toDenoError(err); + } + return ok; +} + +export function sendAsync( + opId: number, + args: object = {}, + zeroCopy?: Uint8Array +): Promise<Ok> { + const promiseId = nextPromiseId(); + args = Object.assign(args, { promiseId }); + const argsUi8 = encode(args); + const promise = util.createResolvable<Ok>(); + promiseTable.set(promiseId, promise); + const r = core.dispatch(opId, argsUi8, zeroCopy); + util.assert(!r); + return promise; +} diff --git a/js/dispatch_minimal.ts b/js/dispatch_minimal.ts index fc3fc61b9..9a310fd22 100644 --- a/js/dispatch_minimal.ts +++ b/js/dispatch_minimal.ts @@ -40,7 +40,7 @@ const scratchBytes = new Uint8Array( ); util.assert(scratchBytes.byteLength === scratch32.length * 4); -export function handleAsyncMsgFromRust(opId: number, ui8: Uint8Array): void { +export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { const buf32 = new Int32Array(ui8.buffer, ui8.byteOffset, ui8.byteLength / 4); const record = recordFromBufMinimal(opId, buf32); const { promiseId, result } = record; @@ -1,7 +1,8 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { core } from "./core"; -import { handleAsyncMsgFromRust } from "./dispatch"; +import * as dispatch from "./dispatch"; import { sendSync, msg, flatbuffers } from "./dispatch_flatbuffers"; +import * as dispatchJson from "./dispatch_json"; import { assert } from "./util"; import * as util from "./util"; import { window } from "./window"; @@ -23,21 +24,12 @@ function setGlobals(pid_: number, noColor_: boolean): void { * console.log(Deno.isTTY().stdout); */ export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } { - const builder = flatbuffers.createBuilder(); - const inner = msg.IsTTY.createIsTTY(builder); - const baseRes = sendSync(builder, msg.Any.IsTTY, inner)!; - assert(msg.Any.IsTTYRes === baseRes.innerType()); - const res = new msg.IsTTYRes(); - assert(baseRes.inner(res) != null); - - return { stdin: res.stdin(), stdout: res.stdout(), stderr: res.stderr() }; + return dispatchJson.sendSync(dispatch.OP_IS_TTY); } /** Exit the Deno process with optional exit code. */ -export function exit(exitCode = 0): never { - const builder = flatbuffers.createBuilder(); - const inner = msg.Exit.createExit(builder, exitCode); - sendSync(builder, msg.Any.Exit, inner); +export function exit(code = 0): never { + dispatchJson.sendSync(dispatch.OP_EXIT, { code }); return util.unreachable(); } @@ -49,22 +41,6 @@ function setEnv(key: string, value: string): void { sendSync(builder, msg.Any.SetEnv, inner); } -function createEnv(inner: msg.EnvironRes): { [index: string]: string } { - const env: { [index: string]: string } = {}; - - for (let i = 0; i < inner.mapLength(); i++) { - const item = inner.map(i)!; - env[item.key()!] = item.value()!; - } - - return new Proxy(env, { - set(obj, prop: string, value: string): boolean { - setEnv(prop, value); - return Reflect.set(obj, prop, value); - } - }); -} - /** Returns a snapshot of the environment variables at invocation. Mutating a * property in the object will set that variable in the environment for * the process. The environment object will only accept `string`s @@ -77,19 +53,13 @@ function createEnv(inner: msg.EnvironRes): { [index: string]: string } { * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); */ export function env(): { [index: string]: string } { - /* Ideally we could write - const res = sendSync({ - command: msg.Command.ENV, + const env = dispatchJson.sendSync(dispatch.OP_ENV); + return new Proxy(env, { + set(obj, prop: string, value: string): boolean { + setEnv(prop, value); + return Reflect.set(obj, prop, value); + } }); - */ - const builder = flatbuffers.createBuilder(); - const inner = msg.Environ.createEnviron(builder); - const baseRes = sendSync(builder, msg.Any.Environ, inner)!; - assert(msg.Any.EnvironRes === baseRes.innerType()); - const res = new msg.EnvironRes(); - assert(baseRes.inner(res) != null); - // TypeScript cannot track assertion above, therefore not null assertion - return createEnv(res); } /** Send to the privileged side that we have setup and are ready. */ @@ -111,7 +81,7 @@ export function start( preserveDenoNamespace = true, source?: string ): msg.StartRes { - core.setAsyncHandler(handleAsyncMsgFromRust); + core.setAsyncHandler(dispatch.asyncMsgFromRust); // First we send an empty `Start` message to let the privileged side know we // are ready. The response should be a `StartRes` message containing the CLI @@ -163,12 +133,5 @@ export function homeDir(): string { * Requires the `--allow-env` flag. */ export function execPath(): string { - const builder = flatbuffers.createBuilder(); - const inner = msg.ExecPath.createExecPath(builder); - const baseRes = sendSync(builder, msg.Any.ExecPath, inner)!; - assert(msg.Any.ExecPathRes === baseRes.innerType()); - const res = new msg.ExecPathRes(); - assert(baseRes.inner(res) != null); - const path = res.path()!; - return path; + return dispatchJson.sendSync(dispatch.OP_EXEC_PATH); } diff --git a/js/utime.ts b/js/utime.ts index 89914b4ca..c71710458 100644 --- a/js/utime.ts +++ b/js/utime.ts @@ -1,24 +1,9 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync, msg, flatbuffers } from "./dispatch_flatbuffers"; -import * as util from "./util"; +import { sendSync, sendAsync } from "./dispatch_json"; +import { OP_UTIME } from "./dispatch"; -function req( - filename: string, - atime: number | Date, - mtime: number | Date -): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] { - const atimeSec = atime instanceof Date ? Math.floor(+atime / 1000) : atime; - const mtimeSec = mtime instanceof Date ? Math.floor(+mtime / 1000) : mtime; - - const builder = flatbuffers.createBuilder(); - const filename_ = builder.createString(filename); - const atimeParts = util.splitNumberToParts(atimeSec); - const atimeMS_ = builder.createLong(atimeParts[0], atimeParts[1]); - const mtimeParts = util.splitNumberToParts(mtimeSec); - const mtimeMS_ = builder.createLong(mtimeParts[0], mtimeParts[1]); - - const inner = msg.Utime.createUtime(builder, filename_, atimeMS_, mtimeMS_); - return [builder, msg.Any.Utime, inner]; +function toSecondsFromEpoch(v: number | Date): number { + return v instanceof Date ? v.valueOf() / 1000 : v; } /** Synchronously changes the access and modification times of a file system @@ -32,7 +17,12 @@ export function utimeSync( atime: number | Date, mtime: number | Date ): void { - sendSync(...req(filename, atime, mtime)); + sendSync(OP_UTIME, { + filename, + // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple + atime: toSecondsFromEpoch(atime), + mtime: toSecondsFromEpoch(mtime) + }); } /** Changes the access and modification times of a file system object @@ -46,5 +36,10 @@ export async function utime( atime: number | Date, mtime: number | Date ): Promise<void> { - await sendAsync(...req(filename, atime, mtime)); + await sendAsync(OP_UTIME, { + filename, + // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple + atime: toSecondsFromEpoch(atime), + mtime: toSecondsFromEpoch(mtime) + }); } |