<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::Value;
use clap::App;

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        TOMLParserError => "TOML Parsing error",
        NoConfigFileFound   => "No config file found",

        ConfigOverrideError => "Config override error",
        ConfigOverrideKeyNotAvailable => "Key not available",
        ConfigOverrideTypeNotMatching => "Configuration Type not matching"

    );
);

pub use self::error::{ConfigError, ConfigErrorKind, MapErrInto};
use libimagerror::into::IntoError;

/// Result type of this module. Either `T` or `ConfigError`
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    /// Get a new configuration object built from the given toml value.
    pub fn with_value(value: Value) -> Configuration {
        Configuration{
            verbosity: get_verbosity(&value),
            editor: get_editor(&value),
            editor_opts: get_editor_opts(&value),
            config: value,
        }
    }

    /// Get the Editor setting from the configuration
    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    /// Get the underlying configuration TOML object
    pub fn config(&self) -> &Value {
        &self.config
    }

    /// Get the configuration of the store, if any.
    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

    /// Override the configuration.
    /// The `v` parameter is expected to contain 'key=value' pairs where the key is a path in the
    /// TOML tree, the value to be an appropriate value.
    ///
    /// The override fails if the configuration which is about to be overridden does not exist or
    /// the `value` part cannot be converted to the type of the configuration value.
    ///
    /// If `v` is empty, this is considered to be a successful `override_config()` call.
    pub fn override_config(&mut self, v: Vec<String>) -> Result<()> {
        use libimagutil::key_value_split::*;
        use libimagutil::iter::*;
        use self::error::ConfigErrorKind as CEK;
        use self::error::MapErrInto;
        use libimagerror::into::IntoError;
        use libimagstore::toml_ext::TomlValueExt;

        v.into_iter()
            .map(|s| { debug!("Trying to process '{}'", s); s })
            .filter_map(|s| match s.into_kv() {
                Some(kv) => Some(kv.into()),
                None => {
                    warn!("Could split at '=' - will be ignore override");
                    None
                }
            })
            .map(|(k, v)| self
                 .config
                 .read(&k[..])
                 .map_err_into(CEK::TOMLParserError)
                 .map(|toml| match toml {
                    Some(value) => match into_value(value, v) {
                        Some(v) => {
                            info!("Successfully overridden: {} = {}", k, v);
                            Ok(v)
                        },
                        None => Err(CEK::ConfigOverrideTypeNotMatching.into_error()),
                    },
                    None => Err(CEK::ConfigOverrideKeyNotAvailable.into_error()),
                })
            )
            .fold_result(|i| i)
            .map_err(Box::new)
            .map_err(|e| CEK::ConfigOverrideError.into_error_with_cause(e))
    }
}

/// Tries to convert the String `s` into the same type as `value`.
///
/// Returns None if string cannot be converted.
///
/// Arrays and Tables are not supported and will yield `None`.
fn into_value(value: Value, s: String) -> Option<Value> {
    use std::str::FromStr;

    match value {
        Value::String(_)  => Some(Value::String(s)),
        Value::Integer(_) => FromStr::from_str(&s[..]).ok().map(Value::Integer),
        Value::Float(_)   => FromStr::from_str(&s[..]).ok().map(Value::Float),
        Value::Boolean(_) => {
            if s == "true" { Some(Value::Boolean(true)) }
            else if s == "false" { Some(Value::Boolean(false)) }
            else { None }
        }
        Value::Datetime(_) => Value::try_from(s).ok(),
        _ => None,
    }
}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| is_match!(v, &Value::Boolean(true))),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/// Helper to fetch the config file
///
/// Tests several variants for the config file path and uses the first one which works.
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;
    use libimagerror::trace::trace_error;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![rtp.clone()],
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .filter_map(|path| {
            let content = {
                let f = File::open(path);
                if f.is_err() {
                    let _ = write!(stderr(), "Error opening file: {:?}", f);
                    return None
                }
                let mut f = f.unwrap();

                let mut s = String::new();
                f.read_to_string(&mut s).ok();
                s
            };

            match ::toml::de::from_str::<::toml::Value>(&content[..]) {
                Ok(res) => Some(res),
                Err(e) => {
                    let line_col = e
                        .line_col()
                        .map(|(line, col)| {
                            format!("Line {}, Column {}", line, col)
                        })
                        .unwrap_or_else(|| String::from("Line unknown, Column unknown"));

                    let _ = write!(stderr(), "Config file parser error at {}", line_col);
                    trace_error(&ConfigErrorKind::TOMLParserError.into_error_with_cause(Box::new(e)));
                    None
                }
            }
        })
        .nth(0)
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

pub trait InternalConfiguration {
    fn enable_logging(&self) -> bool {
        true
    }

    fn use_inmemory_fs(&self) -> bool {
        false
    }
}

impl<'a> InternalConfiguration for App<'a, 'a> {}
<commit_msg>Name the searchpath variable properly<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::Value;
use clap::App;

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        TOMLParserError => "TOML Parsing error",
        NoConfigFileFound   => "No config file found",

        ConfigOverrideError => "Config override error",
        ConfigOverrideKeyNotAvailable => "Key not available",
        ConfigOverrideTypeNotMatching => "Configuration Type not matching"

    );
);

pub use self::error::{ConfigError, ConfigErrorKind, MapErrInto};
use libimagerror::into::IntoError;

/// Result type of this module. Either `T` or `ConfigError`
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(config_searchpath: &PathBuf) -> Result<Configuration> {
        fetch_config(&config_searchpath).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    /// Get a new configuration object built from the given toml value.
    pub fn with_value(value: Value) -> Configuration {
        Configuration{
            verbosity: get_verbosity(&value),
            editor: get_editor(&value),
            editor_opts: get_editor_opts(&value),
            config: value,
        }
    }

    /// Get the Editor setting from the configuration
    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    /// Get the underlying configuration TOML object
    pub fn config(&self) -> &Value {
        &self.config
    }

    /// Get the configuration of the store, if any.
    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

    /// Override the configuration.
    /// The `v` parameter is expected to contain 'key=value' pairs where the key is a path in the
    /// TOML tree, the value to be an appropriate value.
    ///
    /// The override fails if the configuration which is about to be overridden does not exist or
    /// the `value` part cannot be converted to the type of the configuration value.
    ///
    /// If `v` is empty, this is considered to be a successful `override_config()` call.
    pub fn override_config(&mut self, v: Vec<String>) -> Result<()> {
        use libimagutil::key_value_split::*;
        use libimagutil::iter::*;
        use self::error::ConfigErrorKind as CEK;
        use self::error::MapErrInto;
        use libimagerror::into::IntoError;
        use libimagstore::toml_ext::TomlValueExt;

        v.into_iter()
            .map(|s| { debug!("Trying to process '{}'", s); s })
            .filter_map(|s| match s.into_kv() {
                Some(kv) => Some(kv.into()),
                None => {
                    warn!("Could split at '=' - will be ignore override");
                    None
                }
            })
            .map(|(k, v)| self
                 .config
                 .read(&k[..])
                 .map_err_into(CEK::TOMLParserError)
                 .map(|toml| match toml {
                    Some(value) => match into_value(value, v) {
                        Some(v) => {
                            info!("Successfully overridden: {} = {}", k, v);
                            Ok(v)
                        },
                        None => Err(CEK::ConfigOverrideTypeNotMatching.into_error()),
                    },
                    None => Err(CEK::ConfigOverrideKeyNotAvailable.into_error()),
                })
            )
            .fold_result(|i| i)
            .map_err(Box::new)
            .map_err(|e| CEK::ConfigOverrideError.into_error_with_cause(e))
    }
}

