From 279dfa8006f4da7668886dff7fa4cdfd3613c3a1 Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Fri, 27 Oct 2017 14:46:57 -0400
Subject: [PATCH] The builtin map is now a static struct of arrays

---
 src/builtins/mod.rs                | 216 +++++++++++------------------
 src/main.rs                        |   4 +-
 src/shell/assignments.rs           |   2 +-
 src/shell/binary.rs                |   8 +-
 src/shell/flow.rs                  |   2 +-
 src/shell/history.rs               |   4 +-
 src/shell/library.rs               |   2 +-
 src/shell/mod.rs                   |  18 +--
 src/shell/pipe_exec/job_control.rs |   2 +-
 src/shell/pipe_exec/mod.rs         |   6 +-
 src/sys/redox.rs                   |  17 ++-
 src/sys/unix/job_control.rs        |   4 +-
 src/sys/unix/mod.rs                |   9 +-
 src/sys/unix/signals.rs            |   9 +-
 14 files changed, 132 insertions(+), 171 deletions(-)

diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs
index 9f97dd79..93e294a0 100644
--- a/src/builtins/mod.rs
+++ b/src/builtins/mod.rs
@@ -20,7 +20,6 @@ use self::source::source;
 use self::test::test;
 use self::variables::{alias, drop_alias, drop_array, drop_variable};
 
-use fnv::FnvHashMap;
 use std::error::Error;
 use std::io::{self, BufWriter, Write};
 
@@ -30,6 +29,68 @@ use shell::job_control::{JobControl, ProcessState};
 use shell::status::*;
 use sys;
 
+macro_rules! map {
+    ($($name:expr, $func:ident, $help:expr),+) => {{
+        BuiltinMap {
+            name: &[$($name),+],
+            help: &[$($help),+],
+            functions: &[$($func),+],
+        }
+    }
+}}
+
+pub const BUILTINS: &'static BuiltinMap =
+    &map!(
+    "let", list_vars, "Displays a list of local variables",
+    "echo", builtin_echo, "Display a line of text",
+    "cd", builtin_cd, "Change the current directory\n    cd <path>",
+    "dirs", builtin_dirs, "Display the current directory stack",
+    "pushd", builtin_pushd, "Push a directory to the stack",
+    "popd", builtin_popd, "Pop a directory from the stack",
+    "alias", builtin_alias, "View, set or unset aliases",
+    "unalias", builtin_unalias, "Delete an alias",
+    "fn", builtin_fn, "Print list of functions",
+    "read", builtin_read, "Read some variables\n    read <variable>",
+    "drop", builtin_drop, "Delete a variable",
+    "matches", builtin_matches, "Checks if a string matches a given regex",
+    "not", builtin_not, "Reverses the exit status value of the given command.",
+    "set", builtin_set, "Set or unset values of shell options and positional parameters.",
+    "eval", builtin_eval, "evaluates the evaluated expression",
+    "exit", builtin_exit, "Exits the current session",
+    "wait", builtin_wait, "Waits until all running background processes have completed",
+    "jobs", builtin_jobs, "Displays all jobs that are attached to the background",
+    "bg", builtin_bg, "Resumes a stopped background process",
+    "fg",builtin_fg, "Resumes and sets a background process as the active process",
+    "suspend", builtin_suspend, "Suspends the shell with a SIGTSTOP signal",
+    "disown", builtin_disown, "Disowning a process removes that process from the shell's background process table.",
+    "history", builtin_history,"Display a log of all commands previously executed",
+    "source", builtin_source, "Evaluate the file following the command or re-initialize the init file",
+    "test", builtin_test, "Performs tests on files and text",
+    "calc", builtin_calc, "Calculate a mathematical expression",
+    "true", builtin_true, "Do nothing, successfully",
+    "false", builtin_false, "Do nothing, unsuccessfully",
+    "help", builtin_help,
+    "Display helpful information about a given command or list commands if none \
+        specified\n    help <command>",
+    "and",
+    builtin_and,
+    "Execute the command if the shell's previous status is success",
+    "or",
+    builtin_or,
+    "Execute the command if the shell's previous status is failure",
+    "starts-with",
+    starts_with,
+    "Evaluates if the supplied argument starts with a given string",
+    "ends-with",
+    ends_with,
+    "Evaluates if the supplied argument ends with a given string",
+    "contains",
+    contains,
+    "Evaluates if the supplied argument contains a given string",
+    "exists", builtin_exists, "Performs tests on files and text",
+    "ion-docs", ion_docs, "Opens the Ion manual"
+);
+
 /// Structure which represents a Terminal's command.
 /// This command structure contains a name, and the code which run the
 /// functionnality associated to this one, with zero, one or several argument(s).
