<commit_before>use std::rc::*;

use super::address::*;
use super::treenode::*;
use super::basictree::*;

///
/// Represents which of the root's references have changed
///
pub enum TreeChangeType {
    /// The node's child reference has been replaced
    Child,

    /// The node's sibling reference has been replaced
    Sibling
}

///
/// A change represents an alteration to the tree
///
pub struct TreeChange {
    /// The address of the node matching the root of the change
    ///
    /// The address is relative to an imaginary node that is the parent of the real root node. This makes it possible to
    /// replace the entire tree by setting this to `TreeAddress::Here` and the change_type to `TreeChangeType::Child`.
    /// The real root node can be addressed at `TreeAddress::ChildAtIndex(0)`
    root: TreeAddress,

    /// Which of the root's references have changed
    change_type: TreeChangeType,

    /// The tree that should replace the changed reference. The last node in this tree (depth-first) will be given the same sibling as the last node in the replacement range
    replacement_tree: Option<TreeRef>
}

impl TreeChange {
    ///
    /// Creates a new tree change
    ///
    pub fn new<TAddress: ToTreeAddress, TNode: ToTreeNode>(root: &TAddress, change_type: TreeChangeType, replacement_tree: Option<&TNode>) -> TreeChange {
        TreeChange { root: root.to_tree_address(), change_type: change_type, replacement_tree: replacement_tree.map(|x| x.to_tree_node()) }
    }

    ///
    /// Performs a replacement on a basic tree node
    ///
    #[inline]
    fn perform_replacement(node: &BasicTree, change_type: &TreeChangeType, replacement_tree: &Option<TreeRef>) {
        match *change_type {
            TreeChangeType::Child => {
                match *replacement_tree {
                    Some(ref new_child)     => node.set_child_ref(new_child.clone()),
                    None                    => node.clear_child()
                }
            },

            TreeChangeType::Sibling => {
                match *replacement_tree {
                    Some(ref new_sibling)   => node.set_sibling_ref(new_sibling.clone()),
                    None                    => node.clear_child()
                }
            }
        }
    }

    ///
    /// Applies a change to a tree, generating a new tree
    ///
    fn perform_apply(original_tree: &TreeRef, address: &TreeAddress, change_type: &TreeChangeType, replacement_tree: &Option<TreeRef>) -> TreeRef {
        match *address {
            TreeAddress::Here => {
                let new_node = BasicTree::from(original_tree);
                TreeChange::perform_replacement(&new_node, change_type, replacement_tree);

                Rc::new(new_node)
            },

            TreeAddress::ChildAtIndex(child_index, ref child_address) => {
                // Copy the siblings into a stack
                let mut siblings    = vec![];
                let mut current     = original_tree.get_child_ref();

                for _ in 0..child_index {
                    siblings.push(current.clone().unwrap_or_else(|| Rc::new(BasicTree::new("", ()))));

                    current = current.and_then(|x| x.get_sibling_ref());
                }

                // Replace the child matching this item
                let child_tree  = current.clone().unwrap_or_else(|| Rc::new(BasicTree::new("", ())));
                let new_child   = TreeChange::perform_apply(&child_tree, &*child_address, change_type, replacement_tree);
                siblings.push(new_child);

                current = current.and_then(|x| x.get_sibling_ref());

                // Pop siblings to generate the new child item
                while let Some(sibling) = siblings.pop() {
                    match current {
                        Some(next_sibling)  => current = Some(Rc::new(BasicTree::from_with_sibling(sibling, next_sibling))),
                        None                => current = Some(sibling),
                    }
                }

                Rc::new(BasicTree::from_with_child(original_tree, current.unwrap()))
            },

            TreeAddress::ChildWithTag(ref child_tag, ref child_address) => {
                let child_tree  = original_tree.get_child_at(&**child_tag);
                let new_child   = TreeChange::perform_apply(&child_tree, &*child_address, change_type, replacement_tree);

                Rc::new(BasicTree::from_with_child(original_tree, new_child))
            }
        }
    }

    ///
    /// Creates a new tree from an old tree with this change added to it
    ///
    #[inline]
    pub fn apply(&self, original_tree: &TreeRef) -> TreeRef {
        // For simplicity, we actualise the 'imaginary' root
        let imaginary_root      = tree!("", original_tree).to_tree_node();
        let new_imaginary_root  = TreeChange::perform_apply(&imaginary_root, &self.root, &self.change_type, &self.replacement_tree);

        // Result is the child of the imaginary node (or an empty node if the entire tree is deleted)
        new_imaginary_root.get_child_ref().unwrap_or_else(|| "".to_tree_node())
    }
}

#[cfg(test)]
mod change_tests {
    use super::super::super::tree::*;

