diff options
author | Andy Finch <andyfinch7@gmail.com> | 2019-12-05 15:30:20 -0500 |
---|---|---|
committer | Ry Dahl <ry@tinyclouds.org> | 2019-12-05 15:30:20 -0500 |
commit | 7c3b9b4f4f2f4ec8fdeb0e77bb853fd22ffaa476 (patch) | |
tree | aeafe5cc2560c5366704d7a580a5b0e0dced504d /cli/js | |
parent | 214b3eb29aa9cce8a55a247b4bd816cbd19bfe6b (diff) |
feat: first pass at native plugins (#3372)
Diffstat (limited to 'cli/js')
-rw-r--r-- | cli/js/deno.ts | 1 | ||||
-rw-r--r-- | cli/js/dispatch.ts | 18 | ||||
-rw-r--r-- | cli/js/lib.deno_runtime.d.ts | 35 | ||||
-rw-r--r-- | cli/js/permissions.ts | 5 | ||||
-rw-r--r-- | cli/js/permissions_test.ts | 14 | ||||
-rw-r--r-- | cli/js/plugins.ts | 66 | ||||
-rw-r--r-- | cli/js/test_util.ts | 14 |
7 files changed, 148 insertions, 5 deletions
diff --git a/cli/js/deno.ts b/cli/js/deno.ts index 6f07bef67..27a7bb3bd 100644 --- a/cli/js/deno.ts +++ b/cli/js/deno.ts @@ -76,6 +76,7 @@ export { } from "./permissions.ts"; export { truncateSync, truncate } from "./truncate.ts"; export { FileInfo } from "./file_info.ts"; +export { openPlugin } from "./plugins.ts"; export { connect, dial, listen, Listener, Conn } from "./net.ts"; export { dialTLS, listenTLS } from "./tls.ts"; export { metrics, Metrics } from "./metrics.ts"; diff --git a/cli/js/dispatch.ts b/cli/js/dispatch.ts index 35806c3ad..ed6f57052 100644 --- a/cli/js/dispatch.ts +++ b/cli/js/dispatch.ts @@ -1,6 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import * as minimal from "./dispatch_minimal.ts"; import * as json from "./dispatch_json.ts"; +import { AsyncHandler } from "./plugins.ts"; // These consts are shared with Rust. Update with care. export let OP_READ: number; @@ -67,6 +68,16 @@ export let OP_CWD: number; export let OP_FETCH_ASSET: number; export let OP_DIAL_TLS: number; export let OP_HOSTNAME: number; +export let OP_OPEN_PLUGIN: number; + +const PLUGIN_ASYNC_HANDLER_MAP: Map<number, AsyncHandler> = new Map(); + +export function setPluginAsyncHandler( + opId: number, + handler: AsyncHandler +): void { + PLUGIN_ASYNC_HANDLER_MAP.set(opId, handler); +} export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { switch (opId) { @@ -111,6 +122,11 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { json.asyncMsgFromRust(opId, ui8); break; default: - throw Error("bad async opId"); + const handler = PLUGIN_ASYNC_HANDLER_MAP.get(opId); + if (handler) { + handler(ui8); + } else { + throw Error("bad async opId"); + } } } diff --git a/cli/js/lib.deno_runtime.d.ts b/cli/js/lib.deno_runtime.d.ts index fb7767aa6..0d3463869 100644 --- a/cli/js/lib.deno_runtime.d.ts +++ b/cli/js/lib.deno_runtime.d.ts @@ -907,6 +907,7 @@ declare namespace Deno { | "write" | "net" | "env" + | "plugin" | "hrtime"; /** https://w3c.github.io/permissions/#status-of-a-permission */ export type PermissionState = "granted" | "denied" | "prompt"; @@ -924,6 +925,9 @@ declare namespace Deno { interface EnvPermissionDescriptor { name: "env"; } + interface PluginPermissionDescriptor { + name: "plugin"; + } interface HrtimePermissionDescriptor { name: "hrtime"; } @@ -933,6 +937,7 @@ declare namespace Deno { | ReadWritePermissionDescriptor | NetPermissionDescriptor | EnvPermissionDescriptor + | PluginPermissionDescriptor | HrtimePermissionDescriptor; export class Permissions { @@ -982,6 +987,36 @@ declare namespace Deno { */ export function truncate(name: string, len?: number): Promise<void>; + // @url js/plugins.d.ts + + export interface AsyncHandler { + (msg: Uint8Array): void; + } + + export interface PluginOp { + dispatch( + control: Uint8Array, + zeroCopy?: ArrayBufferView | null + ): Uint8Array | null; + setAsyncHandler(handler: AsyncHandler): void; + } + + export interface Plugin { + ops: { + [name: string]: PluginOp; + }; + } + + /** Open and initalize a plugin. + * Requires the `--allow-plugin` flag. + * + * const plugin = Deno.openPlugin("./path/to/some/plugin.so"); + * const some_op = plugin.ops.some_op; + * const response = some_op.dispatch(new Uint8Array([1,2,3,4])); + * console.log(`Response from plugin ${response}`); + */ + export function openPlugin(filename: string): Plugin; + // @url js/net.d.ts type Transport = "tcp"; diff --git a/cli/js/permissions.ts b/cli/js/permissions.ts index c3530e970..e0fb8a84c 100644 --- a/cli/js/permissions.ts +++ b/cli/js/permissions.ts @@ -11,6 +11,7 @@ export type PermissionName = | "net" | "env" | "run" + | "plugin" | "hrtime"; // NOTE: Keep in sync with cli/permissions.rs @@ -31,6 +32,9 @@ interface NetPermissionDescriptor { interface EnvPermissionDescriptor { name: "env"; } +interface PluginPermissionDescriptor { + name: "plugin"; +} interface HrtimePermissionDescriptor { name: "hrtime"; } @@ -40,6 +44,7 @@ type PermissionDescriptor = | ReadWritePermissionDescriptor | NetPermissionDescriptor | EnvPermissionDescriptor + | PluginPermissionDescriptor | HrtimePermissionDescriptor; /** https://w3c.github.io/permissions/#permissionstatus */ diff --git a/cli/js/permissions_test.ts b/cli/js/permissions_test.ts index d9ba538f0..a50718652 100644 --- a/cli/js/permissions_test.ts +++ b/cli/js/permissions_test.ts @@ -7,11 +7,12 @@ const knownPermissions: Deno.PermissionName[] = [ "write", "net", "env", + "plugin", "hrtime" ]; -for (const grant of knownPermissions) { - testPerm({ [grant]: true }, async function envGranted(): Promise<void> { +function genFunc(grant: Deno.PermissionName): () => Promise<void> { + const gen: () => Promise<void> = async function Granted(): Promise<void> { const status0 = await Deno.permissions.query({ name: grant }); assert(status0 != null); assertEquals(status0.state, "granted"); @@ -19,7 +20,14 @@ for (const grant of knownPermissions) { const status1 = await Deno.permissions.revoke({ name: grant }); assert(status1 != null); assertEquals(status1.state, "prompt"); - }); + }; + // Properly name these generated functions. + Object.defineProperty(gen, "name", { value: grant + "Granted" }); + return gen; +} + +for (const grant of knownPermissions) { + testPerm({ [grant]: true }, genFunc(grant)); } test(async function permissionInvalidName(): Promise<void> { diff --git a/cli/js/plugins.ts b/cli/js/plugins.ts new file mode 100644 index 000000000..324ae3408 --- /dev/null +++ b/cli/js/plugins.ts @@ -0,0 +1,66 @@ +import { sendSync } from "./dispatch_json.ts"; +import { OP_OPEN_PLUGIN, setPluginAsyncHandler } from "./dispatch.ts"; +import { core } from "./core.ts"; + +export interface AsyncHandler { + (msg: Uint8Array): void; +} + +interface PluginOp { + dispatch( + control: Uint8Array, + zeroCopy?: ArrayBufferView | null + ): Uint8Array | null; + setAsyncHandler(handler: AsyncHandler): void; +} + +class PluginOpImpl implements PluginOp { + constructor(private readonly opId: number) {} + + dispatch( + control: Uint8Array, + zeroCopy?: ArrayBufferView | null + ): Uint8Array | null { + return core.dispatch(this.opId, control, zeroCopy); + } + + setAsyncHandler(handler: AsyncHandler): void { + setPluginAsyncHandler(this.opId, handler); + } +} + +// TODO(afinch7): add close method. + +interface Plugin { + ops: { + [name: string]: PluginOp; + }; +} + +class PluginImpl implements Plugin { + private _ops: { [name: string]: PluginOp } = {}; + + constructor(private readonly rid: number, ops: { [name: string]: number }) { + for (const op in ops) { + this._ops[op] = new PluginOpImpl(ops[op]); + } + } + + get ops(): { [name: string]: PluginOp } { + return Object.assign({}, this._ops); + } +} + +interface OpenPluginResponse { + rid: number; + ops: { + [name: string]: number; + }; +} + +export function openPlugin(filename: string): Plugin { + const response: OpenPluginResponse = sendSync(OP_OPEN_PLUGIN, { + filename + }); + return new PluginImpl(response.rid, response.ops); +} diff --git a/cli/js/test_util.ts b/cli/js/test_util.ts index 85fffabe6..aac70d2ed 100644 --- a/cli/js/test_util.ts +++ b/cli/js/test_util.ts @@ -26,6 +26,7 @@ interface TestPermissions { net?: boolean; env?: boolean; run?: boolean; + plugin?: boolean; hrtime?: boolean; } @@ -35,6 +36,7 @@ export interface Permissions { net: boolean; env: boolean; run: boolean; + plugin: boolean; hrtime: boolean; } @@ -48,6 +50,7 @@ async function getProcessPermissions(): Promise<Permissions> { write: await isGranted("write"), net: await isGranted("net"), env: await isGranted("env"), + plugin: await isGranted("plugin"), hrtime: await isGranted("hrtime") }; } @@ -75,8 +78,9 @@ function permToString(perms: Permissions): string { const n = perms.net ? 1 : 0; const e = perms.env ? 1 : 0; const u = perms.run ? 1 : 0; + const p = perms.plugin ? 1 : 0; const h = perms.hrtime ? 1 : 0; - return `permR${r}W${w}N${n}E${e}U${u}H${h}`; + return `permR${r}W${w}N${n}E${e}U${u}P${p}H${h}`; } function registerPermCombination(perms: Permissions): void { @@ -93,6 +97,7 @@ function normalizeTestPermissions(perms: TestPermissions): Permissions { net: !!perms.net, run: !!perms.run, env: !!perms.env, + plugin: !!perms.plugin, hrtime: !!perms.hrtime }; } @@ -120,6 +125,7 @@ export function test(fn: testing.TestFunction): void { net: false, env: false, run: false, + plugin: false, hrtime: false }, fn @@ -176,6 +182,7 @@ test(function permissionsMatches(): void { net: false, env: false, run: false, + plugin: false, hrtime: false }, normalizeTestPermissions({ read: true }) @@ -190,6 +197,7 @@ test(function permissionsMatches(): void { net: false, env: false, run: false, + plugin: false, hrtime: false }, normalizeTestPermissions({}) @@ -204,6 +212,7 @@ test(function permissionsMatches(): void { net: true, env: true, run: true, + plugin: true, hrtime: true }, normalizeTestPermissions({ read: true }) @@ -219,6 +228,7 @@ test(function permissionsMatches(): void { net: true, env: false, run: false, + plugin: false, hrtime: false }, normalizeTestPermissions({ read: true }) @@ -234,6 +244,7 @@ test(function permissionsMatches(): void { net: true, env: true, run: true, + plugin: true, hrtime: true }, { @@ -242,6 +253,7 @@ test(function permissionsMatches(): void { net: true, env: true, run: true, + plugin: true, hrtime: true } ) |