summaryrefslogtreecommitdiff
path: root/cli/js
diff options
context:
space:
mode:
authorAndy Finch <andyfinch7@gmail.com>2019-12-05 15:30:20 -0500
committerRy Dahl <ry@tinyclouds.org>2019-12-05 15:30:20 -0500
commit7c3b9b4f4f2f4ec8fdeb0e77bb853fd22ffaa476 (patch)
treeaeafe5cc2560c5366704d7a580a5b0e0dced504d /cli/js
parent214b3eb29aa9cce8a55a247b4bd816cbd19bfe6b (diff)
feat: first pass at native plugins (#3372)
Diffstat (limited to 'cli/js')
-rw-r--r--cli/js/deno.ts1
-rw-r--r--cli/js/dispatch.ts18
-rw-r--r--cli/js/lib.deno_runtime.d.ts35
-rw-r--r--cli/js/permissions.ts5
-rw-r--r--cli/js/permissions_test.ts14
-rw-r--r--cli/js/plugins.ts66
-rw-r--r--cli/js/test_util.ts14
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
}
)