@@ -39,140 +100,26 @@ pub struct Builtin {
     pub main: fn(&[&str], &mut Shell) -> i32,
 }
 
-impl Builtin {
-    /// Return the map from command names to commands
-    pub fn map() -> FnvHashMap<&'static str, Self> {
-        let mut commands: FnvHashMap<&str, Self> =
-            FnvHashMap::with_capacity_and_hasher(32, Default::default());
-
-        // Quick and clean way to insert a builtin, define a function named as the builtin
-        // for example:
-        // fn builtin_not (args: &[&str], shell: &mut Shell) -> i32 {
-        // let cmd = args[1..].join(" ");
-        // shell.on_command(&cmd);
-        // match shell.previous_status {
-        // SUCCESS => FAILURE,
-        // FAILURE => SUCCESS,
-        // _ => shell.previous_status
-        // }
-        // }
-        //
-        // insert_builtin!("not", builtin_not, "Reverses the exit status value of the given
-        // command.");
-        //
-
-        macro_rules! insert_builtin {
-            ($name:expr, $func:ident, $help:expr) => {
-                commands.insert(
-                    $name,
-                    Builtin {
-                        name: $name,
-                        help: $help,
-                        main: $func,
-                    }
-                );
-            }
-        }
+pub struct BuiltinMap {
+    pub(crate) name:      &'static [&'static str],
+    pub(crate) help:      &'static [&'static str],
+    pub(crate) functions: &'static [fn(&[&str], &mut Shell) -> i32],
+}
 