/// Tries to convert the String `s` into the same type as `value`.
///
/// Returns None if string cannot be converted.
///
/// Arrays and Tables are not supported and will yield `None`.
fn into_value(value: Value, s: String) -> Option<Value> {
    use std::str::FromStr;

    match value {
        Value::String(_)  => Some(Value::String(s)),
        Value::Integer(_) => FromStr::from_str(&s[..]).ok().map(Value::Integer),
        Value::Float(_)   => FromStr::from_str(&s[..]).ok().map(Value::Float),
        Value::Boolean(_) => {
            if s == "true" { Some(Value::Boolean(true)) }
            else if s == "false" { Some(Value::Boolean(false)) }
            else { None }
        }
        Value::Datetime(_) => Value::try_from(s).ok(),
        _ => None,
    }
}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| is_match!(v, &Value::Boolean(true))),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/// Helper to fetch the config file
///
/// Tests several variants for the config file path and uses the first one which works.
fn fetch_config(searchpath: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;
    use libimagerror::trace::trace_error;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![searchpath.clone()],
        gen_vars(searchpath.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .filter_map(|path| {
            let content = {
                let f = File::open(path);
                if f.is_err() {
                    let _ = write!(stderr(), "Error opening file: {:?}", f);
                    return None
                }
                let mut f = f.unwrap();

                let mut s = String::new();
                f.read_to_string(&mut s).ok();
                s
            };

            match ::toml::de::from_str::<::toml::Value>(&content[..]) {
                Ok(res) => Some(res),
                Err(e) => {
                    let line_col = e
                        .line_col()
                        .map(|(line, col)| {
                            format!("Line {}, Column {}", line, col)
                        })
                        .unwrap_or_else(|| String::from("Line unknown, Column unknown"));

                    let _ = write!(stderr(), "Config file parser error at {}", line_col);
                    trace_error(&ConfigErrorKind::TOMLParserError.into_error_with_cause(Box::new(e)));
                    None
                }
            }
        })
        .nth(0)
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

pub trait InternalConfiguration {
    fn enable_logging(&self) -> bool {
        true
    }

    fn use_inmemory_fs(&self) -> bool {
        false
    }
}

impl<'a> InternalConfiguration for App<'a, 'a> {}
<|endoftext|>


<commit_before>#![unstable(reason = "not public", issue = "none", feature = "fd")]

use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::lazy::SyncOnceCell;
use crate::mem;
use crate::sys::cvt;
use crate::sys_common::AsInner;

use libc::{c_int, c_void};

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;

#[cfg(any(target_os = "linux", target_os = "macos"))]
fn max_iov() -> c_int {
    static LIM: SyncOnceCell<c_int> = SyncOnceCell::new();

    *LIM.get_or_init(|| {
        let ret = unsafe {
            libc::sysconf(
                #[cfg(target_os = "linux")]
                libc::_SC_IOV_MAX,
                #[cfg(target_os = "macos")]
                libc::_SC_UIO_MAXIOV,
            )
        };

        // 1024 is the default value on modern Linux systems
        // and hopefully more useful than `c_int::MAX`.
        if ret > 0 { ret as c_int } else { 1024 }
    })
}

#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn max_iov() -> c_int {
    c_int::MAX
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd }
    }

    pub fn raw(&self) -> c_int {
        self.fd
    }

    /// Extracts the actual file descriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::readv(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov() as usize) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pread64(
            fd: c_int,
            buf: *mut c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            #[cfg(target_os = "linux")]
            use libc::pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(
                self.fd,
                buf.as_mut_ptr() as *mut c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::writev(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov() as usize) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pwrite64(
            fd: c_int,
            buf: *const c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(
                self.fd,
                buf.as_ptr() as *const c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    #[cfg(target_os = "linux")]
    pub fn get_cloexec(&self) -> io::Result<bool> {
        unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
    }

    #[cfg(not(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    )))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    ))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // is a POSIX flag that was added to Linux in 2.6.24.
        let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
        Ok(FileDesc::new(fd))
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    unsafe fn initializer(&self) -> Initializer {
        Initializer::nop()
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int {
        &self.fd
    }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}

#[cfg(test)]
mod tests {
    use super::{FileDesc, IoSlice};

    #[test]
    fn limit_vector_count() {
        let stdout = FileDesc { fd: 1 };
        let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();

        assert!(stdout.write_vectored(&bufs).is_ok());
    }
}
<commit_msg>Reduce synchronization overhead of I/O vector count memoization<commit_after>#![unstable(reason = "not public", issue = "none", feature = "fd")]

use crate::cmp;
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::cvt;
use crate::sys_common::AsInner;

use libc::{c_int, c_void};

#[derive(Debug)]
pub struct FileDesc {
    fd: c_int,
}

// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
//
// On macOS, however, apparently the 64-bit libc is either buggy or
// intentionally showing odd behavior by rejecting any read with a size
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;

#[cfg(any(target_os = "linux", target_os = "macos"))]
fn max_iov() -> usize {
    static LIM: AtomicUsize = AtomicUsize::new(0);

    let mut lim = LIM.load(Ordering::Relaxed);
    if lim == 0 {
        let ret = unsafe {
            libc::sysconf(
                #[cfg(target_os = "linux")]
                libc::_SC_IOV_MAX,
                #[cfg(target_os = "macos")]
                libc::_SC_UIO_MAXIOV,
            )
        };

        // 1024 is the default value on modern Linux systems
        // and hopefully more useful than `c_int::MAX`.
        lim = if ret > 0 { ret as usize } else { 1024 };
        LIM.store(lim, Ordering::Relaxed);
    }

    lim
}

#[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn max_iov() -> usize {
    c_int::MAX as usize
}

impl FileDesc {
    pub fn new(fd: c_int) -> FileDesc {
        FileDesc { fd }
    }

    pub fn raw(&self) -> c_int {
        self.fd
    }

    /// Extracts the actual file descriptor without closing it.
    pub fn into_raw(self) -> c_int {
        let fd = self.fd;
        mem::forget(self);
        fd
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::readv(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_read_vectored(&self) -> bool {
        true
    }

    pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let mut me = self;
        (&mut me).read_to_end(buf)
    }

    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pread64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pread64(
            fd: c_int,
            buf: *mut c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pread as pread64;
            #[cfg(target_os = "linux")]
            use libc::pread64;
            cvt(pread64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pread64(
                self.fd,
                buf.as_mut_ptr() as *mut c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
        })?;
        Ok(ret as usize)
    }

    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
        let ret = cvt(unsafe {
            libc::writev(
                self.fd,
                bufs.as_ptr() as *const libc::iovec,
                cmp::min(bufs.len(), max_iov()) as c_int,
            )
        })?;
        Ok(ret as usize)
    }

    #[inline]
    pub fn is_write_vectored(&self) -> bool {
        true
    }

    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
        #[cfg(target_os = "android")]
        use super::android::cvt_pwrite64;

        #[cfg(not(target_os = "android"))]
        unsafe fn cvt_pwrite64(
            fd: c_int,
            buf: *const c_void,
            count: usize,
            offset: i64,
        ) -> io::Result<isize> {
            #[cfg(not(target_os = "linux"))]
            use libc::pwrite as pwrite64;
            #[cfg(target_os = "linux")]
            use libc::pwrite64;
            cvt(pwrite64(fd, buf, count, offset))
        }

        unsafe {
            cvt_pwrite64(
                self.fd,
                buf.as_ptr() as *const c_void,
                cmp::min(buf.len(), READ_LIMIT),
                offset as i64,
            )
            .map(|n| n as usize)
        }
    }

    #[cfg(target_os = "linux")]
    pub fn get_cloexec(&self) -> io::Result<bool> {
        unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
    }

    #[cfg(not(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    )))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
            Ok(())
        }
    }
    #[cfg(any(
        target_env = "newlib",
        target_os = "solaris",
        target_os = "illumos",
        target_os = "emscripten",
        target_os = "fuchsia",
        target_os = "l4re",
        target_os = "linux",
        target_os = "haiku",
        target_os = "redox"
    ))]
    pub fn set_cloexec(&self) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
            let new = previous | libc::FD_CLOEXEC;
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
            }
            Ok(())
        }
    }

    #[cfg(target_os = "linux")]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let v = nonblocking as c_int;
            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
            Ok(())
        }
    }

    #[cfg(not(target_os = "linux"))]
    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
        unsafe {
            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
            let new = if nonblocking {
                previous | libc::O_NONBLOCK
            } else {
                previous & !libc::O_NONBLOCK
            };
            if new != previous {
                cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
            }
            Ok(())
        }
    }

    pub fn duplicate(&self) -> io::Result<FileDesc> {
        // We want to atomically duplicate this file descriptor and set the
        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
        // is a POSIX flag that was added to Linux in 2.6.24.
        let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
        Ok(FileDesc::new(fd))
    }
}