    #[test]
    fn can_apply_simple_change_tagged() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&("test", "one"), TreeChangeType::Sibling, Some(&("replaced", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(changed_tree.get_child_ref_at("one").unwrap().get_value().to_int(0) == 1);
        assert!(changed_tree.get_child_ref_at("replaced").unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at("replaced").unwrap().get_sibling_ref().is_none());
        assert!(changed_tree.get_child_ref_at("two").is_none());
        assert!(changed_tree.get_child_ref_at("three").is_none());
    }

    #[test]
    fn can_apply_simple_change_indexed() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&(0, 0), TreeChangeType::Sibling, Some(&("replaced", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(changed_tree.get_child_ref_at(0).unwrap().get_value().to_int(0) == 1);
        assert!(changed_tree.get_child_ref_at(1).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at(1).unwrap().get_sibling_ref().is_none());
        assert!(changed_tree.get_child_ref_at(2).is_none());
        assert!(changed_tree.get_child_ref_at(3).is_none());
    }

    #[test]
    fn can_apply_child_change_tagged() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&("test", "two").to_tree_address(), TreeChangeType::Child, Some(&("new_child", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(!changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).is_none());

        assert!(changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).unwrap().get_sibling_ref().is_none());

        assert!(!changed_tree.get_child_ref_at("one").is_none());
        assert!(!changed_tree.get_child_ref_at("two").is_none());
        assert!(!changed_tree.get_child_ref_at("three").is_none());
        assert!(changed_tree.get_child_ref_at("two").unwrap().get_value().to_int(0) == 2);
    }

    #[test]
    fn can_apply_child_change_indexed() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&(0, 1).to_tree_address(), TreeChangeType::Child, Some(&("new_child", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(!changed_tree.get_child_ref_at((1, 0).to_tree_address()).is_none());

        assert!(changed_tree.get_child_ref_at((1, 0).to_tree_address()).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at((1, 0).to_tree_address()).unwrap().get_sibling_ref().is_none());

        assert!(!changed_tree.get_child_ref_at(0).is_none());
        assert!(!changed_tree.get_child_ref_at(1).is_none());
        assert!(!changed_tree.get_child_ref_at(2).is_none());
        assert!(changed_tree.get_child_ref_at(1).unwrap().get_value().to_int(0) == 2);
    }
}
<commit_msg>Current should become the replaced child in order to support replacing siblings via changes correctly.<commit_after>use std::rc::*;

use super::address::*;
use super::treenode::*;
use super::basictree::*;

///
/// Represents which of the root's references have changed
///
pub enum TreeChangeType {
    /// The node's child reference has been replaced
    Child,

    /// The node's sibling reference has been replaced
    Sibling
}

///
/// A change represents an alteration to the tree
///
pub struct TreeChange {
    /// The address of the node matching the root of the change
    ///
    /// The address is relative to an imaginary node that is the parent of the real root node. This makes it possible to
    /// replace the entire tree by setting this to `TreeAddress::Here` and the change_type to `TreeChangeType::Child`.
    /// The real root node can be addressed at `TreeAddress::ChildAtIndex(0)`
    root: TreeAddress,

    /// Which of the root's references have changed
    change_type: TreeChangeType,

    /// The tree that should replace the changed reference. The last node in this tree (depth-first) will be given the same sibling as the last node in the replacement range
    replacement_tree: Option<TreeRef>
}

impl TreeChange {
    ///
    /// Creates a new tree change
    ///
    pub fn new<TAddress: ToTreeAddress, TNode: ToTreeNode>(root: &TAddress, change_type: TreeChangeType, replacement_tree: Option<&TNode>) -> TreeChange {
        TreeChange { root: root.to_tree_address(), change_type: change_type, replacement_tree: replacement_tree.map(|x| x.to_tree_node()) }
    }

    ///
    /// Performs a replacement on a basic tree node
    ///
    #[inline]
    fn perform_replacement(node: &BasicTree, change_type: &TreeChangeType, replacement_tree: &Option<TreeRef>) {
        match *change_type {
            TreeChangeType::Child => {
                match *replacement_tree {
                    Some(ref new_child)     => node.set_child_ref(new_child.clone()),
                    None                    => node.clear_child()
                }
            },

            TreeChangeType::Sibling => {
                match *replacement_tree {
                    Some(ref new_sibling)   => node.set_sibling_ref(new_sibling.clone()),
                    None                    => node.clear_child()
                }
            }
        }
    }

    ///
    /// Applies a change to a tree, generating a new tree
    ///
    fn perform_apply(original_tree: &TreeRef, address: &TreeAddress, change_type: &TreeChangeType, replacement_tree: &Option<TreeRef>) -> TreeRef {
        match *address {
            TreeAddress::Here => {
                let new_node = BasicTree::from(original_tree);
                TreeChange::perform_replacement(&new_node, change_type, replacement_tree);

                Rc::new(new_node)
            },

            TreeAddress::ChildAtIndex(child_index, ref child_address) => {
                // Copy the siblings into a stack
                let mut siblings    = vec![];
                let mut current     = original_tree.get_child_ref();

                for _ in 0..child_index {
                    siblings.push(current.clone().unwrap_or_else(|| Rc::new(BasicTree::new("", ()))));

                    current = current.and_then(|x| x.get_sibling_ref());
                }

                // Replace the child matching this item
                let child_tree  = current.clone().unwrap_or_else(|| Rc::new(BasicTree::new("", ())));
                let new_child   = TreeChange::perform_apply(&child_tree, &*child_address, change_type, replacement_tree);

                current = Some(new_child);

                // Pop siblings to generate the new child item
                while let Some(sibling) = siblings.pop() {
                    match current {
                        Some(next_sibling)  => current = Some(Rc::new(BasicTree::from_with_sibling(sibling, next_sibling))),
                        None                => current = Some(sibling),
                    }
                }

                Rc::new(BasicTree::from_with_child(original_tree, current.unwrap()))
            },

            TreeAddress::ChildWithTag(ref child_tag, ref child_address) => {
                let child_tree  = original_tree.get_child_at(&**child_tag);
                let new_child   = TreeChange::perform_apply(&child_tree, &*child_address, change_type, replacement_tree);

                Rc::new(BasicTree::from_with_child(original_tree, new_child))
            }
        }
    }

    ///
    /// Creates a new tree from an old tree with this change added to it
    ///
    #[inline]
    pub fn apply(&self, original_tree: &TreeRef) -> TreeRef {
        // For simplicity, we actualise the 'imaginary' root
        let imaginary_root      = tree!("", original_tree).to_tree_node();
        let new_imaginary_root  = TreeChange::perform_apply(&imaginary_root, &self.root, &self.change_type, &self.replacement_tree);

        // Result is the child of the imaginary node (or an empty node if the entire tree is deleted)
        new_imaginary_root.get_child_ref().unwrap_or_else(|| "".to_tree_node())
    }
}

#[cfg(test)]
mod change_tests {
    use super::super::super::tree::*;

    #[test]
    fn can_apply_simple_change_tagged() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&("test", "one"), TreeChangeType::Sibling, Some(&("replaced", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(changed_tree.get_child_ref_at("one").unwrap().get_value().to_int(0) == 1);
        assert!(changed_tree.get_child_ref_at("replaced").unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at("replaced").unwrap().get_sibling_ref().is_none());
        assert!(changed_tree.get_child_ref_at("two").is_none());
        assert!(changed_tree.get_child_ref_at("three").is_none());
    }

    #[test]
    fn can_apply_simple_change_indexed() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&(0, 1), TreeChangeType::Sibling, Some(&("replaced", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(changed_tree.get_child_ref_at(0).unwrap().get_value().to_int(0) == 1);
        assert!(changed_tree.get_child_ref_at(1).unwrap().get_value().to_int(0) == 2);
        assert!(changed_tree.get_child_ref_at(2).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at(2).unwrap().get_sibling_ref().is_none());
        assert!(changed_tree.get_child_ref_at(3).is_none());
    }

    #[test]
    fn can_apply_child_change_tagged() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&("test", "two").to_tree_address(), TreeChangeType::Child, Some(&("new_child", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(!changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).is_none());

        assert!(changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at(("two", "new_child").to_tree_address()).unwrap().get_sibling_ref().is_none());

        assert!(!changed_tree.get_child_ref_at("one").is_none());
        assert!(!changed_tree.get_child_ref_at("two").is_none());
        assert!(!changed_tree.get_child_ref_at("three").is_none());
        assert!(changed_tree.get_child_ref_at("two").unwrap().get_value().to_int(0) == 2);
    }

    #[test]
    fn can_apply_child_change_indexed() {
        let initial_tree    = tree!("test", ("one", 1), ("two", 2), ("three", 3));
        let change_two      = TreeChange::new(&(0, 1).to_tree_address(), TreeChangeType::Child, Some(&("new_child", 4)));
        let changed_tree    = change_two.apply(&initial_tree);

        assert!(!changed_tree.get_child_ref_at((1, 0).to_tree_address()).is_none());

        assert!(changed_tree.get_child_ref_at((1, 0).to_tree_address()).unwrap().get_value().to_int(0) == 4);
        assert!(changed_tree.get_child_ref_at((1, 0).to_tree_address()).unwrap().get_sibling_ref().is_none());

        assert!(!changed_tree.get_child_ref_at(0).is_none());
        assert!(!changed_tree.get_child_ref_at(1).is_none());
        assert!(!changed_tree.get_child_ref_at(2).is_none());
        assert!(changed_tree.get_child_ref_at(1).unwrap().get_value().to_int(0) == 2);
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::fmt;
use std::process;
use std::path::{Path, PathBuf};
use std::io::{BufReader, BufRead};
use std::borrow::Cow;
use std::collections::HashMap;
use libc::getpid;

use serde_json;
use plist::serde::deserialize;
#[cfg(target_os="macos")]
use osascript;
#[cfg(target_os="macos")]
use unix_daemonize::{daemonize_redirect, ChdirMode};
#[cfg(target_os="macos")]
use mac_process_info;
use regex::Regex;

use prelude::*;
use config::Config;
use utils::{TempFile, expand_vars};

#[derive(Deserialize, Debug)]
pub struct InfoPlist {
    #[serde(rename="CFBundleName")]
    name: String,
    #[serde(rename="CFBundleIdentifier")]
    bundle_id: String,
    #[serde(rename="CFBundleShortVersionString")]
    version: String,
    #[serde(rename="CFBundleVersion")]
    build: String,
}

#[derive(Deserialize, Debug)]
pub struct XcodeProjectInfo {
    targets: Vec<String>,
    schemes: Vec<String>,
    configurations: Vec<String>,
    name: String,
    #[serde(default="PathBuf::new")]
    path: PathBuf,
}

impl fmt::Display for InfoPlist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.name(), &self.version)
    }
}

fn var_from_env(name: &str) -> String {
    env::var(name).unwrap_or("".into())
}

pub fn expand_xcodevars<'a, F: Fn(&str) -> String>(s: &'a str, f: F) -> Cow<'a, str> {
    lazy_static! {
        static ref SEP_RE: Regex = Regex::new(r"[\s/]+").unwrap();
    }
    expand_vars(s, |key| {
        if key == "" {
            return "".into();
        }
        let mut iter = key.splitn(2, ':');
        let value = f(iter.next().unwrap());
        match iter.next() {
            Some("rfc1034identifier") => {
                SEP_RE.replace_all(&value, "-").into_owned()
            },
            Some("identifier") => {
                SEP_RE.replace_all(&value, "_").into_owned()
            },
            None | Some(_) => value
        }
    })
}

impl XcodeProjectInfo {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<XcodeProjectInfo> {
        #[derive(Deserialize)]
        struct Output {
            project: XcodeProjectInfo,
        }
        let p = process::Command::new("xcodebuild")
            .arg("-list")
            .arg("-json")
            .arg("-project")
            .arg(path.as_ref().as_os_str())
            .output()?;
        let mut rv: Output = serde_json::from_slice(&p.stdout)?;
        rv.project.path = path.as_ref().canonicalize()?;
        Ok(rv.project)
    }

    pub fn get_build_vars(&self, target: &str, configuration: &str)
        -> Result<HashMap<String, String>>
    {
        let mut rv = HashMap::new();
        let p = process::Command::new("xcodebuild")
            .arg("-showBuildSettings")
            .arg("-project")
            .arg(&self.path)
            .arg("-target")
            .arg(target)
            .arg("-configuration")
            .arg(configuration)
            .output()?;
        for line_rv in p.stdout.lines() {
            let line = line_rv?;
            if line.starts_with("    ") {
                let mut sep = line[4..].splitn(2, " = ");
                if_chain! {
                    if let Some(key) = sep.next();
                    if let Some(value) = sep.next();
                    then {
                        rv.insert(key.to_owned(), value.to_owned());
                    }
                }
            }
        }
        Ok(rv)
    }

    /// Return the first target
    pub fn get_first_target(&self) -> Option<&str> {
        if !self.targets.is_empty() {
            Some(&self.targets[0])
        } else {
            None
        }
    }

    /// Returns the config with a certain name
    pub fn get_configuration(&self, name: &str) -> Option<&str> {
        let name = name.to_lowercase();
        for cfg in &self.configurations {
            if cfg.to_lowercase() == name {
                return Some(&cfg);
            }
        }
        None
    }

    /// Returns the release name for the given target.
    pub fn get_release_name(&self, target: &str, configuration: &str,
                            version_override: Option<&str>)
        -> Result<String>
    {
        let vars = self.get_build_vars(target, configuration)?;
        let plist_path = vars.get("INFOPLIST_FILE").map(|plist| {
            self.path.parent().unwrap().join(plist)
        }).ok_or(Error::from("Could not find info.plist"))?;
        let plist = InfoPlist::from_path(&plist_path)?;
        Ok(format!("{}-{}",
                   plist.bundle_id_from_vars(&vars),
                   version_override.unwrap_or_else(|| plist.version())))
    }
}

impl InfoPlist {

