summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Power <theaaronepower@gmail.com>2018-08-31 12:51:12 +0100
committerRyan Dahl <ry@tinyclouds.org>2018-08-31 13:18:24 -0400
commitf131445a46555f1634aecae0fc1d4979b4cefa6d (patch)
treefb9847c7f1b6aebc98bc8ef788366dd90b3f9b12
parent45dafe15ee87b34d0c3c9b4bc72905c176514051 (diff)
Implemented deno.env and refactored flags.rs
-rw-r--r--js/deno.ts1
-rw-r--r--js/os.ts58
-rw-r--r--js/os_test.ts21
-rw-r--r--js/test_util.ts21
-rw-r--r--src/flags.rs49
-rw-r--r--src/handlers.rs71
-rw-r--r--src/msg.fbs19
-rwxr-xr-xtools/unit_tests.py12
8 files changed, 206 insertions, 46 deletions
diff --git a/js/deno.ts b/js/deno.ts
index e24345ccd..c9611d116 100644
--- a/js/deno.ts
+++ b/js/deno.ts
@@ -1,6 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
// Public deno module.
export {
+ env,
exit,
FileInfo,
makeTempDirSync,
diff --git a/js/os.ts b/js/os.ts
index 8730a6ddf..e98f1557d 100644
--- a/js/os.ts
+++ b/js/os.ts
@@ -128,6 +128,64 @@ export function readFileSync(filename: string): Uint8Array {
return new Uint8Array(dataArray!);
}
+function createEnv(_msg: fbs.EnvironRes): { [index:string]: string } {
+ const env: { [index:string]: string } = {};
+
+ for (let i = 0; i < _msg.mapLength(); i++) {
+ const item = _msg.map(i)!;
+
+ env[item.key()!] = item.value()!;
+ }
+
+ return new Proxy(env, {
+ set(obj, prop: string, value: string | number) {
+ setEnv(prop, value.toString());
+ return Reflect.set(obj, prop, value);
+ }
+ });
+}
+
+function setEnv(key: string, value: string): void {
+ const builder = new flatbuffers.Builder();
+ const _key = builder.createString(key);
+ const _value = builder.createString(value);
+ fbs.SetEnv.startSetEnv(builder);
+ fbs.SetEnv.addKey(builder, _key);
+ fbs.SetEnv.addValue(builder, _value);
+ const msg = fbs.SetEnv.endSetEnv(builder);
+ send(builder, fbs.Any.SetEnv, msg);
+}
+
+/**
+ * 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 or `number`s
+ * as values.
+ * import { env } from "deno";
+ * const env = deno.env();
+ * console.log(env.SHELL)
+ * env.TEST_VAR = "HELLO";
+ *
+ * const newEnv = deno.env();
+ * console.log(env.TEST_VAR == newEnv.TEST_VAR);
+ */
+export function env(): { [index:string]: string } {
+ /* Ideally we could write
+ const res = send({
+ command: fbs.Command.ENV,
+ });
+ */
+ const builder = new flatbuffers.Builder();
+ fbs.Environ.startEnviron(builder);
+ const msg = fbs.Environ.endEnviron(builder);
+ const baseRes = send(builder, fbs.Any.Environ, msg)!;
+ assert(fbs.Any.EnvironRes === baseRes.msgType());
+ const res = new fbs.EnvironRes();
+ assert(baseRes.msg(res) != null);
+ // TypeScript cannot track assertion above, therefore not null assertion
+ return createEnv(res);
+}
+
export class FileInfo {
private _isFile: boolean;
private _isSymlink: boolean;
diff --git a/js/os_test.ts b/js/os_test.ts
index 7bf1d86a5..cd5ede221 100644
--- a/js/os_test.ts
+++ b/js/os_test.ts
@@ -2,6 +2,27 @@
import { test, testPerm, assert, assertEqual } from "./test_util.ts";
import * as deno from "deno";
+testPerm({ env: true }, async function envSuccess() {
+ const env = deno.env();
+ assert(env !== null);
+ env.test_var = "Hello World";
+ const newEnv = deno.env();
+ assertEqual(env.test_var, newEnv.test_var);
+});
+
+test(async function envFailure() {
+ let caughtError = false;
+ try {
+ const env = deno.env();
+ } catch (err) {
+ caughtError = true;
+ // TODO assert(err instanceof deno.PermissionDenied).
+ assertEqual(err.name, "deno.PermissionDenied");
+ }
+
+ assert(caughtError);
+});
+
// TODO Add tests for modified, accessed, and created fields once there is a way
// to create temp files.
test(async function statSyncSuccess() {
diff --git a/js/test_util.ts b/js/test_util.ts
index 116b96ff2..433bbf11b 100644
--- a/js/test_util.ts
+++ b/js/test_util.ts
@@ -17,23 +17,26 @@ testing.setFilter(deno.argv[1]);
interface DenoPermissions {
write?: boolean;
net?: boolean;
+ env?: boolean;
}
function permToString(perms: DenoPermissions): string {
const w = perms.write ? 1 : 0;
const n = perms.net ? 1 : 0;
- return `permW${w}N${n}`;
+ const e = perms.env ? 1 : 0;
+ return `permW${w}N${n}E${e}`;
}
function permFromString(s: string): DenoPermissions {
- const re = /^permW([01])N([01])$/;
+ const re = /^permW([01])N([01])E([01])$/;
const found = s.match(re);
if (!found) {
throw Error("Not a permission string");
}
return {
write: Boolean(Number(found[1])),
- net: Boolean(Number(found[2]))
+ net: Boolean(Number(found[2])),
+ env: Boolean(Number(found[3])),
};
}
@@ -43,14 +46,16 @@ export function testPerm(perms: DenoPermissions, fn: testing.TestFunction) {
}
export function test(fn: testing.TestFunction) {
- testPerm({ write: false, net: false }, fn);
+ testPerm({ write: false, net: false, env: false }, fn);
}
test(function permSerialization() {
- for (let write of [true, false]) {
- for (let net of [true, false]) {
- let perms: DenoPermissions = { write, net };
- testing.assertEqual(perms, permFromString(permToString(perms)));
+ for (const write of [true, false]) {
+ for (const net of [true, false]) {
+ for (const env of [true, false]) {
+ const perms: DenoPermissions = { write, net, env };
+ testing.assertEqual(perms, permFromString(permToString(perms)));
+ }
}
}
});
diff --git a/src/flags.rs b/src/flags.rs
index 7149ab19f..851bacb6b 100644
--- a/src/flags.rs
+++ b/src/flags.rs
@@ -11,7 +11,7 @@ macro_rules! svec {
($($x:expr),*) => (vec![$($x.to_string()),*]);
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Default)]
pub struct DenoFlags {
pub help: bool,
pub log_debug: bool,
@@ -19,6 +19,7 @@ pub struct DenoFlags {
pub reload: bool,
pub allow_write: bool,
pub allow_net: bool,
+ pub allow_env: bool,
}
pub fn print_usage() {
@@ -27,6 +28,7 @@ pub fn print_usage() {
--allow-write Allow file system write access.
--allow-net Allow network access.
+--allow-env Allow environment access.
-v or --version Print the version.
-r or --reload Reload cached remote resources.
-D or --log-debug Log debug output.
@@ -37,14 +39,7 @@ pub fn print_usage() {
// Parses flags for deno. This does not do v8_set_flags() - call that separately.
pub fn set_flags(args: Vec<String>) -> (DenoFlags, Vec<String>) {
- let mut flags = DenoFlags {
- help: false,
- version: false,
- reload: false,
- log_debug: false,
- allow_write: false,
- allow_net: false,
- };
+ let mut flags = DenoFlags::default();
let mut rest = Vec::new();
for a in &args {
match a.as_str() {
@@ -52,6 +47,7 @@ pub fn set_flags(args: Vec<String>) -> (DenoFlags, Vec<String>) {
"-D" | "--log-debug" => flags.log_debug = true,
"-v" | "--version" => flags.version = true,
"-r" | "--reload" => flags.reload = true,
+ "--allow-env" => flags.allow_env = true,
"--allow-write" => flags.allow_write = true,
"--allow-net" => flags.allow_net = true,
_ => rest.push(a.clone()),
@@ -64,15 +60,10 @@ pub fn set_flags(args: Vec<String>) -> (DenoFlags, Vec<String>) {
#[test]
fn test_set_flags_1() {
let (flags, rest) = set_flags(svec!["deno", "--version"]);
- assert!(rest == svec!["deno"]);
- assert!(
- flags == DenoFlags {
- help: false,
- log_debug: false,
+ assert_eq!(rest, svec!["deno"]);
+ assert_eq!(flags, DenoFlags {
version: true,
- reload: false,
- allow_write: false,
- allow_net: false,
+ ..DenoFlags::default()
}
);
}
@@ -80,15 +71,11 @@ fn test_set_flags_1() {
#[test]
fn test_set_flags_2() {
let (flags, rest) = set_flags(svec!["deno", "-r", "-D", "script.ts"]);
- assert!(rest == svec!["deno", "script.ts"]);
- assert!(
- flags == DenoFlags {
- help: false,
+ assert_eq!(rest, svec!["deno", "script.ts"]);
+ assert_eq!(flags, DenoFlags {
log_debug: true,
- version: false,
reload: true,
- allow_write: false,
- allow_net: false,
+ ..DenoFlags::default()
}
);
}
@@ -97,15 +84,11 @@ fn test_set_flags_2() {
fn test_set_flags_3() {
let (flags, rest) =
set_flags(svec!["deno", "-r", "script.ts", "--allow-write"]);
- assert!(rest == svec!["deno", "script.ts"]);
- assert!(
- flags == DenoFlags {
- help: false,
- log_debug: false,
- version: false,
+ assert_eq!(rest, svec!["deno", "script.ts"]);
+ assert_eq!(flags, DenoFlags {
reload: true,
allow_write: true,
- allow_net: false,
+ ..DenoFlags::default()
}
);
}
@@ -142,13 +125,13 @@ fn parse_core_args(args: Vec<String>) -> (Vec<String>, Vec<String>) {
fn test_parse_core_args_1() {
let js_args =
parse_core_args(vec!["deno".to_string(), "--v8-options".to_string()]);
- assert!(js_args == (vec!["deno".to_string(), "--help".to_string()], vec![]));
+ assert_eq!(js_args, (vec!["deno".to_string(), "--help".to_string()], vec![]));
}
#[test]
fn test_parse_core_args_2() {
let js_args = parse_core_args(vec!["deno".to_string(), "--help".to_string()]);
- assert!(js_args == (vec!["deno".to_string()], vec!["--help".to_string()]));
+ assert_eq!(js_args, (vec!["deno".to_string()], vec!["--help".to_string()]));
}
// Pass the command line arguments to v8.
diff --git a/src/handlers.rs b/src/handlers.rs
index 8d321c46f..407ecf5c8 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -45,6 +45,7 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
let output_code = msg.output_code().unwrap();
handle_code_cache(d, &mut builder, filename, source_code, output_code)
}
+ msg::Any::Environ => handle_env(d, &mut builder),
msg::Any::FetchReq => {
// TODO base.msg_as_FetchReq();
let msg = msg::FetchReq::init_from_table(base.msg().unwrap());
@@ -79,6 +80,13 @@ pub extern "C" fn msg_from_js(d: *const DenoC, buf: deno_buf) {
let filename = msg.filename().unwrap();
handle_read_file_sync(d, &mut builder, filename)
}
+ msg::Any::SetEnv => {
+ // TODO base.msg_as_SetEnv();
+ let msg = msg::SetEnv::init_from_table(base.msg().unwrap());
+ let key = msg.key().unwrap();
+ let value = msg.value().unwrap();
+ handle_set_env(d, &mut builder, key, value)
+ }
msg::Any::StatSync => {
// TODO base.msg_as_StatSync();
let msg = msg::StatSync::init_from_table(base.msg().unwrap());
@@ -244,6 +252,69 @@ fn handle_code_cache(
Ok(null_buf()) // null response indicates success.
}
+fn handle_set_env(
+ d: *const DenoC,
+ _builder: &mut FlatBufferBuilder,
+ key: &str,
+ value: &str,
+) -> HandlerResult {
+ let deno = from_c(d);
+ if !deno.flags.allow_env {
+ let err = std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "allow_env is off.",
+ );
+ return Err(err.into());
+ }
+
+ std::env::set_var(key, value);
+ Ok(null_buf())
+}
+
+fn handle_env(
+ d: *const DenoC,
+ builder: &mut FlatBufferBuilder,
+) -> HandlerResult {
+ let deno = from_c(d);
+ if !deno.flags.allow_env {
+ let err = std::io::Error::new(
+ std::io::ErrorKind::PermissionDenied,
+ "allow_env is off.",
+ );
+ return Err(err.into());
+ }
+
+ let vars: Vec<_> = std::env::vars().map(|(key, value)| {
+ let key = builder.create_string(&key);
+ let value = builder.create_string(&value);
+
+ msg::EnvPair::create(builder, &msg::EnvPairArgs {
+ key: Some(key),
+ value: Some(value),
+ ..Default::default()
+ })
+ }).collect();
+
+ let tables = builder.create_vector_of_reverse_offsets(&vars);
+
+ let msg = msg::EnvironRes::create(
+ builder,
+ &msg::EnvironResArgs {
+ map: Some(tables),
+ ..Default::default()
+ },
+ );
+
+ Ok(create_msg(
+ builder,
+ &msg::BaseArgs {
+ msg: Some(flatbuffers::Offset::new(msg.value())),
+ msg_type: msg::Any::EnvironRes,
+ ..Default::default()
+ },
+ ))
+}
+
fn handle_fetch_req(
d: *const DenoC,
_builder: &mut FlatBufferBuilder,
diff --git a/src/msg.fbs b/src/msg.fbs
index eb3753d75..ca2e16af7 100644
--- a/src/msg.fbs
+++ b/src/msg.fbs
@@ -10,6 +10,8 @@ union Any {
TimerStart,
TimerReady,
TimerClear,
+ Environ,
+ EnvironRes,
FetchReq,
FetchRes,
MakeTempDir,
@@ -18,6 +20,7 @@ union Any {
ReadFileSyncRes,
StatSync,
StatSyncRes,
+ SetEnv,
WriteFileSync,
}
@@ -124,6 +127,22 @@ table TimerClear {
id: uint;
}
+table Environ {}
+
+table SetEnv {
+ key: string;
+ value: string;
+}
+
+table EnvironRes {
+ map: [EnvPair];
+}
+
+table EnvPair {
+ key: string;
+ value: string;
+}
+
table FetchReq {
id: uint;
url: string;
diff --git a/tools/unit_tests.py b/tools/unit_tests.py
index a2cfa33b6..460d32bc2 100755
--- a/tools/unit_tests.py
+++ b/tools/unit_tests.py
@@ -10,12 +10,14 @@ import sys
# tests by the special string. permW0N0 means allow-write but not allow-net.
# See js/test_util.ts for more details.
def unit_tests(deno_exe):
- run([deno_exe, "js/unit_tests.ts", "permW0N0"])
- run([deno_exe, "js/unit_tests.ts", "permW1N0", "--allow-write"])
- run([deno_exe, "js/unit_tests.ts", "permW0N1", "--allow-net"])
+ run([deno_exe, "js/unit_tests.ts", "permW0N0E0"])
+ run([deno_exe, "js/unit_tests.ts", "permW1N0E0", "--allow-write"])
+ run([deno_exe, "js/unit_tests.ts", "permW0N1E0", "--allow-net"])
+ run([deno_exe, "js/unit_tests.ts", "permW0N0E1", "--allow-env"])
run([
- deno_exe, "js/unit_tests.ts", "permW1N1", "--allow-write",
- "--allow-net"
+ deno_exe, "js/unit_tests.ts", "permW1N1E1", "--allow-write",
+ "--allow-net",
+ "--allow-env",
])