impl<'a> Read for &'a FileDesc {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    unsafe fn initializer(&self) -> Initializer {
        Initializer::nop()
    }
}

impl AsInner<c_int> for FileDesc {
    fn as_inner(&self) -> &c_int {
        &self.fd
    }
}

impl Drop for FileDesc {
    fn drop(&mut self) {
        // Note that errors are ignored when closing a file descriptor. The
        // reason for this is that if an error occurs we don't actually know if
        // the file descriptor was closed or not, and if we retried (for
        // something like EINTR), we might close another valid file descriptor
        // opened after we closed ours.
        let _ = unsafe { libc::close(self.fd) };
    }
}

#[cfg(test)]
mod tests {
    use super::{FileDesc, IoSlice};

    #[test]
    fn limit_vector_count() {
        let stdout = FileDesc { fd: 1 };
        let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();

        assert!(stdout.write_vectored(&bufs).is_ok());
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate serde_json;
extern crate gethrpc;

use sputnikvm::{Gas, Address, U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch,
                    AccountCommitment, Account, FRONTIER_PATCH, HOMESTEAD_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{GethRPCClient, NormalGethRPCClient, RPCBlock};
use std::str::FromStr;

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn handle_fire_without_rpc(vm: &mut SeqTransactionVM) {
    loop {
        match vm.fire() {
            Ok(()) => break,
            Err(RequireError::Account(address)) => {
                vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
            },
            Err(RequireError::AccountStorage(address, index)) => {
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: M256::zero(),
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: Vec::new(),
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                vm.commit_blockhash(number, M256::zero());
            },
        }
    }
}

fn handle_fire_with_rpc<T: GethRPCClient>(client: &mut T, vm: &mut SeqTransactionVM, block_number: &str) {
    loop {
        match vm.fire() {
            Ok(()) => break,
            Err(RequireError::Account(address)) => {
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), U256::from_str(&block_number).unwrap().as_usize()) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                    .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn main() {
    let matches = clap_app!(sputnikvm =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "CLI tool for SputnikVM.")
        (@arg CREATE: --create "Execute a CreateContract transaction instead of message call.")
        (@arg CODE: --code +takes_value +required "Code to be executed.")
        (@arg RPC: --rpc +takes_value "Indicate this EVM should be run on an actual blockchain.")
        (@arg DATA: --data +takes_value "Data associated with this transaction.")
        (@arg BLOCK: --block +takes_value "Block number associated.")
        (@arg PATCH: --patch +takes_value "Patch to be used.")
        (@arg GAS_LIMIT: --gas_limit +takes_value "Gas limit.")
        (@arg GAS_PRICE: --gas_price +takes_value "Gas price.")
        (@arg CALLER: --caller +takes_value "Caller of the transaction.")
        (@arg ADDRESS: --address +takes_value "Address of the transaction.")
        (@arg VALUE: --value +takes_value "Value of the transaction.")
    ).get_matches();

    let code = read_hex(matches.value_of("CODE").unwrap()).unwrap();
    let data = read_hex(matches.value_of("DATA").unwrap_or("")).unwrap();
    let caller = Address::from_str(matches.value_of("CALLER").unwrap_or("0x0000000000000000000000000000000000000000")).unwrap();
    let address = Address::from_str(matches.value_of("ADDRESS").unwrap_or("0x0000000000000000000000000000000000000000")).unwrap();
    let value = U256::from_str(matches.value_of("VALUE").unwrap_or("0x0")).unwrap();
    let gas_limit = Gas::from_str(matches.value_of("GAS_LIMIT").unwrap_or("0x2540be400")).unwrap();
    let gas_price = Gas::from_str(matches.value_of("GAS_PRICE").unwrap_or("0x0")).unwrap();
    let is_create = matches.is_present("CREATE");
    let patch = match matches.value_of("PATCH") {
        None => &FRONTIER_PATCH,
        Some("frontier") => &FRONTIER_PATCH,
        Some("homestead") => &HOMESTEAD_PATCH,
        _ => panic!("Unsupported patch."),
    };
    let block_number = matches.value_of("BLOCK").unwrap_or("0x0");

    let block = if matches.is_present("RPC") {
        let mut client = NormalGethRPCClient::new(matches.value_of("RPC").unwrap());
        from_rpc_block(&client.get_block_by_number(block_number))
    } else {
        BlockHeader {
            coinbase: Address::default(),
            timestamp: M256::zero(),
            number: M256::from_str(block_number).unwrap(),
            difficulty: M256::zero(),
            gas_limit: Gas::zero(),
        }
    };

    let transaction = if is_create {
        Transaction::ContractCreation {
            caller, value, gas_limit, gas_price,
            init: data,
        }
    } else {
        Transaction::MessageCall {
            caller, address, value, gas_limit, gas_price, data
        }
    };

    let mut vm = SeqTransactionVM::new(transaction, block, patch);
    if matches.is_present("RPC") {
        let mut client = NormalGethRPCClient::new(matches.value_of("RPC").unwrap());
        handle_fire_with_rpc(&mut client, &mut vm, block_number);
    } else {
        handle_fire_without_rpc(&mut vm);
    }

    println!("VM returned: {:?}", vm.status());
    println!("VM out: {:?}", vm.out());
}
<commit_msg>cli: make patch require argument and allow eip150 and eip160<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate serde_json;
extern crate gethrpc;

use sputnikvm::{Gas, Address, U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch,
                    AccountCommitment, Account, FRONTIER_PATCH, HOMESTEAD_PATCH,
                    EIP150_PATCH, EIP160_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{GethRPCClient, NormalGethRPCClient, RPCBlock};
use std::str::FromStr;

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn handle_fire_without_rpc(vm: &mut SeqTransactionVM) {
    loop {
        match vm.fire() {
            Ok(()) => break,
            Err(RequireError::Account(address)) => {
                vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
            },
            Err(RequireError::AccountStorage(address, index)) => {
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: M256::zero(),
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: Vec::new(),
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                vm.commit_blockhash(number, M256::zero());
            },
        }
    }
}

fn handle_fire_with_rpc<T: GethRPCClient>(client: &mut T, vm: &mut SeqTransactionVM, block_number: &str) {
    loop {
        match vm.fire() {
            Ok(()) => break,
            Err(RequireError::Account(address)) => {
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), U256::from_str(&block_number).unwrap().as_usize()) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                    .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn main() {
    let matches = clap_app!(sputnikvm =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "CLI tool for SputnikVM.")
        (@arg CREATE: --create "Execute a CreateContract transaction instead of message call.")
        (@arg CODE: --code +takes_value +required "Code to be executed.")
        (@arg RPC: --rpc +takes_value "Indicate this EVM should be run on an actual blockchain.")
        (@arg DATA: --data +takes_value "Data associated with this transaction.")
        (@arg BLOCK: --block +takes_value "Block number associated.")
        (@arg PATCH: --patch +takes_value +required "Patch to be used.")
        (@arg GAS_LIMIT: --gas_limit +takes_value "Gas limit.")
        (@arg GAS_PRICE: --gas_price +takes_value "Gas price.")
        (@arg CALLER: --caller +takes_value "Caller of the transaction.")
        (@arg ADDRESS: --address +takes_value "Address of the transaction.")
        (@arg VALUE: --value +takes_value "Value of the transaction.")
    ).get_matches();

    let code = read_hex(matches.value_of("CODE").unwrap()).unwrap();
    let data = read_hex(matches.value_of("DATA").unwrap_or("")).unwrap();
    let caller = Address::from_str(matches.value_of("CALLER").unwrap_or("0x0000000000000000000000000000000000000000")).unwrap();
    let address = Address::from_str(matches.value_of("ADDRESS").unwrap_or("0x0000000000000000000000000000000000000000")).unwrap();
    let value = U256::from_str(matches.value_of("VALUE").unwrap_or("0x0")).unwrap();
    let gas_limit = Gas::from_str(matches.value_of("GAS_LIMIT").unwrap_or("0x2540be400")).unwrap();
    let gas_price = Gas::from_str(matches.value_of("GAS_PRICE").unwrap_or("0x0")).unwrap();
    let is_create = matches.is_present("CREATE");
    let patch = match matches.value_of("PATCH") {
        Some("frontier") => &FRONTIER_PATCH,
        Some("homestead") => &HOMESTEAD_PATCH,
        Some("eip150") => &EIP150_PATCH,
        Some("eip160") => &EIP160_PATCH,
        _ => panic!("Unsupported patch."),
    };
    let block_number = matches.value_of("BLOCK").unwrap_or("0x0");

    let block = if matches.is_present("RPC") {
        let mut client = NormalGethRPCClient::new(matches.value_of("RPC").unwrap());
        from_rpc_block(&client.get_block_by_number(block_number))
    } else {
        BlockHeader {
            coinbase: Address::default(),
            timestamp: M256::zero(),
            number: M256::from_str(block_number).unwrap(),
            difficulty: M256::zero(),
            gas_limit: Gas::zero(),
        }
    };

    let transaction = if is_create {
        Transaction::ContractCreation {
            caller, value, gas_limit, gas_price,
            init: data,
        }
    } else {
        Transaction::MessageCall {
            caller, address, value, gas_limit, gas_price, data
        }
    };

    let mut vm = SeqTransactionVM::new(transaction, block, patch);
    if matches.is_present("RPC") {
        let mut client = NormalGethRPCClient::new(matches.value_of("RPC").unwrap());
        handle_fire_with_rpc(&mut client, &mut vm, block_number);
    } else {
        handle_fire_without_rpc(&mut vm);
    }

    println!("VM returned: {:?}", vm.status());
    println!("VM out: {:?}", vm.out());
}
<|endoftext|>


<commit_before>use std::collections::hash_map;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use std::hash::Hasher;
use std::slice;

use crate::clear::Clear;

use crate::wire_format;
use crate::zigzag::encode_zig_zag_32;
use crate::zigzag::encode_zig_zag_64;

/// Unknown value.
///
/// See [`UnknownFields`](crate::UnknownFields) for the explanations.
#[derive(Debug)]
pub enum UnknownValue {
    /// 32-bit unknown (e. g. `fixed32` or `float`)
    Fixed32(u32),
    /// 64-bit unknown (e. g. `fixed64` or `double`)
    Fixed64(u64),
    /// Varint unknown (e. g. `int32` or `bool`)
    Varint(u64),
    /// Length-delimited unknown (e. g. `message` or `string`)
    LengthDelimited(Vec<u8>),
}

impl UnknownValue {
    /// Wire type for this unknown
    pub fn wire_type(&self) -> wire_format::WireType {
        self.get_ref().wire_type()
    }

    /// As ref
    pub fn get_ref<'s>(&'s self) -> UnknownValueRef<'s> {
        match *self {
            UnknownValue::Fixed32(fixed32) => UnknownValueRef::Fixed32(fixed32),
            UnknownValue::Fixed64(fixed64) => UnknownValueRef::Fixed64(fixed64),
            UnknownValue::Varint(varint) => UnknownValueRef::Varint(varint),
            UnknownValue::LengthDelimited(ref bytes) => UnknownValueRef::LengthDelimited(&bytes),
        }
    }

    /// Construct unknown value from `sint32` value.
    pub fn sint32(i: i32) -> UnknownValue {
        UnknownValue::Varint(encode_zig_zag_32(i) as u64)
    }

    /// Construct unknown value from `sint64` value.
    pub fn sint64(i: i64) -> UnknownValue {
        UnknownValue::Varint(encode_zig_zag_64(i))
    }
}

/// Reference to unknown value.
///
/// See [`UnknownFields`](crate::UnknownFields) for explanations.
pub enum UnknownValueRef<'o> {
    /// 32-bit unknown
    Fixed32(u32),
    /// 64-bit unknown
    Fixed64(u64),
    /// Varint unknown
    Varint(u64),
    /// Length-delimited unknown
    LengthDelimited(&'o [u8]),
}

impl<'o> UnknownValueRef<'o> {
    /// Wire-type to serialize this unknown
    pub fn wire_type(&self) -> wire_format::WireType {
        match *self {
            UnknownValueRef::Fixed32(_) => wire_format::WireTypeFixed32,
            UnknownValueRef::Fixed64(_) => wire_format::WireTypeFixed64,
            UnknownValueRef::Varint(_) => wire_format::WireTypeVarint,
            UnknownValueRef::LengthDelimited(_) => wire_format::WireTypeLengthDelimited,
        }
    }
}

/// Field unknown values.
///
/// See [`UnknownFields`](crate::UnknownFields) for explanations.
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
pub struct UnknownValues {
    /// 32-bit unknowns
    pub fixed32: Vec<u32>,
    /// 64-bit unknowns
    pub fixed64: Vec<u64>,
    /// Varint unknowns
    pub varint: Vec<u64>,
    /// Length-delimited unknowns
    pub length_delimited: Vec<Vec<u8>>,
}

impl UnknownValues {
    /// Add unknown value
    pub fn add_value(&mut self, value: UnknownValue) {
        match value {
            UnknownValue::Fixed64(fixed64) => self.fixed64.push(fixed64),
            UnknownValue::Fixed32(fixed32) => self.fixed32.push(fixed32),
            UnknownValue::Varint(varint) => self.varint.push(varint),
            UnknownValue::LengthDelimited(length_delimited) => {
                self.length_delimited.push(length_delimited)
            }
        };
    }

    /// Iterate over unknown values
    pub fn iter<'s>(&'s self) -> UnknownValuesIter<'s> {
        UnknownValuesIter {
            fixed32: self.fixed32.iter(),
            fixed64: self.fixed64.iter(),
            varint: self.varint.iter(),
            length_delimited: self.length_delimited.iter(),
        }
    }
}

impl<'a> IntoIterator for &'a UnknownValues {
    type Item = UnknownValueRef<'a>;
    type IntoIter = UnknownValuesIter<'a>;

    fn into_iter(self) -> UnknownValuesIter<'a> {
        self.iter()
    }
}

/// Iterator over unknown values
pub struct UnknownValuesIter<'o> {
    fixed32: slice::Iter<'o, u32>,
    fixed64: slice::Iter<'o, u64>,
    varint: slice::Iter<'o, u64>,
    length_delimited: slice::Iter<'o, Vec<u8>>,
}