    pub fn discover_from_env() -> Result<Option<InfoPlist>> {
        // we only permit info plist discovery if we get an xcode version
        // (eg: we were launched from xcode)
        if env::var("XCODE_VERSION_ACTUAL").is_err() {
            Ok(None)
        } else if let Ok(path) = env::var("INFOPLIST_FILE") {
            Ok(Some(InfoPlist::from_path(path)?))
        } else {
            Ok(None)
        }
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<InfoPlist> {
        let f = fs::File::open(path.as_ref()).chain_err(||
            Error::from("Could not open Info.plist file"))?;
        let mut rdr = BufReader::new(f);
        Ok(deserialize(&mut rdr).chain_err(||
            Error::from("Could not parse Info.plist file"))?)
    }

    pub fn version(&self) -> &str {
        &self.version
    }

    pub fn build(&self) -> &str {
        &self.build
    }

    pub fn name<'a>(&'a self) -> Cow<'a, str> {
        expand_xcodevars(&self.name, var_from_env)
    }

    pub fn bundle_id<'a>(&'a self) -> Cow<'a, str> {
        expand_xcodevars(&self.bundle_id, var_from_env)
    }

    pub fn bundle_id_from_vars<'a>(&'a self, vars: &HashMap<String, String>)
        -> Cow<'a, str>
    {
        expand_xcodevars(&self.bundle_id, |var: &str| -> String {
            vars.get(var).map(|x| x.to_string()).unwrap_or(var_from_env(var))
        })
    }
}

/// Helper struct that allows the current execution to detach from
/// the xcode console and continue in the background.  This becomes
/// a dummy shim for non xcode runs or platforms.
pub struct MayDetach<'a> {
    config: &'a Config,
    output_file: Option<TempFile>,
    #[allow(dead_code)]
    task_name: &'a str,
}

impl<'a> MayDetach<'a> {
    fn new(config: &'a Config, task_name: &'a str) -> MayDetach<'a> {
        MayDetach {
            config: config,
            output_file: None,
            task_name: task_name,
        }
    }

    /// Returns true if we are deteached from xcode
    pub fn is_detached(&self) -> bool {
        self.output_file.is_some()
    }

    /// If we are launched from xcode this detaches us from the xcode console
    /// and continues execution in the background.  From this moment on output
    /// is captured and the user is notified with notifications.
    #[cfg(target_os="macos")]
    pub fn may_detach(&mut self) -> Result<bool> {
        if !launched_from_xcode() {
            return Ok(false);
        }

        println!("Continuing in background.");
        show_notification(self.config, "Sentry", &format!("{} starting", self.task_name))?;
        let output_file = TempFile::new()?;
        daemonize_redirect(Some(output_file.path()),
                           Some(output_file.path()),
                           ChdirMode::NoChdir).unwrap();
        self.output_file = Some(output_file);
        Ok(true)
    }

    /// For non mac platforms this just never detaches.
    #[cfg(not(target_os="macos"))]
    pub fn may_detach(&mut self) -> Result<bool> {
        Ok(false)
    }

    /// Wraps the execution of a code block.  Does not detach until someone
    /// calls into `may_detach`.
    #[cfg(target_os="macos")]
    pub fn wrap<T, F: FnOnce(&mut MayDetach) -> Result<T>>(
        config: &Config, task_name: &'a str, f: F) -> Result<T>
    {
        use std::time::Duration;
        use std::thread;
        use open;
        use utils::print_error;

        let mut md = MayDetach::new(config, task_name);
        match f(&mut md) {
            Ok(x) => {
                md.show_done()?;
                Ok(x)
            }
            Err(err) => {
                if let Some(ref output_file) = md.output_file {
                    print_error(&err);
                    if md.show_critical_info()? {
                        open::that(&output_file.path())?;
                        thread::sleep(Duration::from_millis(5000));
                    }
                }
                Err(err)
            }
        }
    }

    /// Dummy wrap call that never detaches for non mac platforms.
    #[cfg(not(target_os="macos"))]
    pub fn wrap<T, F: FnOnce(&mut MayDetach) -> Result<T>>(task_name: &'a str, f: F) -> Result<T> {
        f(&mut MayDetach::new(task_name))
    }

    #[cfg(target_os="macos")]
    fn show_critical_info(&self) -> Result<bool> {
        show_critical_info(
            &format!("{} failed", self.task_name),
            "The Sentry build step failed while running in the background. \
             You can ignore this error or view details to attempt to resolve \
             it. Ignoring it might cause your crashes not to be handled \
             properly.")
    }

    #[cfg(target_os="macos")]
    fn show_done(&self) -> Result<()> {
        if self.is_detached() {
            show_notification(self.config, "Sentry", &format!("{} finished", self.task_name))?;
        }
        Ok(())
    }
}

/// Returns true if we were invoked from xcode
#[cfg(target_os="macos")]
pub fn launched_from_xcode() -> bool {
    if env::var("XCODE_VERSION_ACTUAL").is_err() {
        return false;
    }

    let mut pid = unsafe { getpid() as u32 };
    while let Some(parent) = mac_process_info::get_parent_pid(pid) {
        if parent == 1 {
            break;
        }
        if let Ok(name) = mac_process_info::get_process_name(parent) {
            if &name == "Xcode" {
                return true;
            }
        }
        pid = parent;
    }

    false
}

/// Shows a dialog in xcode and blocks.  The dialog will have a title and a
/// message as well as the buttons "Show details" and "Ignore".  Returns
/// `true` if the `show details` button has been pressed.
#[cfg(target_os="macos")]
pub fn show_critical_info(title: &str, msg: &str) -> Result<bool> {
    lazy_static! {
        static ref SCRIPT: osascript::JavaScript = osascript::JavaScript::new("
            var App = Application('XCode');
            App.includeStandardAdditions = true;
            return App.displayAlert($params.title, {
                message: $params.message,
                as: \"critical\",
                buttons: [\"Show details\", \"Ignore\"]
            });
        ");
    }

    #[derive(Serialize)]
    struct AlertParams<'a> {
        title: &'a str,
        message: &'a str,
    }

    #[derive(Debug, Deserialize)]
    struct AlertResult {
        #[serde(rename="buttonReturned")]
        button: String,
    }

    let rv: AlertResult = SCRIPT.execute_with_params(AlertParams {
        title: title,
        message: msg,
    }).chain_err(|| "Failed to display Xcode dialog")?;

    Ok(&rv.button != "Ignore")
}

/// Shows a notification in xcode
#[cfg(target_os="macos")]
pub fn show_notification(config: &Config, title: &str, msg: &str) -> Result<()> {
    lazy_static! {
        static ref SCRIPT: osascript::JavaScript = osascript::JavaScript::new("
            var App = Application.currentApplication();
            App.includeStandardAdditions = true;
            App.displayNotification($params.message, {
                withTitle: $params.title
            });
        ");
    }

    if !config.show_notifications()? {
        return Ok(());
    }

    #[derive(Serialize)]
    struct NotificationParams<'a> {
        title: &'a str,
        message: &'a str,
    }

    Ok(SCRIPT.execute_with_params(NotificationParams {
        title: title,
        message: msg,
    }).chain_err(|| "Failed to display Xcode notification")?)
}

#[test]
fn test_expansion() {
    fn f(name: &str) -> String {
        if name == "FOO_BAR" {
            "foo bar baz / blah".into()
        } else {
            "".into()
        }
    }
    assert_eq!(expand_xcodevars("A$(FOO_BAR:rfc1034identifier)B", f).to_string(),
               "Afoo-bar-baz-blahB".to_string());
    assert_eq!(expand_xcodevars("A$(FOO_BAR:identifier)B", f).to_string(),
               "Afoo_bar_baz_blahB".to_string());
    assert_eq!(expand_xcodevars("A${FOO_BAR:identifier}B", f).to_string(),
               "Afoo_bar_baz_blahB".to_string());
}
<commit_msg>Fixed windows build<commit_after>use std::fs;
use std::env;
use std::fmt;
use std::process;
use std::path::{Path, PathBuf};
use std::io::{BufReader, BufRead};
use std::borrow::Cow;
use std::collections::HashMap;
use libc::getpid;

use serde_json;
use plist::serde::deserialize;
#[cfg(target_os="macos")]
use osascript;
#[cfg(target_os="macos")]
use unix_daemonize::{daemonize_redirect, ChdirMode};
#[cfg(target_os="macos")]
use mac_process_info;
use regex::Regex;

use prelude::*;
use config::Config;
use utils::{TempFile, expand_vars};

#[derive(Deserialize, Debug)]
pub struct InfoPlist {
    #[serde(rename="CFBundleName")]
    name: String,
    #[serde(rename="CFBundleIdentifier")]
    bundle_id: String,
    #[serde(rename="CFBundleShortVersionString")]
    version: String,
    #[serde(rename="CFBundleVersion")]
    build: String,
}

#[derive(Deserialize, Debug)]
pub struct XcodeProjectInfo {
    targets: Vec<String>,
    schemes: Vec<String>,
    configurations: Vec<String>,
    name: String,
    #[serde(default="PathBuf::new")]
    path: PathBuf,
}

impl fmt::Display for InfoPlist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({})", self.name(), &self.version)
    }
}

fn var_from_env(name: &str) -> String {
    env::var(name).unwrap_or("".into())
}

pub fn expand_xcodevars<'a, F: Fn(&str) -> String>(s: &'a str, f: F) -> Cow<'a, str> {
    lazy_static! {
        static ref SEP_RE: Regex = Regex::new(r"[\s/]+").unwrap();
    }
    expand_vars(s, |key| {
        if key == "" {
            return "".into();
        }
        let mut iter = key.splitn(2, ':');
        let value = f(iter.next().unwrap());
        match iter.next() {
            Some("rfc1034identifier") => {
                SEP_RE.replace_all(&value, "-").into_owned()
            },
            Some("identifier") => {
                SEP_RE.replace_all(&value, "_").into_owned()
            },
            None | Some(_) => value
        }
    })
}