-        // Directories
-        insert_builtin!("cd", builtin_cd, "Change the current directory\n    cd <path>");
-
-        insert_builtin!("dirs", builtin_dirs, "Display the current directory stack");
-        insert_builtin!("pushd", builtin_pushd, "Push a directory to the stack");
-        insert_builtin!("popd", builtin_popd, "Pop a directory from the stack");
-
-        // Aliases
-        insert_builtin!("alias", builtin_alias, "View, set or unset aliases");
-        insert_builtin!("unalias", builtin_unalias, "Delete an alias");
-
-        // Variables
-        insert_builtin!("fn", builtin_fn, "Print list of functions");
-        insert_builtin!("read", builtin_read, "Read some variables\n    read <variable>");
-        insert_builtin!("drop", builtin_drop, "Delete a variable");
-
-        // Misc
-        insert_builtin!("matches", builtin_matches, "Checks if a string matches a given regex");
-        insert_builtin!("not", builtin_not, "Reverses the exit status value of the given command.");
-        insert_builtin!(
-            "set",
-            builtin_set,
-            "Set or unset values of shell options and positional parameters."
-        );
-        insert_builtin!("eval", builtin_eval, "evaluates the evaluated expression");
-        insert_builtin!("exit", builtin_exit, "Exits the current session");
-        insert_builtin!(
-            "wait",
-            builtin_wait,
-            "Waits until all running background processes have completed"
-        );
-        insert_builtin!(
-            "jobs",
-            builtin_jobs,
-            "Displays all jobs that are attached to the background"
-        );
-        insert_builtin!("bg", builtin_bg, "Resumes a stopped background process");
-        insert_builtin!(
-            "fg",
-            builtin_fg,
-            "Resumes and sets a background process as the active process"
-        );
-        insert_builtin!("suspend", builtin_suspend, "Suspends the shell with a SIGTSTOP signal");
-        insert_builtin!(
-            "disown",
-            builtin_disown,
-            "Disowning a process removes that process from the shell's background process table."
-        );
-        insert_builtin!(
-            "history",
-            builtin_history,
-            "Display a log of all commands previously executed"
-        );
-        insert_builtin!(
-            "source",
-            builtin_source,
-            "Evaluate the file following the command or re-initialize the init file"
-        );
-        insert_builtin!("echo", builtin_echo, "Display a line of text");
-        insert_builtin!("test", builtin_test, "Performs tests on files and text");
-        insert_builtin!("calc", builtin_calc, "Calculate a mathematical expression");
-        insert_builtin!("true", builtin_true, "Do nothing, successfully");
-        insert_builtin!("false", builtin_false, "Do nothing, unsuccessfully");
-        insert_builtin!(
-            "help",
-            builtin_help,
-            "Display helpful information about a given command or list commands if none \
-             specified\n    help <command>"
-        );
-        insert_builtin!(
-            "and",
-            builtin_and,
-            "Execute the command if the shell's previous status is success"
-        );
-        insert_builtin!(
-            "or",
-            builtin_or,
-            "Execute the command if the shell's previous status is failure"
-        );
-        insert_builtin!(
-            "starts-with",
-            starts_with,
-            "Evaluates if the supplied argument starts with a given string"
-        );
-        insert_builtin!(
-            "ends-with",
-            ends_with,
-            "Evaluates if the supplied argument ends with a given string"
-        );
-        insert_builtin!(
-            "contains",
-            contains,
-            "Evaluates if the supplied argument contains a given string"
-        );
-        insert_builtin!("exists", builtin_exists, "Performs tests on files and text");
-        insert_builtin!("ion-docs", ion_docs, "Opens the Ion manual");
-        insert_builtin!("let", list_vars, "Displays a list of local variables");
-        commands
+impl BuiltinMap {
+    pub fn get(&self, func: &str) -> Option<Builtin> {
+        self.name.iter().position(|&name| name == func).map(|pos| unsafe {
+            Builtin {
+                name: *self.name.get_unchecked(pos),
+                help: *self.help.get_unchecked(pos),
+                main: *self.functions.get_unchecked(pos),
+            }
+        })
     }
+
+    pub fn keys(&self) -> &'static [&'static str] { self.name }
+
+    pub fn contains_key(&self, func: &str) -> bool { self.name.iter().any(|&name| name == func) }
 }
 
 // Definitions of simple builtins go here
@@ -387,8 +334,7 @@ fn builtin_help(args: &[&str], shell: &mut Shell) -> i32 {
             let _ = stdout.write_all(b"\n");
         }
     } else {
-        let mut commands = builtins.keys().cloned().collect::<Vec<&str>>();
-        commands.sort();
+        let mut commands = builtins.keys();
 
         let mut buffer: Vec<u8> = Vec::new();
         for command in commands {
diff --git a/src/main.rs b/src/main.rs
index cd28846c..c047f8e3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -47,7 +47,6 @@ mod builtins;
 mod shell;
 mod ascii_helpers;
 
-use builtins::Builtin;
 use shell::{signals, Binary, Shell};
 use std::sync::atomic::Ordering;
 
@@ -72,7 +71,6 @@ fn main() {
         }
     }
 
-    let builtins = Builtin::map();
-    let shell = Shell::new_bin(&builtins);
+    let shell = Shell::new_bin();
     shell.main();
 }
diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs
index b94a5b17..50029b3c 100644
--- a/src/shell/assignments.rs
+++ b/src/shell/assignments.rs
@@ -52,7 +52,7 @@ pub(crate) trait VariableStore {
     fn export(&mut self, &str) -> i32;
 }
 