impl<'o> Iterator for UnknownValuesIter<'o> {
    type Item = UnknownValueRef<'o>;

    fn next(&mut self) -> Option<UnknownValueRef<'o>> {
        let fixed32 = self.fixed32.next();
        if fixed32.is_some() {
            return Some(UnknownValueRef::Fixed32(*fixed32.unwrap()));
        }
        let fixed64 = self.fixed64.next();
        if fixed64.is_some() {
            return Some(UnknownValueRef::Fixed64(*fixed64.unwrap()));
        }
        let varint = self.varint.next();
        if varint.is_some() {
            return Some(UnknownValueRef::Varint(*varint.unwrap()));
        }
        let length_delimited = self.length_delimited.next();
        if length_delimited.is_some() {
            return Some(UnknownValueRef::LengthDelimited(&length_delimited.unwrap()));
        }
        None
    }
}

/// Hold "unknown" fields in parsed message.
///
/// Field may be unknown if it they are added in newer version of `.proto`.
/// Unknown fields are stored in `UnknownFields` structure, so
/// protobuf message could process messages without losing data.
///
/// For example, in this operation: load from DB, modify, store to DB,
/// even when working with older `.proto` file, new fields won't be lost.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct UnknownFields {
    /// The map.
    // option is needed, because HashMap constructor performs allocation,
    // and very expensive
    // TODO: hide
    pub fields: Option<Box<HashMap<u32, UnknownValues>>>,
}

/// Very simple hash implementation of `Hash` for `UnknownFields`.
/// Since map is unordered, we cannot put entry hashes into hasher,
/// instead we summing hashes of entries.
impl Hash for UnknownFields {
    fn hash<H: Hasher>(&self, state: &mut H) {
        if let Some(ref map) = self.fields {
            if !map.is_empty() {
                let mut hash: u64 = 0;
                for (k, v) in &**map {
                    let mut entry_hasher = DefaultHasher::new();
                    Hash::hash(&(k, v), &mut entry_hasher);
                    hash = hash.wrapping_add(entry_hasher.finish());
                }
                Hash::hash(&map.len(), state);
                Hash::hash(&hash, state);
            }
        }
    }
}