impl XcodeProjectInfo {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<XcodeProjectInfo> {
        #[derive(Deserialize)]
        struct Output {
            project: XcodeProjectInfo,
        }
        let p = process::Command::new("xcodebuild")
            .arg("-list")
            .arg("-json")
            .arg("-project")
            .arg(path.as_ref().as_os_str())
            .output()?;
        let mut rv: Output = serde_json::from_slice(&p.stdout)?;
        rv.project.path = path.as_ref().canonicalize()?;
        Ok(rv.project)
    }

    pub fn get_build_vars(&self, target: &str, configuration: &str)
        -> Result<HashMap<String, String>>
    {
        let mut rv = HashMap::new();
        let p = process::Command::new("xcodebuild")
            .arg("-showBuildSettings")
            .arg("-project")
            .arg(&self.path)
            .arg("-target")
            .arg(target)
            .arg("-configuration")
            .arg(configuration)
            .output()?;
        for line_rv in p.stdout.lines() {
            let line = line_rv?;
            if line.starts_with("    ") {
                let mut sep = line[4..].splitn(2, " = ");
                if_chain! {
                    if let Some(key) = sep.next();
                    if let Some(value) = sep.next();
                    then {
                        rv.insert(key.to_owned(), value.to_owned());
                    }
                }
            }
        }
        Ok(rv)
    }

    /// Return the first target
    pub fn get_first_target(&self) -> Option<&str> {
        if !self.targets.is_empty() {
            Some(&self.targets[0])
        } else {
            None
        }
    }

    /// Returns the config with a certain name
    pub fn get_configuration(&self, name: &str) -> Option<&str> {
        let name = name.to_lowercase();
        for cfg in &self.configurations {
            if cfg.to_lowercase() == name {
                return Some(&cfg);
            }
        }
        None
    }

    /// Returns the release name for the given target.
    pub fn get_release_name(&self, target: &str, configuration: &str,
                            version_override: Option<&str>)
        -> Result<String>
    {
        let vars = self.get_build_vars(target, configuration)?;
        let plist_path = vars.get("INFOPLIST_FILE").map(|plist| {
            self.path.parent().unwrap().join(plist)
        }).ok_or(Error::from("Could not find info.plist"))?;
        let plist = InfoPlist::from_path(&plist_path)?;
        Ok(format!("{}-{}",
                   plist.bundle_id_from_vars(&vars),
                   version_override.unwrap_or_else(|| plist.version())))
    }
}

impl InfoPlist {

    pub fn discover_from_env() -> Result<Option<InfoPlist>> {
        // we only permit info plist discovery if we get an xcode version
        // (eg: we were launched from xcode)
        if env::var("XCODE_VERSION_ACTUAL").is_err() {
            Ok(None)
        } else if let Ok(path) = env::var("INFOPLIST_FILE") {
            Ok(Some(InfoPlist::from_path(path)?))
        } else {
            Ok(None)
        }
    }

    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<InfoPlist> {
        let f = fs::File::open(path.as_ref()).chain_err(||
            Error::from("Could not open Info.plist file"))?;
        let mut rdr = BufReader::new(f);
        Ok(deserialize(&mut rdr).chain_err(||
            Error::from("Could not parse Info.plist file"))?)
    }

    pub fn version(&self) -> &str {
        &self.version
    }

    pub fn build(&self) -> &str {
        &self.build
    }

    pub fn name<'a>(&'a self) -> Cow<'a, str> {
        expand_xcodevars(&self.name, var_from_env)
    }

    pub fn bundle_id<'a>(&'a self) -> Cow<'a, str> {
        expand_xcodevars(&self.bundle_id, var_from_env)
    }

    pub fn bundle_id_from_vars<'a>(&'a self, vars: &HashMap<String, String>)
        -> Cow<'a, str>
    {
        expand_xcodevars(&self.bundle_id, |var: &str| -> String {
            vars.get(var).map(|x| x.to_string()).unwrap_or(var_from_env(var))
        })
    }
}

/// Helper struct that allows the current execution to detach from
/// the xcode console and continue in the background.  This becomes
/// a dummy shim for non xcode runs or platforms.
pub struct MayDetach<'a> {
    config: &'a Config,
    output_file: Option<TempFile>,
    #[allow(dead_code)]
    task_name: &'a str,
}

impl<'a> MayDetach<'a> {
    fn new(config: &'a Config, task_name: &'a str) -> MayDetach<'a> {
        MayDetach {
            config: config,
            output_file: None,
            task_name: task_name,
        }
    }

    /// Returns true if we are deteached from xcode
    pub fn is_detached(&self) -> bool {
        self.output_file.is_some()
    }

    /// If we are launched from xcode this detaches us from the xcode console
    /// and continues execution in the background.  From this moment on output
    /// is captured and the user is notified with notifications.
    #[cfg(target_os="macos")]
    pub fn may_detach(&mut self) -> Result<bool> {
        if !launched_from_xcode() {
            return Ok(false);
        }

        println!("Continuing in background.");
        show_notification(self.config, "Sentry", &format!("{} starting", self.task_name))?;
        let output_file = TempFile::new()?;
        daemonize_redirect(Some(output_file.path()),
                           Some(output_file.path()),
                           ChdirMode::NoChdir).unwrap();
        self.output_file = Some(output_file);
        Ok(true)
    }

    /// For non mac platforms this just never detaches.
    #[cfg(not(target_os="macos"))]
    pub fn may_detach(&mut self) -> Result<bool> {
        Ok(false)
    }

    /// Wraps the execution of a code block.  Does not detach until someone
    /// calls into `may_detach`.
    #[cfg(target_os="macos")]
    pub fn wrap<T, F: FnOnce(&mut MayDetach) -> Result<T>>(
        config: &Config, task_name: &'a str, f: F) -> Result<T>
    {
        use std::time::Duration;
        use std::thread;
        use open;
        use utils::print_error;

        let mut md = MayDetach::new(config, task_name);
        match f(&mut md) {
            Ok(x) => {
                md.show_done()?;
                Ok(x)
            }
            Err(err) => {
                if let Some(ref output_file) = md.output_file {
                    print_error(&err);
                    if md.show_critical_info()? {
                        open::that(&output_file.path())?;
                        thread::sleep(Duration::from_millis(5000));
                    }
                }
                Err(err)
            }
        }
    }

    /// Dummy wrap call that never detaches for non mac platforms.
    #[cfg(not(target_os="macos"))]
    pub fn wrap<T, F: FnOnce(&mut MayDetach) -> Result<T>>(
        config: &Config, task_name: &'a str, f: F) -> Result<T> {
        f(&mut MayDetach::new(config, task_name))
    }

    #[cfg(target_os="macos")]
    fn show_critical_info(&self) -> Result<bool> {
        show_critical_info(
            &format!("{} failed", self.task_name),
            "The Sentry build step failed while running in the background. \
             You can ignore this error or view details to attempt to resolve \
             it. Ignoring it might cause your crashes not to be handled \
             properly.")
    }

    #[cfg(target_os="macos")]
    fn show_done(&self) -> Result<()> {
        if self.is_detached() {
            show_notification(self.config, "Sentry", &format!("{} finished", self.task_name))?;
        }
        Ok(())
    }
}

/// Returns true if we were invoked from xcode
#[cfg(target_os="macos")]
pub fn launched_from_xcode() -> bool {
    if env::var("XCODE_VERSION_ACTUAL").is_err() {
        return false;
    }

    let mut pid = unsafe { getpid() as u32 };
    while let Some(parent) = mac_process_info::get_parent_pid(pid) {
        if parent == 1 {
            break;
        }
        if let Ok(name) = mac_process_info::get_process_name(parent) {
            if &name == "Xcode" {
                return true;
            }
        }
        pid = parent;
    }

    false
}

