Skip to content

Commit

Permalink
Provide option to delete Deno namespace in worker (denoland#2717)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkassimo authored and ry committed Aug 5, 2019
1 parent aaa7a3e commit ddee2df
Show file tree
Hide file tree
Showing 18 changed files with 128 additions and 17 deletions.
4 changes: 3 additions & 1 deletion cli/main.rs
Expand Up @@ -196,8 +196,10 @@ fn create_worker_and_state(
s.status(status, msg).expect("shell problem");
}
});
// TODO(kevinkassimo): maybe make include_deno_namespace also configurable?
let state =
ThreadSafeState::new(flags, argv, ops::op_selector_std, progress).unwrap();
ThreadSafeState::new(flags, argv, ops::op_selector_std, progress, true)
.unwrap();
let worker = Worker::new(
"main".to_string(),
startup_data::deno_isolate_init(),
Expand Down
1 change: 1 addition & 0 deletions cli/msg.fbs
Expand Up @@ -204,6 +204,7 @@ table FormatErrorRes {
// Create worker as host
table CreateWorker {
specifier: string;
include_deno_namespace: bool;
}

table CreateWorkerRes {
Expand Down
8 changes: 7 additions & 1 deletion cli/ops.rs
Expand Up @@ -2069,6 +2069,10 @@ fn op_create_worker(
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 =
inner.include_deno_namespace() && state.include_deno_namespace;

let parent_state = state.clone();

Expand All @@ -2077,13 +2081,15 @@ fn op_create_worker(
parent_state.argv.clone(),
op_selector_std,
parent_state.progress.clone(),
include_deno_namespace,
)?;
let rid = child_state.resource.rid;
let name = format!("USER-WORKER-{}", specifier);
let deno_main_call = format!("denoMain({})", include_deno_namespace);

let mut worker =
Worker::new(name, startup_data::deno_isolate_init(), child_state);
worker.execute("denoMain()").unwrap();
worker.execute(&deno_main_call).unwrap();
worker.execute("workerMain()").unwrap();

let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
Expand Down
5 changes: 5 additions & 0 deletions cli/state.rs
Expand Up @@ -84,6 +84,8 @@ pub struct State {
pub js_compiler: JsCompiler,
pub json_compiler: JsonCompiler,
pub ts_compiler: TsCompiler,

pub include_deno_namespace: bool,
}

impl Clone for ThreadSafeState {
Expand Down Expand Up @@ -151,6 +153,7 @@ impl ThreadSafeState {
argv_rest: Vec<String>,
dispatch_selector: ops::OpSelector,
progress: Progress,
include_deno_namespace: bool,
) -> Result<Self, ErrBox> {
let custom_root = env::var("DENO_DIR").map(String::into).ok();

Expand Down Expand Up @@ -223,6 +226,7 @@ impl ThreadSafeState {
ts_compiler,
js_compiler: JsCompiler {},
json_compiler: JsonCompiler {},
include_deno_namespace,
};

Ok(ThreadSafeState(Arc::new(state)))
Expand Down Expand Up @@ -302,6 +306,7 @@ impl ThreadSafeState {
argv,
ops::op_selector_std,
Progress::new(),
true,
)
.unwrap()
}
Expand Down
4 changes: 3 additions & 1 deletion cli/worker.rs
Expand Up @@ -126,6 +126,7 @@ mod tests {
argv,
op_selector_std,
Progress::new(),
true,
)
.unwrap();
let state_ = state.clone();
Expand Down Expand Up @@ -155,6 +156,7 @@ mod tests {
argv,
op_selector_std,
Progress::new(),
true,
)
.unwrap();
let state_ = state.clone();
Expand Down Expand Up @@ -182,7 +184,7 @@ mod tests {
let mut flags = flags::DenoFlags::default();
flags.reload = true;
let state =
ThreadSafeState::new(flags, argv, op_selector_std, Progress::new())
ThreadSafeState::new(flags, argv, op_selector_std, Progress::new(), true)
.unwrap();
let state_ = state.clone();
tokio_util::run(lazy(move || {
Expand Down
6 changes: 5 additions & 1 deletion core/shared_queue.js
Expand Up @@ -29,8 +29,12 @@ SharedQueue Binary Layout
const INDEX_RECORDS = 3 + MAX_RECORDS;
const HEAD_INIT = 4 * INDEX_RECORDS;

// Available on start due to bindings.
const Deno = window[GLOBAL_NAMESPACE];
const core = Deno[CORE_NAMESPACE];
// Warning: DO NOT use window.Deno after this point.
// It is possible that the Deno namespace has been deleted.
// Use the above local Deno and core variable instead.

let sharedBytes;
let shared32;
Expand Down Expand Up @@ -165,7 +169,7 @@ SharedQueue Binary Layout
const success = push(control);
// If successful, don't use first argument of core.send.
const arg0 = success ? null : control;
return window.Deno.core.send(arg0, zeroCopy);
return Deno.core.send(arg0, zeroCopy);
}

const denoCore = {
Expand Down
2 changes: 1 addition & 1 deletion js/compiler.ts
Expand Up @@ -25,7 +25,7 @@ const console = new Console(core.print);
window.console = console;
window.workerMain = workerMain;
export default function denoMain(): void {
os.start("TS");
os.start(true, "TS");
}

const ASSETS = "$asset$";
Expand Down
2 changes: 2 additions & 0 deletions js/core.ts
@@ -1,4 +1,6 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { window } from "./window";

// This allows us to access core in API even if we
// dispose window.Deno
export const core = window.Deno.core as DenoCore;
3 changes: 1 addition & 2 deletions js/globals.ts
Expand Up @@ -32,7 +32,6 @@ import * as request from "./request";
// These imports are not exposed and therefore are fine to just import the
// symbols required.
import { core } from "./core";
import { immutableDefine } from "./util";

// During the build process, augmentations to the variable `window` in this
// file are tracked and created as part of default library that is built into
Expand Down Expand Up @@ -71,7 +70,7 @@ window.window = window;
// This is the Deno namespace, it is handled differently from other window
// properties when building the runtime type library, as the whole module
// is flattened into a single namespace.
immutableDefine(window, "Deno", deno);
window.Deno = deno;
Object.freeze(window.Deno);

// Globally available functions and object instances.
Expand Down
7 changes: 5 additions & 2 deletions js/main.ts
Expand Up @@ -18,8 +18,11 @@ import { setLocation } from "./location";
// builtin modules
import * as deno from "./deno";

export default function denoMain(name?: string): void {
const startResMsg = os.start(name);
export default function denoMain(
preserveDenoNamespace: boolean = true,
name?: string
): void {
const startResMsg = os.start(preserveDenoNamespace, name);

setVersions(startResMsg.denoVersion()!, startResMsg.v8Version()!);

Expand Down
20 changes: 16 additions & 4 deletions js/os.ts
Expand Up @@ -112,7 +112,10 @@ function sendStart(): msg.StartRes {
// This function bootstraps an environment within Deno, it is shared both by
// the runtime and the compiler environments.
// @internal
export function start(source?: string): msg.StartRes {
export function start(
preserveDenoNamespace = true,
source?: string
): msg.StartRes {
core.setAsyncHandler(handleAsyncMsgFromRust);

// First we send an empty `Start` message to let the privileged side know we
Expand All @@ -124,9 +127,18 @@ export function start(source?: string): msg.StartRes {

setGlobals(startResMsg.pid(), startResMsg.noColor(), startResMsg.execPath()!);

// Deno.core could ONLY be safely frozen here (not in globals.ts)
// since shared_queue.js will modify core properties.
Object.freeze(window.Deno.core);
if (preserveDenoNamespace) {
util.immutableDefine(window, "Deno", window.Deno);
// Deno.core could ONLY be safely frozen here (not in globals.ts)
// since shared_queue.js will modify core properties.
Object.freeze(window.Deno.core);
// core.sharedQueue is an object so we should also freeze it.
Object.freeze(window.Deno.core.sharedQueue);
} else {
// Remove window.Deno
delete window.Deno;
assert(window.Deno === undefined);
}

return startResMsg;
}
Expand Down
31 changes: 27 additions & 4 deletions js/workers.ts
Expand Up @@ -20,10 +20,17 @@ export function decodeMessage(dataIntArray: Uint8Array): any {
return JSON.parse(dataJson);
}

function createWorker(specifier: string): number {
function createWorker(
specifier: string,
includeDenoNamespace: boolean
): number {
const builder = flatbuffers.createBuilder();
const specifier_ = builder.createString(specifier);
const inner = msg.CreateWorker.createCreateWorker(builder, specifier_);
const inner = msg.CreateWorker.createCreateWorker(
builder,
specifier_,
includeDenoNamespace
);
const baseRes = sendSync(builder, msg.Any.CreateWorker, inner);
assert(baseRes != null);
assert(
Expand Down Expand Up @@ -149,6 +156,18 @@ export interface Worker {
closed: Promise<void>;
}

// TODO(kevinkassimo): Maybe implement reasonable web worker options?
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface WorkerOptions {}

/** Extended Deno Worker initialization options.
* `noDenoNamespace` hides global `window.Deno` namespace for
* spawned worker and nested workers spawned by it (default: false).
*/
export interface DenoWorkerOptions extends WorkerOptions {
noDenoNamespace?: boolean;
}

export class WorkerImpl implements Worker {
private readonly rid: number;
private isClosing: boolean = false;
Expand All @@ -157,8 +176,12 @@ export class WorkerImpl implements Worker {
public onmessage?: (data: any) => void;
public onmessageerror?: () => void;

constructor(specifier: string) {
this.rid = createWorker(specifier);
constructor(specifier: string, options?: DenoWorkerOptions) {
let includeDenoNamespace = true;
if (options && options.noDenoNamespace) {
includeDenoNamespace = false;
}
this.rid = createWorker(specifier, includeDenoNamespace);
this.run();
this.isClosedPromise = hostGetWorkerClosed(this.rid);
this.isClosedPromise.then(
Expand Down
2 changes: 2 additions & 0 deletions tests/039_worker_deno_ns.test
@@ -0,0 +1,2 @@
args: run --reload tests/039_worker_deno_ns.ts
output: tests/039_worker_deno_ns.ts.out
25 changes: 25 additions & 0 deletions tests/039_worker_deno_ns.ts
@@ -0,0 +1,25 @@
const w1 = new Worker("./tests/039_worker_deno_ns/has_ns.ts");
const w2 = new Worker("./tests/039_worker_deno_ns/no_ns.ts", {
noDenoNamespace: true
});
let w1MsgCount = 0;
let w2MsgCount = 0;
w1.onmessage = (msg): void => {
console.log(msg.data);
w1MsgCount++;
if (w1MsgCount === 1) {
w1.postMessage("CONTINUE");
} else {
w2.postMessage("START");
}
};
w2.onmessage = (msg): void => {
console.log(msg.data);
w2MsgCount++;
if (w2MsgCount === 1) {
w2.postMessage("CONTINUE");
} else {
Deno.exit(0);
}
};
w1.postMessage("START");
4 changes: 4 additions & 0 deletions tests/039_worker_deno_ns.ts.out
@@ -0,0 +1,4 @@
has_ns.ts: is window.Deno available: true
[SPAWNED BY has_ns.ts] maybe_ns.ts: is window.Deno available: true
no_ns.ts: is window.Deno available: false
[SPAWNED BY no_ns.ts] maybe_ns.ts: is window.Deno available: false
10 changes: 10 additions & 0 deletions tests/039_worker_deno_ns/has_ns.ts
@@ -0,0 +1,10 @@
onmessage = (msg): void => {
if (msg.data === "START") {
postMessage("has_ns.ts: is window.Deno available: " + !!window.Deno);
} else {
const worker = new Worker("./tests/039_worker_deno_ns/maybe_ns.ts");
worker.onmessage = (msg): void => {
postMessage("[SPAWNED BY has_ns.ts] " + msg.data);
};
}
};
1 change: 1 addition & 0 deletions tests/039_worker_deno_ns/maybe_ns.ts
@@ -0,0 +1 @@
postMessage("maybe_ns.ts: is window.Deno available: " + !!window.Deno);
10 changes: 10 additions & 0 deletions tests/039_worker_deno_ns/no_ns.ts
@@ -0,0 +1,10 @@
onmessage = (msg): void => {
if (msg.data === "START") {
postMessage("no_ns.ts: is window.Deno available: " + !!window.Deno);
} else {
const worker = new Worker("./tests/039_worker_deno_ns/maybe_ns.ts");
worker.onmessage = (msg): void => {
postMessage("[SPAWNED BY no_ns.ts] " + msg.data);
};
}
};

0 comments on commit ddee2df

Please sign in to comment.