impl UnknownFields {
    /// Empty unknown fields
    pub fn new() -> UnknownFields {
        Default::default()
    }

    fn init_map(&mut self) {
        if self.fields.is_none() {
            self.fields = Some(Default::default());
        }
    }

    fn find_field<'a>(&'a mut self, number: &'a u32) -> &'a mut UnknownValues {
        self.init_map();

        match self.fields.as_mut().unwrap().entry(*number) {
            hash_map::Entry::Occupied(e) => e.into_mut(),
            hash_map::Entry::Vacant(e) => e.insert(Default::default()),
        }
    }

    /// Add unknown fixed 32-bit
    pub fn add_fixed32(&mut self, number: u32, fixed32: u32) {
        self.find_field(&number).fixed32.push(fixed32);
    }

    /// Add unknown fixed 64-bit
    pub fn add_fixed64(&mut self, number: u32, fixed64: u64) {
        self.find_field(&number).fixed64.push(fixed64);
    }

    /// Add unknown varint
    pub fn add_varint(&mut self, number: u32, varint: u64) {
        self.find_field(&number).varint.push(varint);
    }

    /// Add unknown length delimited
    pub fn add_length_delimited(&mut self, number: u32, length_delimited: Vec<u8>) {
        self.find_field(&number)
            .length_delimited
            .push(length_delimited);
    }

    /// Add unknown value
    pub fn add_value(&mut self, number: u32, value: UnknownValue) {
        self.find_field(&number).add_value(value);
    }

    /// Iterate over all unknowns
    pub fn iter<'s>(&'s self) -> UnknownFieldsIter<'s> {
        UnknownFieldsIter {
            entries: self.fields.as_ref().map(|m| m.iter()),
        }
    }

    /// Find unknown field by number
    pub fn get(&self, field_number: u32) -> Option<&UnknownValues> {
        match self.fields {
            Some(ref map) => map.get(&field_number),
            None => None,
        }
    }
}

impl Clear for UnknownFields {
    fn clear(&mut self) {
        if let Some(ref mut fields) = self.fields {
            fields.clear();
        }
    }
}

impl<'a> IntoIterator for &'a UnknownFields {
    type Item = (u32, &'a UnknownValues);
    type IntoIter = UnknownFieldsIter<'a>;

    fn into_iter(self) -> UnknownFieldsIter<'a> {
        self.iter()
    }
}

/// Iterator over [`UnknownFields`](crate::UnknownFields)
pub struct UnknownFieldsIter<'s> {
    entries: Option<hash_map::Iter<'s, u32, UnknownValues>>,
}

impl<'s> Iterator for UnknownFieldsIter<'s> {
    type Item = (u32, &'s UnknownValues);

    fn next(&mut self) -> Option<(u32, &'s UnknownValues)> {
        match self.entries {
            Some(ref mut entries) => entries.next().map(|(&number, values)| (number, values)),
            None => None,
        }
    }
}

#[cfg(test)]
mod test {
    use super::UnknownFields;
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hash;
    use std::hash::Hasher;

    #[test]
    fn unknown_fields_hash() {
        let mut unknown_fields_1 = UnknownFields::new();
        let mut unknown_fields_2 = UnknownFields::new();

        // Check field order is not important

        unknown_fields_1.add_fixed32(10, 222);
        unknown_fields_1.add_fixed32(10, 223);
        unknown_fields_1.add_fixed64(14, 224);

        unknown_fields_2.add_fixed32(10, 222);
        unknown_fields_2.add_fixed64(14, 224);
        unknown_fields_2.add_fixed32(10, 223);

        fn hash(unknown_fields: &UnknownFields) -> u64 {
            let mut hasher = DefaultHasher::new();
            Hash::hash(unknown_fields, &mut hasher);
            hasher.finish()
        }

        assert_eq!(hash(&unknown_fields_1), hash(&unknown_fields_2));
    }
}
<commit_msg>Hide UnknownFields::fields field<commit_after>use std::collections::hash_map;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use std::hash::Hasher;
use std::slice;

use crate::clear::Clear;

use crate::wire_format;
use crate::zigzag::encode_zig_zag_32;
use crate::zigzag::encode_zig_zag_64;

/// Unknown value.
///
/// See [`UnknownFields`](crate::UnknownFields) for the explanations.
#[derive(Debug)]
pub enum UnknownValue {
    /// 32-bit unknown (e. g. `fixed32` or `float`)
    Fixed32(u32),
    /// 64-bit unknown (e. g. `fixed64` or `double`)
    Fixed64(u64),
    /// Varint unknown (e. g. `int32` or `bool`)
    Varint(u64),
    /// Length-delimited unknown (e. g. `message` or `string`)
    LengthDelimited(Vec<u8>),
}

impl UnknownValue {
    /// Wire type for this unknown
    pub fn wire_type(&self) -> wire_format::WireType {
        self.get_ref().wire_type()
    }

    /// As ref
    pub fn get_ref<'s>(&'s self) -> UnknownValueRef<'s> {
        match *self {
            UnknownValue::Fixed32(fixed32) => UnknownValueRef::Fixed32(fixed32),
            UnknownValue::Fixed64(fixed64) => UnknownValueRef::Fixed64(fixed64),
            UnknownValue::Varint(varint) => UnknownValueRef::Varint(varint),
            UnknownValue::LengthDelimited(ref bytes) => UnknownValueRef::LengthDelimited(&bytes),
        }
    }

    /// Construct unknown value from `sint32` value.
    pub fn sint32(i: i32) -> UnknownValue {
        UnknownValue::Varint(encode_zig_zag_32(i) as u64)
    }

    /// Construct unknown value from `sint64` value.
    pub fn sint64(i: i64) -> UnknownValue {
        UnknownValue::Varint(encode_zig_zag_64(i))
    }
}

/// Reference to unknown value.
///
/// See [`UnknownFields`](crate::UnknownFields) for explanations.
pub enum UnknownValueRef<'o> {
    /// 32-bit unknown
    Fixed32(u32),
    /// 64-bit unknown
    Fixed64(u64),
    /// Varint unknown
    Varint(u64),
    /// Length-delimited unknown
    LengthDelimited(&'o [u8]),
}

impl<'o> UnknownValueRef<'o> {
    /// Wire-type to serialize this unknown
    pub fn wire_type(&self) -> wire_format::WireType {
        match *self {
            UnknownValueRef::Fixed32(_) => wire_format::WireTypeFixed32,
            UnknownValueRef::Fixed64(_) => wire_format::WireTypeFixed64,
            UnknownValueRef::Varint(_) => wire_format::WireTypeVarint,
            UnknownValueRef::LengthDelimited(_) => wire_format::WireTypeLengthDelimited,
        }
    }
}

/// Field unknown values.
///
/// See [`UnknownFields`](crate::UnknownFields) for explanations.
#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
pub struct UnknownValues {
    /// 32-bit unknowns
    pub fixed32: Vec<u32>,
    /// 64-bit unknowns
    pub fixed64: Vec<u64>,
    /// Varint unknowns
    pub varint: Vec<u64>,
    /// Length-delimited unknowns
    pub length_delimited: Vec<Vec<u8>>,
}

impl UnknownValues {
    /// Add unknown value
    pub fn add_value(&mut self, value: UnknownValue) {
        match value {
            UnknownValue::Fixed64(fixed64) => self.fixed64.push(fixed64),
            UnknownValue::Fixed32(fixed32) => self.fixed32.push(fixed32),
            UnknownValue::Varint(varint) => self.varint.push(varint),
            UnknownValue::LengthDelimited(length_delimited) => {
                self.length_delimited.push(length_delimited)
            }
        };
    }

    /// Iterate over unknown values
    pub fn iter<'s>(&'s self) -> UnknownValuesIter<'s> {
        UnknownValuesIter {
            fixed32: self.fixed32.iter(),
            fixed64: self.fixed64.iter(),
            varint: self.varint.iter(),
            length_delimited: self.length_delimited.iter(),
        }
    }
}

impl<'a> IntoIterator for &'a UnknownValues {
    type Item = UnknownValueRef<'a>;
    type IntoIter = UnknownValuesIter<'a>;

    fn into_iter(self) -> UnknownValuesIter<'a> {
        self.iter()
    }
}