-impl<'a> VariableStore for Shell<'a> {
+impl VariableStore for Shell {
     fn local(&mut self, expression: &str) -> i32 {
         match AssignmentActions::new(expression) {
             Ok(assignment_actions) => {
diff --git a/src/shell/binary.rs b/src/shell/binary.rs
index a3ca88ab..3e580e8d 100644
--- a/src/shell/binary.rs
+++ b/src/shell/binary.rs
@@ -45,7 +45,7 @@ pub(crate) trait Binary {
     fn prompt_fn(&mut self) -> Option<String>;
 }
 
-impl<'a> Binary for Shell<'a> {
+impl Binary for Shell {
     fn prompt(&mut self) -> String {
         if self.flow_control.level == 0 {
             let rprompt = match self.prompt_fn() {
@@ -112,7 +112,7 @@ impl<'a> Binary for Shell<'a> {
                 let prompt = self.prompt();
                 let funcs = &self.functions;
                 let vars = &self.variables;
-                let builtins = self.builtins;
+                let builtins = &self.builtins;
 
                 let line = self.context.as_mut().unwrap().read_line(
                     prompt,
@@ -152,9 +152,9 @@ impl<'a> Binary for Shell<'a> {
                                 // Creates a list of definitions from the shell environment that
                                 // will be used
                                 // in the creation of a custom completer.
-                                let words = builtins.iter()
+                                let words = builtins.keys().iter()
                                 // Add built-in commands to the completer's definitions.
-                                .map(|(&s, _)| Identifier::from(s))
+                                .map(|&s| Identifier::from(s))
                                 // Add the history list to the completer's definitions.
                                 .chain(history.iter().cloned())
                                 // Add the aliases to the completer's definitions.
diff --git a/src/shell/flow.rs b/src/shell/flow.rs
index 2df3e05c..f4fb46d7 100644
--- a/src/shell/flow.rs
+++ b/src/shell/flow.rs
@@ -62,7 +62,7 @@ pub(crate) trait FlowLogic {
     fn execute_match(&mut self, expression: String, cases: Vec<Case>) -> Condition;
 }
 
-impl<'a> FlowLogic for Shell<'a> {
+impl FlowLogic for Shell {
     fn on_command(&mut self, command_string: &str) {
         self.break_flow = false;
         let mut iterator = StatementSplitter::new(command_string).map(parse_and_validate);
diff --git a/src/shell/history.rs b/src/shell/history.rs
index fe7841c8..0917e89f 100644
--- a/src/shell/history.rs
+++ b/src/shell/history.rs
@@ -67,7 +67,7 @@ trait ShellHistoryPrivate {
     fn should_save_command(&self, command: &str) -> bool;
 }
 
-impl<'a> ShellHistory for Shell<'a> {
+impl ShellHistory for Shell {
     fn print_history(&self, _arguments: &[&str]) -> i32 {
         if let Some(context) = self.context.as_ref() {
             let mut buffer = Vec::with_capacity(8 * 1024);
@@ -140,7 +140,7 @@ impl<'a> ShellHistory for Shell<'a> {
     }
 }
 
-impl<'a> ShellHistoryPrivate for Shell<'a> {
+impl ShellHistoryPrivate for Shell {
     fn should_save_command(&self, command: &str) -> bool {
         // just for convenience and to make the code look a bit cleaner
         let ignore = &self.ignore_setting.flags;
diff --git a/src/shell/library.rs b/src/shell/library.rs
index 986811eb..b93a1518 100644
--- a/src/shell/library.rs
+++ b/src/shell/library.rs
@@ -12,7 +12,7 @@ pub trait IonLibrary {
     fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32>;
 }
 
-impl<'a> IonLibrary for Shell<'a> {
+impl IonLibrary for Shell {
     fn execute_command(&mut self, command: &str) -> i32 {
         self.on_command(command);
         self.previous_status
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index 39d48757..f0f13e23 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -31,7 +31,7 @@ use self::pipe_exec::PipelineExecution;
 use self::status::*;
 use self::variables::Variables;
 use app_dirs::{app_root, AppDataType, AppInfo};
-use builtins::*;
+use builtins::{BuiltinMap, BUILTINS};
 use fnv::FnvHashMap;
 use liner::Context;
 use parser::{ArgumentSplitter, Expander, Select};
@@ -51,9 +51,9 @@ use types::*;
 /// the entirety of the
 /// program. It is initialized at the beginning of the program, and lives until the end of the
 /// program.
-pub struct Shell<'a> {
+pub struct Shell {
     /// Contains a list of built-in commands that were created when the program started.
-    pub builtins: &'a FnvHashMap<&'static str, Builtin>,
+    pub builtins: &'static BuiltinMap,
     /// Contains the history, completions, and manages writes to the history file.
     /// Note that the context is only available in an interactive session.
     pub context: Option<Context>,
@@ -90,12 +90,12 @@ pub struct Shell<'a> {
     ignore_setting: IgnoreSetting,
 }
 
-impl<'a> Shell<'a> {
+impl<'a> Shell {
     #[allow(dead_code)]
     /// Panics if DirectoryStack construction fails
-    pub(crate) fn new_bin(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> {
+    pub(crate) fn new_bin() -> Shell {
         Shell {
-            builtins:            builtins,
+            builtins:            BUILTINS,
             context:             None,
             variables:           Variables::default(),
             flow_control:        FlowControl::default(),
@@ -115,9 +115,9 @@ impl<'a> Shell<'a> {
     }
 
     #[allow(dead_code)]
-    pub fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> {
+    pub fn new() -> Shell {
         Shell {
-            builtins:            builtins,
+            builtins:            BUILTINS,
             context:             None,
             variables:           Variables::default(),
             flow_control:        FlowControl::default(),
@@ -303,7 +303,7 @@ impl<'a> Shell<'a> {
     }
 }
 
-impl<'a> Expander for Shell<'a> {
+impl<'a> Expander for Shell {
     fn tilde(&self, input: &str) -> Option<String> {
         self.variables.tilde_expansion(input, &self.directory_stack)
     }
diff --git a/src/shell/pipe_exec/job_control.rs b/src/shell/pipe_exec/job_control.rs
index 55b8f80e..786f7ab5 100644
--- a/src/shell/pipe_exec/job_control.rs
+++ b/src/shell/pipe_exec/job_control.rs
@@ -102,7 +102,7 @@ pub struct BackgroundProcess {
     pub name:          String,
 }
 
-impl<'a> JobControl for Shell<'a> {
+impl JobControl for Shell {
     fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 {
         // Resume the background task, if needed.
         if cont {
diff --git a/src/shell/pipe_exec/mod.rs b/src/shell/pipe_exec/mod.rs
index 3c5b9d64..21946764 100644
--- a/src/shell/pipe_exec/mod.rs
+++ b/src/shell/pipe_exec/mod.rs
@@ -391,7 +391,7 @@ pub(crate) trait PipelineExecution {
     ) -> i32;
 }
 
-impl<'a> PipelineExecution for Shell<'a> {
+impl PipelineExecution for Shell {
     fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32 {
         // Remove any leftover foreground tasks from the last execution.
         self.foreground.clear();
@@ -447,9 +447,9 @@ impl<'a> PipelineExecution for Shell<'a> {
                         "cd".into(),
                         iter::once("cd".into()).chain(job.args.drain()).collect(),
                     )
-                } else if self.functions.contains_key::<str>(job.args[0].as_ref()) {
+                } else if self.functions.contains_key(job.args[0].as_str()) {
                     RefinedJob::function(job.args[0].clone().into(), job.args.drain().collect())
-                } else if self.builtins.contains_key::<str>(job.args[0].as_ref()) {
+                } else if self.builtins.contains_key(job.args[0].as_str()) {
                     RefinedJob::builtin(job.args[0].clone().into(), job.args.drain().collect())
                 } else {
                     let mut command = Command::new(job.args[0].clone());
diff --git a/src/sys/redox.rs b/src/sys/redox.rs
index 0c812180..cd1145c8 100644
--- a/src/sys/redox.rs
+++ b/src/sys/redox.rs
@@ -83,7 +83,10 @@ pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> {
 
 pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(syscall::close(fd)).and(Ok(())) }
 
-pub(crate) fn close_stdin() {
+pub(crate) fn close_stdin()
+// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn
+// close_stdin()
+{
     syscall::close(STDIN_FILENO);
 }
 
@@ -103,12 +106,16 @@ fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
 
 // TODO
 pub mod signals {
-    pub(crate) fn block() { }
+    pub(crate) fn block() // fn block() // fn block() // fn block() // fn block()
+    {
+    }
 
     /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be
     /// controlled
     /// by the shell.
-    pub(crate) fn unblock() { }
+    pub(crate) fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn unblock()
+    {
+    }
 }
 
 pub mod job_control {
@@ -132,8 +139,8 @@ pub mod job_control {
     }
 
 
-    pub(crate) fn watch_foreground<'a, F, D>(
-        shell: &mut Shell<'a>,
+    pub(crate) fn watch_foreground<F, D>(
+        shell: &mut Shell,
         _pid: u32,
         last_pid: u32,
         _get_command: F,
diff --git a/src/sys/unix/job_control.rs b/src/sys/unix/job_control.rs
index 922ebc65..3881f2f4 100644
--- a/src/sys/unix/job_control.rs
+++ b/src/sys/unix/job_control.rs
@@ -81,8 +81,8 @@ pub(crate) fn watch_background(
 const FIRST: u8 = 1;
 const LAST: u8 = 2;
 
-pub(crate) fn watch_foreground<'a, F, D>(
-    shell: &mut Shell<'a>,
+pub(crate) fn watch_foreground<F, D>(
+    shell: &mut Shell,
     first_pid: u32,
     last_pid: u32,
     get_command: F,
diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs
index 898717ff..cb70ecf6 100644
--- a/src/sys/unix/mod.rs
+++ b/src/sys/unix/mod.rs
@@ -80,8 +80,13 @@ pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> {
 
 pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(unsafe { libc::close(fd) }).and(Ok(())) }
 
-pub(crate) fn close_stdin() {
-    unsafe { libc::close(STDIN_FILENO); }
+pub(crate) fn close_stdin()
+// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn
+// close_stdin()
+{
+    unsafe {
+        libc::close(STDIN_FILENO);
+    }
 }
 
 pub(crate) fn isatty(fd: RawFd) -> bool { unsafe { libc::isatty(fd) == 1 } }
diff --git a/src/sys/unix/signals.rs b/src/sys/unix/signals.rs
index 659384f0..49c477f3 100644
--- a/src/sys/unix/signals.rs
+++ b/src/sys/unix/signals.rs
@@ -4,7 +4,9 @@ use std::ptr;
 
 /// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives
 /// them.
-pub(crate) fn block() // fn block() // fn block()
+pub(crate) fn block()
+// fn block() // fn block() // fn block() // fn block() // fn block() // fn
+// block()
 {
     unsafe {
         let mut sigset = mem::uninitialized::<sigset_t>();
@@ -20,7 +22,10 @@ pub(crate) fn block() // fn block() // fn block()
 /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be
 /// controlled
 /// by the shell.
-pub(crate) fn unblock() // fn unblock() // fn unblock()
+pub(crate) fn unblock()
+// fn unblock()
+// fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn
+// unblock()
 {
     unsafe {
         let mut sigset = mem::uninitialized::<sigset_t>();
-- 
GitLab