/// Shows a dialog in xcode and blocks.  The dialog will have a title and a
/// message as well as the buttons "Show details" and "Ignore".  Returns
/// `true` if the `show details` button has been pressed.
#[cfg(target_os="macos")]
pub fn show_critical_info(title: &str, msg: &str) -> Result<bool> {
    lazy_static! {
        static ref SCRIPT: osascript::JavaScript = osascript::JavaScript::new("
            var App = Application('XCode');
            App.includeStandardAdditions = true;
            return App.displayAlert($params.title, {
                message: $params.message,
                as: \"critical\",
                buttons: [\"Show details\", \"Ignore\"]
            });
        ");
    }

    #[derive(Serialize)]
    struct AlertParams<'a> {
        title: &'a str,
        message: &'a str,
    }

    #[derive(Debug, Deserialize)]
    struct AlertResult {
        #[serde(rename="buttonReturned")]
        button: String,
    }

    let rv: AlertResult = SCRIPT.execute_with_params(AlertParams {
        title: title,
        message: msg,
    }).chain_err(|| "Failed to display Xcode dialog")?;

    Ok(&rv.button != "Ignore")
}

/// Shows a notification in xcode
#[cfg(target_os="macos")]
pub fn show_notification(config: &Config, title: &str, msg: &str) -> Result<()> {
    lazy_static! {
        static ref SCRIPT: osascript::JavaScript = osascript::JavaScript::new("
            var App = Application.currentApplication();
            App.includeStandardAdditions = true;
            App.displayNotification($params.message, {
                withTitle: $params.title
            });
        ");
    }

    if !config.show_notifications()? {
        return Ok(());
    }

    #[derive(Serialize)]
    struct NotificationParams<'a> {
        title: &'a str,
        message: &'a str,
    }

    Ok(SCRIPT.execute_with_params(NotificationParams {
        title: title,
        message: msg,
    }).chain_err(|| "Failed to display Xcode notification")?)
}

#[test]
fn test_expansion() {
    fn f(name: &str) -> String {
        if name == "FOO_BAR" {
            "foo bar baz / blah".into()
        } else {
            "".into()
        }
    }
    assert_eq!(expand_xcodevars("A$(FOO_BAR:rfc1034identifier)B", f).to_string(),
               "Afoo-bar-baz-blahB".to_string());
    assert_eq!(expand_xcodevars("A$(FOO_BAR:identifier)B", f).to_string(),
               "Afoo_bar_baz_blahB".to_string());
    assert_eq!(expand_xcodevars("A${FOO_BAR:identifier}B", f).to_string(),
               "Afoo_bar_baz_blahB".to_string());
}
<|endoftext|>


<commit_before>
use super::IndexBufferHandle;
use super::VertexBufferHandle;

pub enum AttributeType {
    AttributeByte,
    AttributeUnsignedByte,
    AttributeShort,
    AttributeUnsignedShort,
    AttributeInt,
    AttributeUnsignedInt,
    AttributeHalfFloat,
    AttributeFloat,
    AttributeDouble,
    AttributeInt2101010Rev,
    AttributeUnsignedInt2101010Rev
}

pub struct VertexAttribute {
    index: uint,
    size: uint,
    attribute_type: AttributeType,
    normalized: bool,
    stride: uint,
    offset: uint,
    vertex_buffer: VertexBufferHandle
}

pub struct VertexArray {
    vertex_attributes: Vec<VertexAttribute>,
    index_buffer: Option<IndexBufferHandle>
}

impl VertexArray {
    pub fn new(attributes: &[VertexAttribute], index_buffer: Option<IndexBufferHandle>) -> VertexArray {
        VertexArray { vertex_attributes: Vec::new(), index_buffer: index_buffer }
    }
}<commit_msg>Add quick sketch of convenience constructor for VAO, now commented.<commit_after>
use super::IndexBufferHandle;
use super::VertexBufferHandle;

pub enum AttributeType {
    AttributeByte,
    AttributeUnsignedByte,
    AttributeShort,
    AttributeUnsignedShort,
    AttributeInt,
    AttributeUnsignedInt,
    AttributeHalfFloat,
    AttributeFloat,
    AttributeDouble,
    AttributeInt2101010Rev,
    AttributeUnsignedInt2101010Rev
}

pub struct VertexAttribute {
    index: uint,
    size: uint,
    attribute_type: AttributeType,
    normalized: bool,
    stride: uint,
    offset: uint,
    vertex_buffer: VertexBufferHandle
}

// pub type QuickVertexAttribute (u8, AttributeType, bool)

pub struct VertexArray {
    vertex_attributes: Vec<VertexAttribute>,
    index_buffer: Option<IndexBufferHandle>
}

impl VertexArray {
    pub fn new(attributes: &[VertexAttribute], index_buffer: Option<IndexBufferHandle>) -> VertexArray {
        VertexArray { vertex_attributes: Vec::new(), index_buffer: index_buffer }
    }

    /* pub fn new_single_vbo(attributes: &[QuickVertexAttribute],
            vertex_buffer: VertexBufferHandle,
            index_buffer: Option<IndexBufferHandle>) -> VertexArray {
    } */
}<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::fmt::{self, Debug};
use std::sync::{Condvar as StdCondvar, MutexGuard};

/// A Condition Variable.
#[derive(Default)]
pub struct Condvar {
    std: StdCondvar,
}

impl Condvar {
    /// Creates a new condvar that is ready to be waited on.
    pub fn new() -> Condvar {
        Condvar {
            std: StdCondvar::new(),
        }
    }

    /// Waits on a condvar, blocking the current thread until it is notified.
    pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
        match self.std.wait(guard) {
            Ok(guard) => guard,
            Err(_) => panic!("condvar is poisoned"),
        }
    }

    /// Notifies one thread blocked by this condvar.
    pub fn notify_one(&self) {
        self.std.notify_one();
    }

    /// Notifies all threads blocked by this condvar.
    pub fn notify_all(&self) {
        self.std.notify_all();
    }
}

impl Debug for Condvar {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.std, formatter)
    }
}
<commit_msg>sync: add wait_timeout method to condvar wrapper<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::fmt::{self, Debug};
use std::sync::{Condvar as StdCondvar, MutexGuard, WaitTimeoutResult};
use std::time::Duration;

/// A Condition Variable.
#[derive(Default)]
pub struct Condvar {
    std: StdCondvar,
}

impl Condvar {
    /// Creates a new condvar that is ready to be waited on.
    pub fn new() -> Condvar {
        Condvar {
            std: StdCondvar::new(),
        }
    }

    /// Waits on a condvar, blocking the current thread until it is notified.
    pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
        match self.std.wait(guard) {
            Ok(guard) => guard,
            Err(_) => panic!("condvar is poisoned"),
        }
    }

    /// Waits on a condvar, blocking the current thread until it is notified
    /// or the specified duration has elapsed.
    pub fn wait_timeout<'a, T>(
        &self,
        guard: MutexGuard<'a, T>,
        dur: Duration,
    ) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
        match self.std.wait_timeout(guard, dur) {
            Ok(result) => result,
            Err(_) => panic!("condvar is poisoned"),
        }
    }

    /// Notifies one thread blocked by this condvar.
    pub fn notify_one(&self) {
        self.std.notify_one();
    }

    /// Notifies all threads blocked by this condvar.
    pub fn notify_all(&self) {
        self.std.notify_all();
    }
}

impl Debug for Condvar {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.std, formatter)
    }
}
<|endoftext|>


<commit_before>use core::{Apply, Bindings, BindingsValue, Unify};
use itertools::Itertools;
use itertools::FoldWhile::{Continue, Done};
use std;
use std::marker::PhantomData;

#[cfg(test)]
mod tests;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnificationIndex {
    Init,
    Actor(usize),
    Datum(usize),
    Exhausted,
}