/// Iterator over unknown values
pub struct UnknownValuesIter<'o> {
    fixed32: slice::Iter<'o, u32>,
    fixed64: slice::Iter<'o, u64>,
    varint: slice::Iter<'o, u64>,
    length_delimited: slice::Iter<'o, Vec<u8>>,
}

impl<'o> Iterator for UnknownValuesIter<'o> {
    type Item = UnknownValueRef<'o>;

    fn next(&mut self) -> Option<UnknownValueRef<'o>> {
        let fixed32 = self.fixed32.next();
        if fixed32.is_some() {
            return Some(UnknownValueRef::Fixed32(*fixed32.unwrap()));
        }
        let fixed64 = self.fixed64.next();
        if fixed64.is_some() {
            return Some(UnknownValueRef::Fixed64(*fixed64.unwrap()));
        }
        let varint = self.varint.next();
        if varint.is_some() {
            return Some(UnknownValueRef::Varint(*varint.unwrap()));
        }
        let length_delimited = self.length_delimited.next();
        if length_delimited.is_some() {
            return Some(UnknownValueRef::LengthDelimited(&length_delimited.unwrap()));
        }
        None
    }
}

/// Hold "unknown" fields in parsed message.
///
/// Field may be unknown if it they are added in newer version of `.proto`.
/// Unknown fields are stored in `UnknownFields` structure, so
/// protobuf message could process messages without losing data.
///
/// For example, in this operation: load from DB, modify, store to DB,
/// even when working with older `.proto` file, new fields won't be lost.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct UnknownFields {
    /// The map.
    // option is needed, because HashMap constructor performs allocation,
    // and very expensive
    fields: Option<Box<HashMap<u32, UnknownValues>>>,
}

/// Very simple hash implementation of `Hash` for `UnknownFields`.
/// Since map is unordered, we cannot put entry hashes into hasher,
/// instead we summing hashes of entries.
impl Hash for UnknownFields {
    fn hash<H: Hasher>(&self, state: &mut H) {
        if let Some(ref map) = self.fields {
            if !map.is_empty() {
                let mut hash: u64 = 0;
                for (k, v) in &**map {
                    let mut entry_hasher = DefaultHasher::new();
                    Hash::hash(&(k, v), &mut entry_hasher);
                    hash = hash.wrapping_add(entry_hasher.finish());
                }
                Hash::hash(&map.len(), state);
                Hash::hash(&hash, state);
            }
        }
    }
}

impl UnknownFields {
    /// Empty unknown fields
    pub fn new() -> UnknownFields {
        Default::default()
    }

    fn init_map(&mut self) {
        if self.fields.is_none() {
            self.fields = Some(Default::default());
        }
    }

    fn find_field<'a>(&'a mut self, number: &'a u32) -> &'a mut UnknownValues {
        self.init_map();

        match self.fields.as_mut().unwrap().entry(*number) {
            hash_map::Entry::Occupied(e) => e.into_mut(),
            hash_map::Entry::Vacant(e) => e.insert(Default::default()),
        }
    }

    /// Add unknown fixed 32-bit
    pub fn add_fixed32(&mut self, number: u32, fixed32: u32) {
        self.find_field(&number).fixed32.push(fixed32);
    }

    /// Add unknown fixed 64-bit
    pub fn add_fixed64(&mut self, number: u32, fixed64: u64) {
        self.find_field(&number).fixed64.push(fixed64);
    }

    /// Add unknown varint
    pub fn add_varint(&mut self, number: u32, varint: u64) {
        self.find_field(&number).varint.push(varint);
    }

    /// Add unknown length delimited
    pub fn add_length_delimited(&mut self, number: u32, length_delimited: Vec<u8>) {
        self.find_field(&number)
            .length_delimited
            .push(length_delimited);
    }

    /// Add unknown value
    pub fn add_value(&mut self, number: u32, value: UnknownValue) {
        self.find_field(&number).add_value(value);
    }

    /// Iterate over all unknowns
    pub fn iter<'s>(&'s self) -> UnknownFieldsIter<'s> {
        UnknownFieldsIter {
            entries: self.fields.as_ref().map(|m| m.iter()),
        }
    }

    /// Find unknown field by number
    pub fn get(&self, field_number: u32) -> Option<&UnknownValues> {
        match self.fields {
            Some(ref map) => map.get(&field_number),
            None => None,
        }
    }
}

impl Clear for UnknownFields {
    fn clear(&mut self) {
        if let Some(ref mut fields) = self.fields {
            fields.clear();
        }
    }
}

impl<'a> IntoIterator for &'a UnknownFields {
    type Item = (u32, &'a UnknownValues);
    type IntoIter = UnknownFieldsIter<'a>;

    fn into_iter(self) -> UnknownFieldsIter<'a> {
        self.iter()
    }
}

/// Iterator over [`UnknownFields`](crate::UnknownFields)
pub struct UnknownFieldsIter<'s> {
    entries: Option<hash_map::Iter<'s, u32, UnknownValues>>,
}

impl<'s> Iterator for UnknownFieldsIter<'s> {
    type Item = (u32, &'s UnknownValues);

    fn next(&mut self) -> Option<(u32, &'s UnknownValues)> {
        match self.entries {
            Some(ref mut entries) => entries.next().map(|(&number, values)| (number, values)),
            None => None,
        }
    }
}

#[cfg(test)]
mod test {
    use super::UnknownFields;
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hash;
    use std::hash::Hasher;

    #[test]
    fn unknown_fields_hash() {
        let mut unknown_fields_1 = UnknownFields::new();
        let mut unknown_fields_2 = UnknownFields::new();

        // Check field order is not important

        unknown_fields_1.add_fixed32(10, 222);
        unknown_fields_1.add_fixed32(10, 223);
        unknown_fields_1.add_fixed64(14, 224);

        unknown_fields_2.add_fixed32(10, 222);
        unknown_fields_2.add_fixed64(14, 224);
        unknown_fields_2.add_fixed32(10, 223);

        fn hash(unknown_fields: &UnknownFields) -> u64 {
            let mut hasher = DefaultHasher::new();
            Hash::hash(unknown_fields, &mut hasher);
            hasher.finish()
        }

        assert_eq!(hash(&unknown_fields_1), hash(&unknown_fields_2));
    }
}
<|endoftext|>


<commit_before>//! Block by block CAST

use std::{default, iter, fmt};
use std::collections::LinkedList;

use super::c_simple;
use super::c_simple::{CAST, CASTNode};
use frontend::radeco_containers::RadecoFunction;
use petgraph::graph::{Graph, NodeIndex, EdgeIndex};
use petgraph::visit::EdgeRef;
use petgraph::EdgeDirection;
use petgraph::Direction;

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTNode {
    Action(ActionNode),
    Value(ValueNode),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionNode {
    Assignment,
    Call,
    Return,
    If,
    Goto,
}

#[derive(Debug, Clone, PartialEq)]
enum ValueNode {
    // The string is the name of variable
    Variable(String),
    // TODO
    Constant,
}

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTEdge {
    Action(ActionEdge),
    Value(ValueEdge),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionEdge {
    IfThen,
    IfElse,
    FuncCall,
    FuncRet,
    Normal,
}

#[derive(Debug, Clone, PartialEq)]
enum ValueEdge {
    AssignSrc,
    AssignDst,
    Arg(u8),
    GotoDst,
}

struct SimpleCAST {
    // Name of function of this AST
    fname: String,
    entry: NodeIndex,
    ast: Graph<SimpleCASTNode, SimpleCASTEdge>,
}

impl SimpleCASTNode {
    fn is_exit(&self) -> bool {
        match self {
            &SimpleCASTNode::Action(ActionNode::Return) => true,
            _ => false,
        }
    }
}

impl SimpleCAST {

    fn next_actions(&self, idx: NodeIndex) -> Vec<NodeIndex> {
        self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Action(_) => Some(e.target()),
                    _ => None,
                }
            }).collect()
    }

    /// Returns a pair (IfThen, IfElse)
    fn branch(&self, idx: NodeIndex) -> Option<(NodeIndex, Option<NodeIndex>)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::If)) {
            return None;
        }
        let ns = self.next_actions(idx);
        // TODO This implementation is not correct
        // specify node by matching IfThen, IfElse
        if ns.len() == 2 {
            Some((ns[0], Some(ns[1])))
        } else if ns.len() <= 1 {
            Some((ns[0], None))
        } else {
            radeco_warn!("ns.len() should return less than or equal 2");
            None
        }
    }

    /// Returns destination of goto statement
    fn goto(&self, idx: NodeIndex) -> Option<NodeIndex> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Goto))  {
            return None;
        };
        let goto_dsts = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::GotoDst) => Some(e.target()),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        if goto_dsts.len() != 1 {
            radeco_warn!("Destination of Goto has more than 1 labels");
            return None;
        };
        goto_dsts.first().map(|n| n.clone())
    }

    /// Returns a pair (Dst, Src) which represents Dst = Src
    fn assignment(&self, idx: NodeIndex) -> Option<(NodeIndex, NodeIndex)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Assignment)) {
            return None;
        }
        let src = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter(|e| e.weight() == &SimpleCASTEdge::Value(ValueEdge::AssignSrc))
            .map(|e| e.target())
            .next();
        let dst = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter(|e| e.weight() == &SimpleCASTEdge::Value(ValueEdge::AssignDst))
            .map(|e| e.target())
            .next();
        if src.is_some() && dst.is_some() {
            return None;
        }
        Some((dst.unwrap(), src.unwrap()))
    }

    /// Returns arguments of function call
    fn args_call(&self, idx: NodeIndex) -> Option<Vec<NodeIndex>> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Call)) {
            return None;
        }
        let mut args = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::Arg(o)) => Some((o, e.target())),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        args.sort_by_key(|k| k.0);
        let ret = args.into_iter()
            .map(|(_, b)| b)
            .collect();
        Some(ret)
    }

    fn to_c_ast(&self) -> CAST {
        let mut c_ast = CAST::new(&self.fname);
        let mut queue = LinkedList::new();
        queue.push_back(self.entry);
        while let Some(current_node) = queue.pop_front() {
            // XXX ?
            for n in self.next_actions(current_node) {
                queue.push_back(n);
            }
            match self.ast.node_weight(current_node) {
                Some(&SimpleCASTNode::Action(ActionNode::Assignment)) => {
                    if let Some((dst, src)) = self.assignment(current_node) {
                        // TODO insert node
                    } else {
                        // TODO
                        radeco_err!("Something wrong");
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Call)) => {
                    let args = self.args_call(current_node);
                    // TODO insert node, args
                },
                Some(&SimpleCASTNode::Action(ActionNode::Return)) => {
                    // TODO insert node
                },
                Some(&SimpleCASTNode::Action(ActionNode::If)) => {
                    // TODO insert node
                    if let Some((if_then, if_else)) = self.branch(current_node) {
                        // TODO
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Goto)) => {
                    if let Some(dst) = self.goto(current_node) {
                        // TODO insert label, gotos
                    }
                },
                _ => {},
            };
        }
        c_ast
    }

}
<commit_msg>update comment<commit_after>//! Block by block CAST

use std::{default, iter, fmt};
use std::collections::LinkedList;

use super::c_simple;
use super::c_simple::{CAST, CASTNode};
use frontend::radeco_containers::RadecoFunction;
use petgraph::graph::{Graph, NodeIndex, EdgeIndex};
use petgraph::visit::EdgeRef;
use petgraph::EdgeDirection;
use petgraph::Direction;

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTNode {
    Action(ActionNode),
    Value(ValueNode),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionNode {
    Assignment,
    Call,
    Return,
    If,
    Goto,
}

// TODO
#[derive(Debug, Clone, PartialEq)]
enum ValueNode {
    /// The string is the name of variable
    Variable(String),
    Constant,
}

#[derive(Debug, Clone, PartialEq)]
enum SimpleCASTEdge {
    Action(ActionEdge),
    Value(ValueEdge),
}

#[derive(Debug, Clone, PartialEq)]
enum ActionEdge {
    IfThen,
    IfElse,
    FuncCall,
    FuncRet,
    Normal,
}

#[derive(Debug, Clone, PartialEq)]
enum ValueEdge {
    AssignSrc,
    AssignDst,
    Arg(u8),
    GotoDst,
}

struct SimpleCAST {
    /// Name of function of this AST
    fname: String,
    entry: NodeIndex,
    ast: Graph<SimpleCASTNode, SimpleCASTEdge>,
}

impl SimpleCASTNode {
    fn is_exit(&self) -> bool {
        match self {
            &SimpleCASTNode::Action(ActionNode::Return) => true,
            _ => false,
        }
    }
}

impl SimpleCAST {

    fn next_actions(&self, idx: NodeIndex) -> Vec<NodeIndex> {
        self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Action(_) => Some(e.target()),
                    _ => None,
                }
            }).collect()
    }

    /// Returns a pair (IfThen, IfElse)
    fn branch(&self, idx: NodeIndex) -> Option<(NodeIndex, Option<NodeIndex>)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::If)) {
            return None;
        }
        let ns = self.next_actions(idx);
        // TODO This implementation is not correct
        // specify node by matching IfThen, IfElse
        if ns.len() == 2 {
            Some((ns[0], Some(ns[1])))
        } else if ns.len() <= 1 {
            Some((ns[0], None))
        } else {
            radeco_warn!("ns.len() should return less than or equal 2");
            None
        }
    }

    /// Returns destination of goto statement
    fn goto(&self, idx: NodeIndex) -> Option<NodeIndex> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Goto))  {
            return None;
        };
        let goto_dsts = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::GotoDst) => Some(e.target()),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        if goto_dsts.len() != 1 {
            radeco_warn!("Destination of Goto has more than 1 labels");
            return None;
        };
        goto_dsts.first().map(|n| n.clone())
    }

    /// Returns a pair (Dst, Src) which represents Dst = Src
    fn assignment(&self, idx: NodeIndex) -> Option<(NodeIndex, NodeIndex)> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Assignment)) {
            return None;
        }
        let src = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter(|e| e.weight() == &SimpleCASTEdge::Value(ValueEdge::AssignSrc))
            .map(|e| e.target())
            .next();
        let dst = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter(|e| e.weight() == &SimpleCASTEdge::Value(ValueEdge::AssignDst))
            .map(|e| e.target())
            .next();
        if src.is_some() && dst.is_some() {
            return None;
        }
        Some((dst.unwrap(), src.unwrap()))
    }

    /// Returns arguments of function call
    fn args_call(&self, idx: NodeIndex) -> Option<Vec<NodeIndex>> {
        if self.ast.node_weight(idx) != Some(&SimpleCASTNode::Action(ActionNode::Call)) {
            return None;
        }
        let mut args = self.ast.edges_directed(idx, Direction::Outgoing)
            .into_iter()
            .filter_map(|e| {
                match e.weight() {
                    &SimpleCASTEdge::Value(ValueEdge::Arg(o)) => Some((o, e.target())),
                    _ => None,
                }
            }).collect::<Vec<_>>();
        args.sort_by_key(|k| k.0);
        let ret = args.into_iter()
            .map(|(_, b)| b)
            .collect();
        Some(ret)
    }

    fn to_c_ast(&self) -> CAST {
        let mut c_ast = CAST::new(&self.fname);
        let mut queue = LinkedList::new();
        queue.push_back(self.entry);
        while let Some(current_node) = queue.pop_front() {
            // XXX ?
            for n in self.next_actions(current_node) {
                queue.push_back(n);
            }
            match self.ast.node_weight(current_node) {
                Some(&SimpleCASTNode::Action(ActionNode::Assignment)) => {
                    if let Some((dst, src)) = self.assignment(current_node) {
                        // TODO insert node
                    } else {
                        // TODO
                        radeco_err!("Something wrong");
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Call)) => {
                    let args = self.args_call(current_node);
                    // TODO insert node, args
                },
                Some(&SimpleCASTNode::Action(ActionNode::Return)) => {
                    // TODO insert node
                },
                Some(&SimpleCASTNode::Action(ActionNode::If)) => {
                    // TODO insert node
                    if let Some((if_then, if_else)) = self.branch(current_node) {
                        // TODO
                    }
                },
                Some(&SimpleCASTNode::Action(ActionNode::Goto)) => {
                    if let Some(dst) = self.goto(current_node) {
                        // TODO insert label, gotos
                    }
                },
                _ => {},
            };
        }
        c_ast
    }

}
<|endoftext|>