impl std::fmt::Display for UnificationIndex {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            UnificationIndex::Init => write!(f, "Init"),
            UnificationIndex::Actor(idx) => write!(f, "Actor({})", idx),
            UnificationIndex::Datum(idx) => write!(f, "Datum({})", idx),
            UnificationIndex::Exhausted => write!(f, "Exhausted"),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PlanningConfig {
    pub debug: bool,
    pub max_depth: usize,
    pub reuse_data: bool,
}

fn increment_unification_index(current_unification_index: &UnificationIndex,
                               datum_count: usize,
                               rule_count: usize)
                               -> UnificationIndex {
    let initial_rule_index = if rule_count > 0 {
        UnificationIndex::Actor(0)
    } else {
        UnificationIndex::Exhausted
    };

    let initial_datum_index = if datum_count > 0 {
        UnificationIndex::Datum(0)
    } else {
        initial_rule_index.clone()
    };

    match *current_unification_index {
        UnificationIndex::Exhausted => UnificationIndex::Exhausted,
        UnificationIndex::Init => initial_datum_index.clone(),
        UnificationIndex::Datum(current_idx) => {
            if current_idx + 1 < datum_count {
                UnificationIndex::Datum(current_idx + 1)
            } else {
                initial_rule_index
            }
        }
        UnificationIndex::Actor(current_idx) => {
            if current_idx + 1 < rule_count {
                UnificationIndex::Actor(current_idx + 1)
            } else {
                UnificationIndex::Exhausted
            }
        }
    }
}

/// Determine the first subgoal to increment
pub fn first_subgoal_to_increment(unification_indices: &Vec<UnificationIndex>) -> Option<usize> {
    if unification_indices.is_empty() {
        None
    } else {
        // Check subgoals find index that is in the Init state
        let last_index = unification_indices.len() - 1;
        let mut idx = 0;

        loop {
            if unification_indices[idx] == UnificationIndex::Init {
                return Some(idx);
            }
            if unification_indices[idx] == UnificationIndex::Exhausted {
                if idx == 0 {
                    return None;
                } else {
                    return Some(idx - 1);
                }
            }
            if idx == last_index {
                return Some(idx);
            }
            idx += 1;
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Goal<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> {
    pub pattern: U,
    pub subgoals: Vec<Goal<T, U, A>>,
    pub unification_index: UnificationIndex,
    _a_marker: PhantomData<A>,
    _t_marker: PhantomData<T>,
}

impl<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> Goal<T, U, A> {
    pub fn new(pattern: U, unification_index: UnificationIndex, subgoals: Vec<Self>) -> Self {
        Goal {
            pattern: pattern,
            subgoals: subgoals,
            unification_index: unification_index,
            _a_marker: PhantomData,
            _t_marker: PhantomData,
        }
    }

    pub fn with_pattern(pattern: U) -> Self {
        Goal::new(pattern, UnificationIndex::Init, Vec::new())
    }

    /// Construct a mutated plan
    pub fn increment(&self, data: &Vec<&U>, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Self> {
        let mut goal = self.clone();

        // If there are any subgoals, increment them first
        if !self.subgoals.is_empty() {
            if let Some(subgoals) = self.increment_subgoals(data, rules, snowflake_prefix_id) {
                goal.subgoals = subgoals;
                return Some(goal);
            }
        }

        // If subgoals cannot be incrementped, increment this goal
        loop {
            goal.unification_index = increment_unification_index(&goal.unification_index, data.len(), rules.len());
            match goal.unification_index {
                UnificationIndex::Datum(_idx) => return Some(goal),
                UnificationIndex::Actor(idx) => {
                    if let Some(subgoals) = Self::create_subgoals(&self.pattern, idx, rules, snowflake_prefix_id) {
                        goal.subgoals = subgoals;
                        return Some(goal);
                    }
                }
                // If this goal cannot be incrementped, return None
                UnificationIndex::Exhausted => return None,
                UnificationIndex::Init => panic!("Init after incrementing; this should never happen"),
            }
        }
    }

    /// Determine if the plan is valid
    pub fn satisifed(&self, data: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        println!("#### Checking satisfaction - {}", self);
        match self.unification_index {
            UnificationIndex::Datum(datum_idx) => {
                println!("#### Comparing {} with {}, results: {}",
                         self.pattern,
                         data[datum_idx],
                         self.pattern.unify(data[datum_idx], bindings).is_some());
                self.pattern.unify(data[datum_idx], bindings)
            }
            UnificationIndex::Actor(_actor_idx) => {
                self.subgoals.iter().fold_while(Some(bindings.clone()), |bindings, subgoal| {
                    let bindings = subgoal.satisifed(data, bindings.as_ref().unwrap());
                    if bindings.is_some() {
                        Continue(bindings)
                    } else {
                        Done(None)
                    }
                })
            }
            UnificationIndex::Init => self.pattern.unify(&U::nil(), &bindings),
            UnificationIndex::Exhausted => None,
        }
    }

    pub fn create_subgoals(r_pattern: &U, idx: usize, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Vec<Self>> {
        let rule = rules[idx].snowflake(format!("{}", snowflake_prefix_id));
        rule.r_apply(r_pattern, &Bindings::new()).and_then(|(subgoal_patterns, bindings)| {
            Some(subgoal_patterns.into_iter()
                .map(|pattern| Goal::with_pattern(pattern.apply_bindings(&bindings).unwrap()))
                .collect())
        })
    }

    pub fn increment_subgoals(&self, data: &Vec<&U>, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Vec<Self>> {
        let unification_indices = self.subgoals.iter().map(|sg| sg.unification_index.clone()).collect();
        first_subgoal_to_increment(&unification_indices).and_then(|idx| {
            self.subgoals[idx].increment(data, rules, snowflake_prefix_id).and_then(|new_subgoal| {
                let mut subgoals = self.subgoals.clone();
                subgoals[idx] = new_subgoal;
                Some(subgoals)
            })
        })
    }

    pub fn pprint(&self, ntabs: usize) -> String {
        let tabv: Vec<String> = (0..ntabs).map(|_| "\t".to_string()).collect();
        let tabs = tabv.join("");
        let subgoal_v: Vec<String> = self.subgoals.iter().map(|sg| sg.pprint(ntabs + 1)).collect();
        let subgoal_s = subgoal_v.join("\n");
        format!("{}{} @ {}\n{}",
                tabs,
                self.pattern,
                self.unification_index,
                subgoal_s)
    }
}

impl<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> std::fmt::Display for Goal<T, U, A> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "Goal tree:\n{}", self.pprint(1))
    }
}
<commit_msg>Doh!  Didn't need print statements<commit_after>use core::{Apply, Bindings, BindingsValue, Unify};
use itertools::Itertools;
use itertools::FoldWhile::{Continue, Done};
use std;
use std::marker::PhantomData;

#[cfg(test)]
mod tests;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum UnificationIndex {
    Init,
    Actor(usize),
    Datum(usize),
    Exhausted,
}

impl std::fmt::Display for UnificationIndex {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            UnificationIndex::Init => write!(f, "Init"),
            UnificationIndex::Actor(idx) => write!(f, "Actor({})", idx),
            UnificationIndex::Datum(idx) => write!(f, "Datum({})", idx),
            UnificationIndex::Exhausted => write!(f, "Exhausted"),
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PlanningConfig {
    pub debug: bool,
    pub max_depth: usize,
    pub reuse_data: bool,
}

fn increment_unification_index(current_unification_index: &UnificationIndex,
                               datum_count: usize,
                               rule_count: usize)
                               -> UnificationIndex {
    let initial_rule_index = if rule_count > 0 {
        UnificationIndex::Actor(0)
    } else {
        UnificationIndex::Exhausted
    };

    let initial_datum_index = if datum_count > 0 {
        UnificationIndex::Datum(0)
    } else {
        initial_rule_index.clone()
    };

    match *current_unification_index {
        UnificationIndex::Exhausted => UnificationIndex::Exhausted,
        UnificationIndex::Init => initial_datum_index.clone(),
        UnificationIndex::Datum(current_idx) => {
            if current_idx + 1 < datum_count {
                UnificationIndex::Datum(current_idx + 1)
            } else {
                initial_rule_index
            }
        }
        UnificationIndex::Actor(current_idx) => {
            if current_idx + 1 < rule_count {
                UnificationIndex::Actor(current_idx + 1)
            } else {
                UnificationIndex::Exhausted
            }
        }
    }
}

/// Determine the first subgoal to increment
pub fn first_subgoal_to_increment(unification_indices: &Vec<UnificationIndex>) -> Option<usize> {
    if unification_indices.is_empty() {
        None
    } else {
        // Check subgoals find index that is in the Init state
        let last_index = unification_indices.len() - 1;
        let mut idx = 0;

        loop {
            if unification_indices[idx] == UnificationIndex::Init {
                return Some(idx);
            }
            if unification_indices[idx] == UnificationIndex::Exhausted {
                if idx == 0 {
                    return None;
                } else {
                    return Some(idx - 1);
                }
            }
            if idx == last_index {
                return Some(idx);
            }
            idx += 1;
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Goal<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> {
    pub pattern: U,
    pub subgoals: Vec<Goal<T, U, A>>,
    pub unification_index: UnificationIndex,
    _a_marker: PhantomData<A>,
    _t_marker: PhantomData<T>,
}

impl<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> Goal<T, U, A> {
    pub fn new(pattern: U, unification_index: UnificationIndex, subgoals: Vec<Self>) -> Self {
        Goal {
            pattern: pattern,
            subgoals: subgoals,
            unification_index: unification_index,
            _a_marker: PhantomData,
            _t_marker: PhantomData,
        }
    }

    pub fn with_pattern(pattern: U) -> Self {
        Goal::new(pattern, UnificationIndex::Init, Vec::new())
    }

    /// Construct a mutated plan
    pub fn increment(&self, data: &Vec<&U>, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Self> {
        let mut goal = self.clone();

        // If there are any subgoals, increment them first
        if !self.subgoals.is_empty() {
            if let Some(subgoals) = self.increment_subgoals(data, rules, snowflake_prefix_id) {
                goal.subgoals = subgoals;
                return Some(goal);
            }
        }

        // If subgoals cannot be incrementped, increment this goal
        loop {
            goal.unification_index = increment_unification_index(&goal.unification_index, data.len(), rules.len());
            match goal.unification_index {
                UnificationIndex::Datum(_idx) => return Some(goal),
                UnificationIndex::Actor(idx) => {
                    if let Some(subgoals) = Self::create_subgoals(&self.pattern, idx, rules, snowflake_prefix_id) {
                        goal.subgoals = subgoals;
                        return Some(goal);
                    }
                }
                // If this goal cannot be incrementped, return None
                UnificationIndex::Exhausted => return None,
                UnificationIndex::Init => panic!("Init after incrementing; this should never happen"),
            }
        }
    }

    /// Determine if the plan is valid
    pub fn satisifed(&self, data: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        match self.unification_index {
            UnificationIndex::Datum(datum_idx) => self.pattern.unify(data[datum_idx], bindings),
            UnificationIndex::Actor(_actor_idx) => {
                self.subgoals.iter().fold_while(Some(bindings.clone()), |bindings, subgoal| {
                    let bindings = subgoal.satisifed(data, bindings.as_ref().unwrap());
                    if bindings.is_some() {
                        Continue(bindings)
                    } else {
                        Done(None)
                    }
                })
            }
            UnificationIndex::Init => self.pattern.unify(&U::nil(), &bindings),
            UnificationIndex::Exhausted => None,
        }
    }

    pub fn create_subgoals(r_pattern: &U, idx: usize, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Vec<Self>> {
        let rule = rules[idx].snowflake(format!("{}", snowflake_prefix_id));
        rule.r_apply(r_pattern, &Bindings::new()).and_then(|(subgoal_patterns, bindings)| {
            Some(subgoal_patterns.into_iter()
                .map(|pattern| Goal::with_pattern(pattern.apply_bindings(&bindings).unwrap()))
                .collect())
        })
    }

    pub fn increment_subgoals(&self, data: &Vec<&U>, rules: &Vec<&A>, snowflake_prefix_id: usize) -> Option<Vec<Self>> {
        let unification_indices = self.subgoals.iter().map(|sg| sg.unification_index.clone()).collect();
        first_subgoal_to_increment(&unification_indices).and_then(|idx| {
            self.subgoals[idx].increment(data, rules, snowflake_prefix_id).and_then(|new_subgoal| {
                let mut subgoals = self.subgoals.clone();
                subgoals[idx] = new_subgoal;
                Some(subgoals)
            })
        })
    }

    pub fn pprint(&self, ntabs: usize) -> String {
        let tabv: Vec<String> = (0..ntabs).map(|_| "\t".to_string()).collect();
        let tabs = tabv.join("");
        let subgoal_v: Vec<String> = self.subgoals.iter().map(|sg| sg.pprint(ntabs + 1)).collect();
        let subgoal_s = subgoal_v.join("\n");
        format!("{}{} @ {}\n{}",
                tabs,
                self.pattern,
                self.unification_index,
                subgoal_s)
    }
}

impl<T: BindingsValue, U: Unify<T>, A: Apply<T, U>> std::fmt::Display for Goal<T, U, A> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "Goal tree:\n{}", self.pprint(1))
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use super::es;

#[cfg(target_os = "windows")]
pub const EXE: &str = "exe";

#[cfg(not(target_os = "windows"))]
pub const EXE: &str = "";

#[cfg(target_os = "windows")]
pub const OPEN: &str = "start";

#[cfg(target_os = "macos")]
pub const OPEN: &str = "open";

#[cfg(not(any(target_os = "macos", target_os = "windows")))]
pub const OPEN: &str = "xdg-open";

pub fn open(p: &Path) {
    es::shell(&format!("{} {:?}",OPEN,p));
}

<commit_msg>-e allows usual override with EDITOR/VISUAL and special-cases MacOS<commit_after>use std::path::Path;
use std::env;
use std::process::Command;
use super::es::traits::*;
extern crate open;

#[cfg(target_os = "windows")]
pub const EXE: &str = "exe";

#[cfg(not(target_os = "windows"))]
pub const EXE: &str = "";

pub fn open(p: &Path) {
    open::that(p).or_die("cannot open");
}

pub fn edit(p: &Path) {
    let editor = if let Ok(ed) = env::var("VISUAL") {
        ed
    } else
    if let Ok(ed) = env::var("EDITOR") {
        ed
    } else
    if cfg!(target_os = "macos") {
        "emacs".into()
    } else {
        "open".into()
    };
    if editor == "open" {
        open(p);
    } else {
        Command::new(&editor).arg(&p).status().or_die("Cannot find editor");
    }
}

<|endoftext|>


<commit_before><commit_msg>Fix #304<commit_after><|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use thiserror::Error;

const WRITE_USR_ACCESS: [u8; 4] = [0x30, 0x01, 0xa0, 0x01];

#[derive(Error, Debug, Serialize, Deserialize)]
pub enum Error {
    #[error("Command {0:#x?} not found in bitstream")]
    CommandNotFound(u32),
}

fn find_cmd(bitstream: &[u8], cmd: &[u8; 4]) -> Result<usize> {
    Ok(bitstream
        .chunks(4)
        .enumerate()
        .find(|(_, op)| *op == cmd)
        .ok_or(Error::CommandNotFound(u32::from_be_bytes(*cmd)))?
        .0
        * 4)
}

pub fn usr_access_get(bitstream: &[u8]) -> Result<u32> {
    let i = find_cmd(&bitstream, &WRITE_USR_ACCESS)?;
    if (i + 8) > bitstream.len() {
        return Err(Error::CommandNotFound(u32::from_be_bytes(WRITE_USR_ACCESS)).into());
    }
    let operand = &bitstream[i + 4..i + 8];
    let usr_access = u32::from_be_bytes(operand.try_into()?);
    log::info!("Bitstream file USR_ACCESS value: {:#x}", usr_access);
    Ok(usr_access)
}

<commit_msg>[opentitantool] Add usr_access_set, usr_access_timestamp, rm_crc<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use thiserror::Error;

use chrono::{Datelike, Timelike, Utc};

const WRITE_USR_ACCESS: [u8; 4] = [0x30, 0x01, 0xa0, 0x01];
const WRITE_CRC_REG: [u8; 4] = [0x30, 0x00, 0x00, 0x01];
const NOOP: [u8; 4] = [0x20, 0x00, 0x00, 0x00];

#[derive(Error, Debug, Serialize, Deserialize)]
pub enum Error {
    #[error("Command {0:#x?} not found in bitstream")]
    CommandNotFound(u32),
}

fn find_cmd(bitstream: &[u8], cmd: &[u8; 4]) -> Result<usize> {
    Ok(bitstream
        .chunks(4)
        .enumerate()
        .find(|(_, op)| *op == cmd)
        .ok_or(Error::CommandNotFound(u32::from_be_bytes(*cmd)))?
        .0
        * 4)
}

/// Searches for the following pattern in the bitstream
///
/// 0x30000001 (write the following word to the CRC register and check)
/// 0xXXXXXXXX (value to write to the CRC register)
/// 0x20000000 (NOOP)
/// 0x20000000 (NOOP)
///
/// and replaces it with
///
/// 0x20000000 (NOOP)
/// 0x20000000 (NOOP)
/// 0x20000000 (NOOP)
/// 0x20000000 (NOOP)
///
/// to remove the CRC check during FPGA configuration.
fn remove_crc(bitstream: &mut [u8]) {
    let mut start = 0;
    while start < bitstream.len() {
        match find_cmd(&bitstream[start..], &WRITE_CRC_REG) {
            Ok(mut i) => {
                i += start;
                if (i + 16) <= bitstream.len()
                    && bitstream[i + 8..i + 12] == NOOP
                    && bitstream[i + 12..i + 16] == NOOP
                {
                    log::info!("Replaced WRITE_CRC_REG command at {:#x} with NOOP", i);
                    bitstream[i..i + 4].copy_from_slice(&NOOP);
                    bitstream[i + 4..i + 8].copy_from_slice(&NOOP);
                }
                start = i + 16;
            }
            _ => return,
        }
    }
}

pub fn usr_access_get(bitstream: &[u8]) -> Result<u32> {
    let i = find_cmd(&bitstream, &WRITE_USR_ACCESS)?;
    if (i + 8) > bitstream.len() {
        return Err(Error::CommandNotFound(u32::from_be_bytes(WRITE_USR_ACCESS)).into());
    }
    let operand = &bitstream[i + 4..i + 8];
    let usr_access = u32::from_be_bytes(operand.try_into()?);
    log::info!("Bitstream file USR_ACCESS value: {:#x}", usr_access);
    Ok(usr_access)
}

/// Returns a 32-bit timestamp suitable to be used as a USR_ACCESS value:
///
/// |-------+-------+-------+-------------------------+-------+--------+--------|
/// | Bits: | 31:27 | 26:23 | 22:17                   | 16:12 | 11:6   | 5:0    |
/// |-------+-------+-------+-------------------------+-------+--------+--------|
/// | Data: | Day   | Month | Year (since 2000, 0-63) | Hour  | Minute | Second |
/// |-------+-------+-------+-------------------------+-------+--------+--------|
///
/// From
/// https://www.xilinx.com/content/dam/xilinx/support/documents/application_notes/xapp1232-bitstream-id-with-usr_access.pdf
pub fn usr_access_timestamp() -> u32 {
    let now = Utc::now();
    now.day() << 27
        | now.month() << 23
        | now.year_ce().1.checked_sub(2000u32).unwrap() << 17
        | now.hour() << 12
        | now.minute() << 6
        | now.second()
}

pub fn usr_access_set(bitstream: &mut [u8], val: u32) -> Result<()> {
    let i = find_cmd(bitstream, &WRITE_USR_ACCESS)?;
    log::info!(
        "Bitstream file old USR_ACCESS value: {:#x}",
        u32::from_be_bytes(bitstream[i + 4..i + 8].try_into()?)
    );
    bitstream[i + 4..i + 8].copy_from_slice(&val.to_be_bytes());
    log::info!("Bitstream file new USR_ACCESS value: {:#x}", val);
    remove_crc(bitstream);
    Ok(())
}
<|endoftext|>


<commit_before>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#![no_std]
#![no_main]

use core::{arch::asm, ffi::c_void, panic::PanicInfo};
use x86_64::{
    instructions::{hlt, interrupts::int3, segmentation::Segment},
    registers::{
        control::{Cr3, Cr3Flags},
        segmentation::*,
    },
    structures::{
        gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
        idt::InterruptDescriptorTable,
        paging::{
            page_table::{PageTable, PageTableFlags},
            PhysFrame,
        },
    },
    PhysAddr, VirtAddr,
};
mod asm;

// Magic constants where various data structures will be stored.
// Many of these are set to be same as with crosvm; see
// https://google.github.io/crosvm/appendix/memory_layout.html for more details.
const BOOT_GDT_ADDR: PhysAddr = PhysAddr::new(0x1500);
// The GDT provided by crosvm has 4 entries, which means that the IDT can go immediately after that
// to address 0x1520. The Rust x86_64::GlobalDescriptorTable has 8 entries instead of 4; therefore,
// we must move our IDT data structure a bit lower in memory.
const BOOT_IDT_ADDR: PhysAddr = PhysAddr::new(0x1550);
const BOOT_PML4_ADDR: PhysAddr = PhysAddr::new(0x9000);
const BOOT_PDPT_ADDR: PhysAddr = PhysAddr::new(0xA000);
const BOOT_PD_ADDR: PhysAddr = PhysAddr::new(0xB000);

extern "C" {
    #[link_name = "pd_offset"]
    static BIOS_PD: c_void;
}

/// Creates page tables that identity-map the first 1GiB of memory using 2MiB hugepages.
pub fn create_page_tables(pml4: &mut PageTable, pdpt: &mut PageTable, pd: &mut PageTable) {
    pml4.zero();
    pml4[0].set_addr(
        PhysAddr::new(pdpt as *const _ as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );

    pdpt.zero();
    pdpt[0].set_addr(
        PhysAddr::new(pd as *const _ as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );

    pd.iter_mut().enumerate().for_each(|(i, entry)| {
        entry.set_addr(
            PhysAddr::new((i as u64) * 4096),
            PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::HUGE_PAGE,
        );
    });
}

pub fn create_gdt(gdt: &mut GlobalDescriptorTable) -> (SegmentSelector, SegmentSelector) {
    *gdt = GlobalDescriptorTable::new();
    let cs = gdt.add_entry(Descriptor::kernel_code_segment());
    let ds = gdt.add_entry(Descriptor::kernel_data_segment());
    (cs, ds)
}

pub fn create_idt(idt: &mut InterruptDescriptorTable) {
    *idt = InterruptDescriptorTable::new();
}

/// Passes control to the operating system kernel. No more code from the BIOS will run.
///
/// # Safety
///
/// This assumes that the kernel entry point is valid.
pub unsafe fn jump_to_kernel(entry_point: VirtAddr) -> ! {
    asm!(
        // Boot stack pointer
        "mov $0x8000, %rsp",
        // Zero page address
        "mov $0x7000, %rsi",
        // ...and away we go!
        "jmp *{0}",
        in(reg) entry_point.as_u64(),
        options(noreturn, att_syntax)
    );
}

#[no_mangle]
pub extern "C" fn rust64_start() -> ! {
    /* Set up the machine according to the 64-bit Linux boot protocol.
     * See https://www.kernel.org/doc/html/latest/x86/boot.html#id1 for the particular requirements.
     */

    // Safety, for all these data structure dereferences: The addresses are to well-known locations,
    // same as crosvm is using, in the lowest 1MB of memory which we know is valid.
    let gdt = unsafe { &mut *(BOOT_GDT_ADDR.as_u64() as *mut GlobalDescriptorTable) };
    let (cs, ds) = create_gdt(gdt);
    gdt.load();
    // Safety: we've set up the valid data structures in create_gdt, above.
    unsafe {
        CS::set_reg(cs);
        DS::set_reg(ds);
        ES::set_reg(ds);
        FS::set_reg(ds);
        GS::set_reg(ds);
        SS::set_reg(ds);
    }

    let idt = unsafe { &mut *(BOOT_IDT_ADDR.as_u64() as *mut InterruptDescriptorTable) };
    create_idt(idt);
    idt.load();

    let pml4 = unsafe { &mut *(BOOT_PML4_ADDR.as_u64() as *mut PageTable) };
    let pdpt = unsafe { &mut *(BOOT_PDPT_ADDR.as_u64() as *mut PageTable) };
    let pd = unsafe { &mut *(BOOT_PD_ADDR.as_u64() as *mut PageTable) };
    create_page_tables(pml4, pdpt, pd);
    /* We need to do some trickery here. All of the stage0 code is somewhere within [4G-2M; 4G).
     * Thus, let's keep our own last PD, so that we can continue executing after reloading the
     * page tables.
     */
    // Safety: dereferencing the raw pointer is safe as that's the currently in-use page directory.
    pdpt[3].set_addr(
        PhysAddr::new((unsafe { &BIOS_PD as *const _ }) as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );
    // Safety: changing the page tables here is safe because (a) we've populated the new page tables
    // at the well-known address of BOOT_PML4_ADDR and (b) we've ensured we keep the mapping for our
    // own code.
    unsafe {
        Cr3::write(
            PhysFrame::from_start_address(BOOT_PML4_ADDR).unwrap(),
            Cr3Flags::empty(),
        );
    }
    /* TODO(#3198): interrogate qemu for memory areas and set up the zero page. */

    /* TODO(#3199): determine the correct entry point from the ELF header */
    // Safety: this assumes the kernel is loaded at the given address.
    unsafe {
        jump_to_kernel(VirtAddr::new(0x200000));
    }
}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    // Trigger a breakpoint exception. As we don't have a #BP handler, this will triple fault and
    // terminate the program.
    int3();

    loop {
        hlt();
    }
}
<commit_msg>We use 2MiB pages, not 4KiB pages.<commit_after>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#![no_std]
#![no_main]

use core::{arch::asm, ffi::c_void, panic::PanicInfo};
use x86_64::{
    instructions::{hlt, interrupts::int3, segmentation::Segment},
    registers::{
        control::{Cr3, Cr3Flags},
        segmentation::*,
    },
    structures::{
        gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
        idt::InterruptDescriptorTable,
        paging::{
            page::Size2MiB,
            page_table::{PageTable, PageTableFlags},
            PageSize, PhysFrame,
        },
    },
    PhysAddr, VirtAddr,
};
mod asm;

// Magic constants where various data structures will be stored.
// Many of these are set to be same as with crosvm; see
// https://google.github.io/crosvm/appendix/memory_layout.html for more details.
const BOOT_GDT_ADDR: PhysAddr = PhysAddr::new(0x1500);
// The GDT provided by crosvm has 4 entries, which means that the IDT can go immediately after that
// to address 0x1520. The Rust x86_64::GlobalDescriptorTable has 8 entries instead of 4; therefore,
// we must move our IDT data structure a bit lower in memory.
const BOOT_IDT_ADDR: PhysAddr = PhysAddr::new(0x1550);
const BOOT_PML4_ADDR: PhysAddr = PhysAddr::new(0x9000);
const BOOT_PDPT_ADDR: PhysAddr = PhysAddr::new(0xA000);
const BOOT_PD_ADDR: PhysAddr = PhysAddr::new(0xB000);

extern "C" {
    #[link_name = "pd_offset"]
    static BIOS_PD: c_void;
}

/// Creates page tables that identity-map the first 1GiB of memory using 2MiB hugepages.
pub fn create_page_tables(pml4: &mut PageTable, pdpt: &mut PageTable, pd: &mut PageTable) {
    pml4.zero();
    pml4[0].set_addr(
        PhysAddr::new(pdpt as *const _ as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );

    pdpt.zero();
    pdpt[0].set_addr(
        PhysAddr::new(pd as *const _ as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );

    pd.iter_mut().enumerate().for_each(|(i, entry)| {
        entry.set_addr(
            PhysAddr::new((i as u64) * Size2MiB::SIZE),
            PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::HUGE_PAGE,
        );
    });
}

pub fn create_gdt(gdt: &mut GlobalDescriptorTable) -> (SegmentSelector, SegmentSelector) {
    *gdt = GlobalDescriptorTable::new();
    let cs = gdt.add_entry(Descriptor::kernel_code_segment());
    let ds = gdt.add_entry(Descriptor::kernel_data_segment());
    (cs, ds)
}

pub fn create_idt(idt: &mut InterruptDescriptorTable) {
    *idt = InterruptDescriptorTable::new();
}

/// Passes control to the operating system kernel. No more code from the BIOS will run.
///
/// # Safety
///
/// This assumes that the kernel entry point is valid.
pub unsafe fn jump_to_kernel(entry_point: VirtAddr) -> ! {
    asm!(
        // Boot stack pointer
        "mov $0x8000, %rsp",
        // Zero page address
        "mov $0x7000, %rsi",
        // ...and away we go!
        "jmp *{0}",
        in(reg) entry_point.as_u64(),
        options(noreturn, att_syntax)
    );
}

#[no_mangle]
pub extern "C" fn rust64_start() -> ! {
    /* Set up the machine according to the 64-bit Linux boot protocol.
     * See https://www.kernel.org/doc/html/latest/x86/boot.html#id1 for the particular requirements.
     */

    // Safety, for all these data structure dereferences: The addresses are to well-known locations,
    // same as crosvm is using, in the lowest 1MB of memory which we know is valid.
    let gdt = unsafe { &mut *(BOOT_GDT_ADDR.as_u64() as *mut GlobalDescriptorTable) };
    let (cs, ds) = create_gdt(gdt);
    gdt.load();
    // Safety: we've set up the valid data structures in create_gdt, above.
    unsafe {
        CS::set_reg(cs);
        DS::set_reg(ds);
        ES::set_reg(ds);
        FS::set_reg(ds);
        GS::set_reg(ds);
        SS::set_reg(ds);
    }

    let idt = unsafe { &mut *(BOOT_IDT_ADDR.as_u64() as *mut InterruptDescriptorTable) };
    create_idt(idt);
    idt.load();

    let pml4 = unsafe { &mut *(BOOT_PML4_ADDR.as_u64() as *mut PageTable) };
    let pdpt = unsafe { &mut *(BOOT_PDPT_ADDR.as_u64() as *mut PageTable) };
    let pd = unsafe { &mut *(BOOT_PD_ADDR.as_u64() as *mut PageTable) };
    create_page_tables(pml4, pdpt, pd);
    /* We need to do some trickery here. All of the stage0 code is somewhere within [4G-2M; 4G).
     * Thus, let's keep our own last PD, so that we can continue executing after reloading the
     * page tables.
     */
    // Safety: dereferencing the raw pointer is safe as that's the currently in-use page directory.
    pdpt[3].set_addr(
        PhysAddr::new((unsafe { &BIOS_PD as *const _ }) as u64),
        PageTableFlags::PRESENT | PageTableFlags::WRITABLE,
    );
    // Safety: changing the page tables here is safe because (a) we've populated the new page tables
    // at the well-known address of BOOT_PML4_ADDR and (b) we've ensured we keep the mapping for our
    // own code.
    unsafe {
        Cr3::write(
            PhysFrame::from_start_address(BOOT_PML4_ADDR).unwrap(),
            Cr3Flags::empty(),
        );
    }
    /* TODO(#3198): interrogate qemu for memory areas and set up the zero page. */

    /* TODO(#3199): determine the correct entry point from the ELF header */
    // Safety: this assumes the kernel is loaded at the given address.
    unsafe {
        jump_to_kernel(VirtAddr::new(0x200000));
    }
}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    // Trigger a breakpoint exception. As we don't have a #BP handler, this will triple fault and
    // terminate the program.
    int3();

    loop {
        hlt();
    }
}
<|endoftext|>