<commit_before>use crate::query_testing;
use anyhow::{Context, Result};
use std::{
    fs,
    io::{self, Write},
    ops::Range,
    path::Path,
};
use tree_sitter::{Language, Parser, Query, QueryCursor};

pub fn query_files_at_paths(
    language: Language,
    paths: Vec<String>,
    query_path: &Path,
    ordered_captures: bool,
    range: Option<Range<usize>>,
    should_test: bool,
) -> Result<()> {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();

    let query_source = fs::read_to_string(query_path)
        .with_context(|| format!("Error reading query file {:?}", query_path))?;
    let query = Query::new(language, &query_source).with_context(|| "Query compilation failed")?;

    let mut query_cursor = QueryCursor::new();
    if let Some(range) = range {
        query_cursor.set_byte_range(range);
    }

    let mut parser = Parser::new();
    parser.set_language(language)?;

    for path in paths {
        let mut results = Vec::new();

        writeln!(&mut stdout, "{}", path)?;

        let source_code =
            fs::read(&path).with_context(|| format!("Error reading source file {:?}", path))?;
        let tree = parser.parse(&source_code, None).unwrap();

        if ordered_captures {
            for (mat, capture_index) in
                query_cursor.captures(&query, tree.root_node(), source_code.as_slice())
            {
                let capture = mat.captures[capture_index];
                let capture_name = &query.capture_names()[capture.index as usize];
                writeln!(
                    &mut stdout,
                    "    pattern: {}, capture: {}, row: {}, text: {:?}",
                    mat.pattern_index,
                    capture_name,
                    capture.node.start_position().row,
                    capture.node.utf8_text(&source_code).unwrap_or("")
                )?;
                results.push(query_testing::CaptureInfo {
                    name: capture_name.to_string(),
                    start: capture.node.start_position(),
                    end: capture.node.end_position(),
                });
            }
        } else {
            for m in query_cursor.matches(&query, tree.root_node(), source_code.as_slice()) {
                writeln!(&mut stdout, "  pattern: {}", m.pattern_index)?;
                for capture in m.captures {
                    let start = capture.node.start_position();
                    let end = capture.node.end_position();
                    let capture_name = &query.capture_names()[capture.index as usize];
                    if end.row == start.row {
                        writeln!(
                            &mut stdout,
                            "    capture: {}, start: {}, text: {:?}",
                            capture_name,
                            start,
                            capture.node.utf8_text(&source_code).unwrap_or("")
                        )?;
                    } else {
                        writeln!(
                            &mut stdout,
                            "    capture: {}, start: {}, end: {}",
                            capture_name, start, end,
                        )?;
                    }
                    results.push(query_testing::CaptureInfo {
                        name: capture_name.to_string(),
                        start: capture.node.start_position(),
                        end: capture.node.end_position(),
                    });
                }
            }
        }
        if query_cursor.did_exceed_match_limit() {
            writeln!(
                &mut stdout,
                "  WARNING: Query exceeded maximum number of in-progress captures!"
            )?;
        }
        if should_test {
            query_testing::assert_expected_captures(results, path, &mut parser, language)?
        }
    }

    Ok(())
}
<commit_msg>cli(query): Improve and unify query subcommand output<commit_after>use crate::query_testing;
use anyhow::{Context, Result};
use std::{
    fs,
    io::{self, Write},
    ops::Range,
    path::Path,
};
use tree_sitter::{Language, Parser, Query, QueryCursor};

pub fn query_files_at_paths(
    language: Language,
    paths: Vec<String>,
    query_path: &Path,
    ordered_captures: bool,
    range: Option<Range<usize>>,
    should_test: bool,
) -> Result<()> {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();

    let query_source = fs::read_to_string(query_path)
        .with_context(|| format!("Error reading query file {:?}", query_path))?;
    let query = Query::new(language, &query_source).with_context(|| "Query compilation failed")?;

    let mut query_cursor = QueryCursor::new();
    if let Some(range) = range {
        query_cursor.set_byte_range(range);
    }

    let mut parser = Parser::new();
    parser.set_language(language)?;

    for path in paths {
        let mut results = Vec::new();

        writeln!(&mut stdout, "{}", path)?;

        let source_code =
            fs::read(&path).with_context(|| format!("Error reading source file {:?}", path))?;
        let tree = parser.parse(&source_code, None).unwrap();

        if ordered_captures {
            for (mat, capture_index) in
                query_cursor.captures(&query, tree.root_node(), source_code.as_slice())
            {
                let capture = mat.captures[capture_index];
                let capture_name = &query.capture_names()[capture.index as usize];
                writeln!(
                    &mut stdout,
                    "    pattern: {:>2}, capture: {} - {}, start: {}, end: {}, text: `{}`",
                    mat.pattern_index,
                    capture.index,
                    capture_name,
                    capture.node.start_position(),
                    capture.node.end_position(),
                    capture.node.utf8_text(&source_code).unwrap_or("")
                )?;
                results.push(query_testing::CaptureInfo {
                    name: capture_name.to_string(),
                    start: capture.node.start_position(),
                    end: capture.node.end_position(),
                });
            }
        } else {
            for m in query_cursor.matches(&query, tree.root_node(), source_code.as_slice()) {
                writeln!(&mut stdout, "  pattern: {}", m.pattern_index)?;
                for capture in m.captures {
                    let start = capture.node.start_position();
                    let end = capture.node.end_position();
                    let capture_name = &query.capture_names()[capture.index as usize];
                    if end.row == start.row {
                        writeln!(
                            &mut stdout,
                            "    capture: {} - {}, start: {}, end: {}, text: `{}`",
                            capture.index,
                            capture_name,
                            start,
                            end,
                            capture.node.utf8_text(&source_code).unwrap_or("")
                        )?;
                    } else {
                        writeln!(
                            &mut stdout,
                            "    capture: {}, start: {}, end: {}",
                            capture_name, start, end,
                        )?;
                    }
                    results.push(query_testing::CaptureInfo {
                        name: capture_name.to_string(),
                        start: capture.node.start_position(),
                        end: capture.node.end_position(),
                    });
                }
            }
        }
        if query_cursor.did_exceed_match_limit() {
            writeln!(
                &mut stdout,
                "  WARNING: Query exceeded maximum number of in-progress captures!"
            )?;
        }
        if should_test {
            query_testing::assert_expected_captures(results, path, &mut parser, language)?
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use hook::result::HookResult;
use store::FileLockEntry;
use storeid::StoreId;

pub trait StoreIdAccessor : Send + Sync {
    fn access(&self, &StoreId) -> HookResult<()>;
}

pub trait MutableHookDataAccessor : Send + Sync {
    fn access_mut(&self, &mut FileLockEntry) -> HookResult<()>;
}

pub trait NonMutableHookDataAccessor : Send + Sync {
    fn access(&self, &FileLockEntry) -> HookResult<()>;
}

pub enum HookDataAccessor<'a> {
    StoreIdAccess(&'a StoreIdAccessor),
    MutableAccess(&'a MutableHookDataAccessor),
    NonMutableAccess(&'a NonMutableHookDataAccessor),
}

pub trait HookDataAccessorProvider {
    fn accessor(&self) -> HookDataAccessor;
}

<commit_msg>Hook data accessors should implement Debug<commit_after>use std::fmt::Debug;

use hook::result::HookResult;
use store::FileLockEntry;
use storeid::StoreId;

pub trait StoreIdAccessor : Debug + Send + Sync {
    fn access(&self, &StoreId) -> HookResult<()>;
}

pub trait MutableHookDataAccessor : Debug + Send + Sync {
    fn access_mut(&self, &mut FileLockEntry) -> HookResult<()>;
}

pub trait NonMutableHookDataAccessor : Debug + Send + Sync {
    fn access(&self, &FileLockEntry) -> HookResult<()>;
}

#[derive(Debug)]
pub enum HookDataAccessor<'a> {
    StoreIdAccess(&'a StoreIdAccessor),
    MutableAccess(&'a MutableHookDataAccessor),
    NonMutableAccess(&'a NonMutableHookDataAccessor),
}

pub trait HookDataAccessorProvider {
    fn accessor(&self) -> HookDataAccessor;
}

<|endoftext|>