<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0 rls
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [CrateVersion] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_trans", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("aho-corasick"),
    Crate("ar"),
    Crate("atty"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("cmake"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("humantime"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("log"),
    Crate("log_settings"),
    Crate("memchr"),
    Crate("miniz-sys"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("quick-error"),
    Crate("rand"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("scoped-tls"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("tempdir"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("void"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl<'a> Crate<'a> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dep is in-tree
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist
    if must_be_on_whitelist {
        // If this dependency is not on the WHITELIST, add to bad set
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}
<commit_msg>handle license check<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0, rls
    "colored",            // MPL-2.0, rustfmt
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [CrateVersion] = &[
    CrateVersion("rustc", "0.0.0"),
    CrateVersion("rustc_trans", "0.0.0"),
];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("aho-corasick"),
    Crate("ar"),
    Crate("atty"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("cmake"),
    Crate("ena"),
    Crate("env_logger"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("humantime"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("log"),
    Crate("log_settings"),
    Crate("memchr"),
    Crate("miniz-sys"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("quick-error"),
    Crate("rand"),
    Crate("redox_syscall"),
    Crate("redox_termios"),
    Crate("regex"),
    Crate("regex-syntax"),
    Crate("remove_dir_all"),
    Crate("rustc-demangle"),
    Crate("scoped-tls"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("tempdir"),
    Crate("termcolor"),
    Crate("terminon"),
    Crate("termion"),
    Crate("thread_local"),
    Crate("ucd-util"),
    Crate("unicode-width"),
    Crate("unreachable"),
    Crate("utf8-ranges"),
    Crate("void"),
    Crate("winapi"),
    Crate("winapi-build"),
    Crate("winapi-i686-pc-windows-gnu"),
    Crate("winapi-x86_64-pc-windows-gnu"),
    Crate("wincolor"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct CrateVersion<'a>(&'a str, &'a str); // (name, version)

impl<'a> Crate<'a> {
    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

impl<'a> CrateVersion<'a> {
    /// Returns the struct and whether or not the dep is in-tree
    pub fn from_str(s: &'a str) -> (Self, bool) {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();
        let version = parts.next().unwrap();
        let path = parts.next().unwrap();

        let is_path_dep = path.starts_with("(path+");

        (CrateVersion(name, version), is_path_dep)
    }

    pub fn id_str(&self) -> String {
        format!("{} {}", self.0, self.1)
    }
}

impl<'a> From<CrateVersion<'a>> for Crate<'a> {
    fn from(cv: CrateVersion<'a>) -> Crate<'a> {
        Crate(cv.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate, false);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<CrateVersion<'a>>,
    krate: CrateVersion<'a>,
    must_be_on_whitelist: bool,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this path is in-tree, we don't require it to be on the whitelist
    if must_be_on_whitelist {
        // If this dependency is not on the WHITELIST, add to bad set
        if !whitelist.contains(&krate.into()) {
            unapproved.insert(krate.into());
        }
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let (krate, is_path_dep) = CrateVersion::from_str(dep);

        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate, !is_path_dep);
        unapproved.append(&mut bad);
    }

    unapproved
}
<|endoftext|>


<commit_before>#![feature(core_intrinsics)]

use std::panic::Location;

#[track_caller]
fn tracked() -> &'static Location<'static> {
    Location::caller() // most importantly, we never get line 7
}

fn nested_intrinsic() -> &'static Location<'static> {
    Location::caller()
}

fn nested_tracked() -> &'static Location<'static> {
    tracked()
}

macro_rules! caller_location_from_macro {
    () => {
        core::panic::Location::caller()
    };
}

fn test_fn_ptr() {
    fn pass_to_ptr_call<T>(f: fn(T), x: T) {
        f(x);
    }

    #[track_caller]
    fn tracked_unit(_: ()) {
        let expected_line = line!() - 1;
        let location = std::panic::Location::caller();
        assert_eq!(location.file(), file!());
        assert_eq!(location.line(), expected_line, "call shims report location as fn definition");
    }

    pass_to_ptr_call(tracked_unit, ());
}

fn test_trait_obj() {
    trait Tracked {
        #[track_caller]
        fn handle(&self) -> &'static Location<'static> {
            std::panic::Location::caller()
        }
    }

    impl Tracked for () {}
    impl Tracked for u8 {}

    // Test that we get the correct location
    // even with a call through a trait object

    let tracked: &dyn Tracked = &5u8;
    let location = tracked.handle();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 28);

    const TRACKED: &dyn Tracked = &();
    let location = TRACKED.handle();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 28);
}

fn main() {
    let location = Location::caller();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 20);

    let tracked = tracked();
    let expected_line = line!() - 1;
    assert_eq!(tracked.file(), file!());
    assert_eq!(tracked.line(), expected_line);
    assert_eq!(tracked.column(), 19);

    let nested = nested_intrinsic();
    assert_eq!(nested.file(), file!());
    assert_eq!(nested.line(), 11);
    assert_eq!(nested.column(), 5);

    let contained = nested_tracked();
    assert_eq!(contained.file(), file!());
    assert_eq!(contained.line(), 15);
    assert_eq!(contained.column(), 5);

    // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
    // i.e. point to where the macro was invoked, instead of the macro itself.
    let inmacro = caller_location_from_macro!();
    let expected_line = line!() - 1;
    assert_eq!(inmacro.file(), file!());
    assert_eq!(inmacro.line(), expected_line);
    assert_eq!(inmacro.column(), 19);

    let intrinsic = core::intrinsics::caller_location();
    let expected_line = line!() - 1;
    assert_eq!(intrinsic.file(), file!());
    assert_eq!(intrinsic.line(), expected_line);
    assert_eq!(intrinsic.column(), 21);

    test_fn_ptr();
    test_trait_obj();
}
<commit_msg>add more track_caller tests<commit_after>#![feature(core_intrinsics)]

use std::panic::Location;

#[track_caller]
fn tracked() -> &'static Location<'static> {
    Location::caller() // most importantly, we never get line 7
}

fn nested_intrinsic() -> &'static Location<'static> {
    Location::caller()
}

fn nested_tracked() -> &'static Location<'static> {
    tracked()
}

macro_rules! caller_location_from_macro {
    () => {
        core::panic::Location::caller()
    };
}

fn test_fn_ptr() {
    fn pass_to_ptr_call<T>(f: fn(T), x: T) {
        f(x);
    }

    #[track_caller]
    fn tracked_unit(_: ()) {
        let expected_line = line!() - 1;
        let location = std::panic::Location::caller();
        assert_eq!(location.file(), file!());
        assert_eq!(location.line(), expected_line, "call shims report location as fn definition");
    }

    pass_to_ptr_call(tracked_unit, ());
}

fn test_trait_obj() {
    trait Tracked {
        #[track_caller]
        fn handle(&self) -> &'static Location<'static> {
            std::panic::Location::caller()
        }
    }

    impl Tracked for () {}
    impl Tracked for u8 {}

    // Test that we get the correct location
    // even with a call through a trait object

    let tracked: &dyn Tracked = &5u8;
    let location = tracked.handle();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 28);

    const TRACKED: &dyn Tracked = &();
    let location = TRACKED.handle();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 28);
}

fn test_trait_obj2() {
    // track_caller on the impl but not the trait.
    pub trait Foo {
        fn foo(&self) -> &'static Location<'static>;
    }

    struct Bar;
    impl Foo for Bar {
        #[track_caller]
        fn foo(&self) -> &'static Location<'static> {
            std::panic::Location::caller()
        }
    }
    let expected_line = line!() - 4; // the `fn` signature above

    let f = &Bar as &dyn Foo;
    let loc = f.foo(); // trait doesn't track, so we don't point at this call site
    assert_eq!(loc.file(), file!());
    assert_eq!(loc.line(), expected_line);
}

fn main() {
    let location = Location::caller();
    let expected_line = line!() - 1;
    assert_eq!(location.file(), file!());
    assert_eq!(location.line(), expected_line);
    assert_eq!(location.column(), 20);

    let tracked = tracked();
    let expected_line = line!() - 1;
    assert_eq!(tracked.file(), file!());
    assert_eq!(tracked.line(), expected_line);
    assert_eq!(tracked.column(), 19);

    let nested = nested_intrinsic();
    assert_eq!(nested.file(), file!());
    assert_eq!(nested.line(), 11);
    assert_eq!(nested.column(), 5);

    let contained = nested_tracked();
    assert_eq!(contained.file(), file!());
    assert_eq!(contained.line(), 15);
    assert_eq!(contained.column(), 5);

    // `Location::caller()` in a macro should behave similarly to `file!` and `line!`,
    // i.e. point to where the macro was invoked, instead of the macro itself.
    let inmacro = caller_location_from_macro!();
    let expected_line = line!() - 1;
    assert_eq!(inmacro.file(), file!());
    assert_eq!(inmacro.line(), expected_line);
    assert_eq!(inmacro.column(), 19);

    let intrinsic = core::intrinsics::caller_location();
    let expected_line = line!() - 1;
    assert_eq!(intrinsic.file(), file!());
    assert_eq!(intrinsic.line(), expected_line);
    assert_eq!(intrinsic.column(), 21);

    test_fn_ptr();
    test_trait_obj();
    test_trait_obj2();
}
<|endoftext|>


<commit_before>use serde_derive::Deserialize;
use serde_yaml;
#[cfg(not(debug_assertions))]
use state_processing::{per_block_processing, per_slot_processing};
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::*;

#[derive(Debug, Deserialize)]
pub struct ExpectedState {
    pub slot: Option<Slot>,
    pub genesis_time: Option<u64>,
    pub fork: Option<Fork>,
    pub validator_registry: Option<Vec<Validator>>,
    pub validator_balances: Option<Vec<u64>>,
    pub previous_epoch_attestations: Option<Vec<PendingAttestation>>,
    pub current_epoch_attestations: Option<Vec<PendingAttestation>>,
    pub historical_roots: Option<Vec<Hash256>>,
    pub finalized_epoch: Option<Epoch>,
    pub latest_block_roots: Option<Vec<Hash256>>,
}

impl ExpectedState {
    // Return a list of fields that differ, and a string representation of the beacon state's field.
    fn check(&self, state: &BeaconState) -> Vec<(&str, String)> {
        // Check field equality
        macro_rules! cfe {
            ($field_name:ident) => {
                if self.$field_name.as_ref().map_or(true, |$field_name| {
                    println!("  > Checking {}", stringify!($field_name));
                    $field_name == &state.$field_name
                }) {
                    vec![]
                } else {
                    vec![(stringify!($field_name), format!("{:#?}", state.$field_name))]
                }
            };
        }

        vec![
            cfe!(slot),
            cfe!(genesis_time),
            cfe!(fork),
            cfe!(validator_registry),
            cfe!(validator_balances),
            cfe!(previous_epoch_attestations),
            cfe!(current_epoch_attestations),
            cfe!(historical_roots),
            cfe!(finalized_epoch),
            cfe!(latest_block_roots),
        ]
        .into_iter()
        .flat_map(|x| x)
        .collect()
    }
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
    pub name: String,
    pub config: ChainSpec,
    pub verify_signatures: bool,
    pub initial_state: BeaconState,
    pub blocks: Vec<BeaconBlock>,
    pub expected_state: ExpectedState,
}

#[derive(Debug, Deserialize)]
pub struct TestDoc {
    pub title: String,
    pub summary: String,
    pub fork: String,
    pub test_cases: Vec<TestCase>,
}

fn load_test_case(test_name: &str) -> TestDoc {
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(format!("yaml_utils/specs/{}", test_name));

        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    yaml_str = yaml_str.to_lowercase();

    serde_yaml::from_str(&yaml_str.as_str()).unwrap()
}

fn run_state_transition_test(test_name: &str) {
    let doc = load_test_case(test_name);

    // Run Tests
    let mut ok = true;
    for (i, test_case) in doc.test_cases.iter().enumerate() {
        let fake_crypto = cfg!(feature = "fake_crypto");
        if !test_case.verify_signatures == fake_crypto {
            println!("Running {}", test_case.name);
        } else {
            println!(
                "Skipping {} (fake_crypto: {}, need fake: {})",
                test_case.name, fake_crypto, !test_case.verify_signatures
            );
            continue;
        }
        let mut state = test_case.initial_state.clone();
        for (j, block) in test_case.blocks.iter().enumerate() {
            while block.slot > state.slot {
                per_slot_processing(&mut state, &test_case.config).unwrap();
            }
            let res = per_block_processing(&mut state, &block, &test_case.config);
            if res.is_err() {
                println!("Error in {} (#{}), on block {}", test_case.name, i, j);
                println!("{:?}", res);
                ok = false;
            }
        }

        let mismatched_fields = test_case.expected_state.check(&state);
        if !mismatched_fields.is_empty() {
            println!(
                "Error in expected state, these fields didn't match: {:?}",
                mismatched_fields.iter().map(|(f, _)| f).collect::<Vec<_>>()
            );
            for (field_name, state_val) in mismatched_fields {
                println!("state.{} was: {}", field_name, state_val);
            }
            ok = false;
        }
    }

    assert!(ok, "one or more tests failed, see above");
}

#[test]
#[cfg(not(debug_assertions))]
fn test_read_yaml() {
    load_test_case("sanity-check_small-config_32-vals.yaml");
    load_test_case("sanity-check_default-config_100-vals.yaml");
}

#[test]
#[cfg(not(debug_assertions))]
fn run_state_transition_tests_small() {
    run_state_transition_test("sanity-check_small-config_32-vals.yaml");
}

// Run with --ignored to run this test
#[test]
#[ignore]
fn run_state_transition_tests_large() {
    run_state_transition_test("sanity-check_default-config_100-vals.yaml");
}
<commit_msg>state transition tests: use TreeHashVector<commit_after>use serde_derive::Deserialize;
use serde_yaml;
#[cfg(not(debug_assertions))]
use state_processing::{per_block_processing, per_slot_processing};
use std::{fs::File, io::prelude::*, path::PathBuf};
use types::*;

#[derive(Debug, Deserialize)]
pub struct ExpectedState {
    pub slot: Option<Slot>,
    pub genesis_time: Option<u64>,
    pub fork: Option<Fork>,
    pub validator_registry: Option<Vec<Validator>>,
    pub validator_balances: Option<Vec<u64>>,
    pub previous_epoch_attestations: Option<Vec<PendingAttestation>>,
    pub current_epoch_attestations: Option<Vec<PendingAttestation>>,
    pub historical_roots: Option<Vec<Hash256>>,
    pub finalized_epoch: Option<Epoch>,
    pub latest_block_roots: Option<TreeHashVector<Hash256>>,
}

impl ExpectedState {
    // Return a list of fields that differ, and a string representation of the beacon state's field.
    fn check(&self, state: &BeaconState) -> Vec<(&str, String)> {
        // Check field equality
        macro_rules! cfe {
            ($field_name:ident) => {
                if self.$field_name.as_ref().map_or(true, |$field_name| {
                    println!("  > Checking {}", stringify!($field_name));
                    $field_name == &state.$field_name
                }) {
                    vec![]
                } else {
                    vec![(stringify!($field_name), format!("{:#?}", state.$field_name))]
                }
            };
        }

        vec![
            cfe!(slot),
            cfe!(genesis_time),
            cfe!(fork),
            cfe!(validator_registry),
            cfe!(validator_balances),
            cfe!(previous_epoch_attestations),
            cfe!(current_epoch_attestations),
            cfe!(historical_roots),
            cfe!(finalized_epoch),
            cfe!(latest_block_roots),
        ]
        .into_iter()
        .flat_map(|x| x)
        .collect()
    }
}

#[derive(Debug, Deserialize)]
pub struct TestCase {
    pub name: String,
    pub config: ChainSpec,
    pub verify_signatures: bool,
    pub initial_state: BeaconState,
    pub blocks: Vec<BeaconBlock>,
    pub expected_state: ExpectedState,
}

#[derive(Debug, Deserialize)]
pub struct TestDoc {
    pub title: String,
    pub summary: String,
    pub fork: String,
    pub test_cases: Vec<TestCase>,
}

fn load_test_case(test_name: &str) -> TestDoc {
    let mut file = {
        let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        file_path_buf.push(format!("yaml_utils/specs/{}", test_name));

        File::open(file_path_buf).unwrap()
    };
    let mut yaml_str = String::new();
    file.read_to_string(&mut yaml_str).unwrap();
    yaml_str = yaml_str.to_lowercase();

    serde_yaml::from_str(&yaml_str.as_str()).unwrap()
}

fn run_state_transition_test(test_name: &str) {
    let doc = load_test_case(test_name);

    // Run Tests
    let mut ok = true;
    for (i, test_case) in doc.test_cases.iter().enumerate() {
        let fake_crypto = cfg!(feature = "fake_crypto");
        if !test_case.verify_signatures == fake_crypto {
            println!("Running {}", test_case.name);
        } else {
            println!(
                "Skipping {} (fake_crypto: {}, need fake: {})",
                test_case.name, fake_crypto, !test_case.verify_signatures
            );
            continue;
        }
        let mut state = test_case.initial_state.clone();
        for (j, block) in test_case.blocks.iter().enumerate() {
            while block.slot > state.slot {
                per_slot_processing(&mut state, &test_case.config).unwrap();
            }
            let res = per_block_processing(&mut state, &block, &test_case.config);
            if res.is_err() {
                println!("Error in {} (#{}), on block {}", test_case.name, i, j);
                println!("{:?}", res);
                ok = false;
            }
        }

        let mismatched_fields = test_case.expected_state.check(&state);
        if !mismatched_fields.is_empty() {
            println!(
                "Error in expected state, these fields didn't match: {:?}",
                mismatched_fields.iter().map(|(f, _)| f).collect::<Vec<_>>()
            );
            for (field_name, state_val) in mismatched_fields {
                println!("state.{} was: {}", field_name, state_val);
            }
            ok = false;
        }
    }

    assert!(ok, "one or more tests failed, see above");
}

#[test]
#[cfg(not(debug_assertions))]
fn test_read_yaml() {
    load_test_case("sanity-check_small-config_32-vals.yaml");
    load_test_case("sanity-check_default-config_100-vals.yaml");
}

#[test]
#[cfg(not(debug_assertions))]
fn run_state_transition_tests_small() {
    run_state_transition_test("sanity-check_small-config_32-vals.yaml");
}

// Run with --ignored to run this test
#[test]
#[ignore]
fn run_state_transition_tests_large() {
    run_state_transition_test("sanity-check_default-config_100-vals.yaml");
}
<|endoftext|>


<commit_before>use crate::ptr::write_bytes;

pub(super) trait SpecFill<T> {
    fn spec_fill(&mut self, value: T);
}

impl<T: Clone> SpecFill<T> for [T] {
    default fn spec_fill(&mut self, value: T) {
        if let Some((last, elems)) = self.split_last_mut() {
            for el in elems {
                el.clone_from(&value);
            }

            *last = value
        }
    }
}

impl<T: Copy> SpecFill<T> for [T] {
    default fn spec_fill(&mut self, value: T) {
        for item in self.iter_mut() {
            *item = value;
        }
    }
}

impl SpecFill<u8> for [u8] {
    fn spec_fill(&mut self, value: u8) {
        // SAFETY: this is slice of u8
        unsafe {
            let ptr = self.as_mut_ptr();
            let len = self.len();
            write_bytes(ptr, value, len);
        }
    }
}

impl SpecFill<i8> for [i8] {
    fn spec_fill(&mut self, value: i8) {
        // SAFETY: this is slice of i8
        unsafe {
            let ptr = self.as_mut_ptr();
            let len = self.len();
            write_bytes(ptr, value as u8, len);
        }
    }
}

impl SpecFill<bool> for [bool] {
    fn spec_fill(&mut self, value: bool) {
        // SAFETY: this is slice of bool
        unsafe {
            let ptr = self.as_mut_ptr();
            let len = self.len();
            write_bytes(ptr, value as u8, len);
        }
    }
}
<commit_msg>generalize slice::fill specialization for byte-sized items<commit_after>use crate::mem::{size_of, transmute_copy};
use crate::ptr::write_bytes;

pub(super) trait SpecFill<T> {
    fn spec_fill(&mut self, value: T);
}

impl<T: Clone> SpecFill<T> for [T] {
    default fn spec_fill(&mut self, value: T) {
        if let Some((last, elems)) = self.split_last_mut() {
            for el in elems {
                el.clone_from(&value);
            }

            *last = value
        }
    }
}

impl<T: Copy> SpecFill<T> for [T] {
    fn spec_fill(&mut self, value: T) {
        if size_of::<T>() == 1 {
            // SAFETY: The size_of check above ensures that values are 1 byte wide, as required
            // for the transmute and write_bytes
            unsafe {
                let value: u8 = transmute_copy(&value);
                write_bytes(self.as_mut_ptr(), value, self.len());
            }
        } else {
            for item in self.iter_mut() {
                *item = value;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use rand::Rng;
use std::f64::consts::PI;

// `(f32, f32)` would be faster for some RNGs (including `rand::thread_rng` on 32-bit platforms
// and `rand::weak_rng`), but less accurate.
fn is_inside_circle((x, y): (f64, f64)) -> bool {
    x * x + y * y <= 1.0
}

fn simulate<R: Rng>(rng: &mut R, samples: usize) -> f64 {
    let mut count = 0;
    for _ in 0..samples {
        count += is_inside_circle(rng.gen()) as usize;
    }
    (count as f64) / (samples as f64)
}

fn main() {
    let mut rng = rand::weak_rng();

    println!("Real pi: {}", PI);

    for samples in (3..9).map(|e| 10_usize.pow(e)) {
        let estimate = 4.0 * simulate(&mut rng, samples);
        let deviation = 100.0 * (1.0 - estimate / PI).abs();
        println!("{:9}: {:<11} dev: {:.5}%", samples, estimate, deviation);
    }
}
<commit_msg>improve comments<commit_after>extern crate rand;

use rand::Rng;
use std::f64::consts::PI;

// `(f32, f32)` would be faster for some RNGs (including `rand::thread_rng` on 32-bit platforms
// and `rand::weak_rng` as of rand v0.4) as `next_u64` combines two `next_u32`s if not natively
// supported by the RNG.  It would less accurate however.
fn is_inside_circle((x, y): (f64, f64)) -> bool {
    x * x + y * y <= 1.0
}

fn simulate<R: Rng>(rng: &mut R, samples: usize) -> f64 {
    let mut count = 0;
    for _ in 0..samples {
        count += is_inside_circle(rng.gen()) as usize;
    }
    (count as f64) / (samples as f64)
}

fn main() {
    let mut rng = rand::weak_rng();

    println!("Real pi: {}", PI);

    for samples in (3..9).map(|e| 10_usize.pow(e)) {
        let estimate = 4.0 * simulate(&mut rng, samples);
        let deviation = 100.0 * (1.0 - estimate / PI).abs();
        println!("{:9}: {:<11} dev: {:.5}%", samples, estimate, deviation);
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::io;
use rand::{Rng, thread_rng};
use Choice::*;

#[derive(PartialEq, Clone, Copy)]
pub enum Choice {
    Rock,
    Paper,
    Scissors,
}

impl Choice {
    fn print(&self) -> &str {
        match *self {
            Rock => "Rock",
            Paper => "Paper",
            Scissors => "Scissors",
        }
    }

    fn rand(&mut self) {
        match thread_rng().gen_range(0, 2) {
            0 => *self = Rock,
            1 => *self = Paper,
            2 | _ => *self = Scissors,
        }
    }
}

fn beats(c1: Choice, c2: Choice) -> bool {
    (c1 == Rock) && (c2 == Scissors) || (c1 == Scissors && c2 == Paper) ||
    (c1 == Paper && c2 == Rock)
}

fn ai_move(v: [usize; 3]) -> Choice {
    // weighted random choice
    let sum = thread_rng().gen_range(0, v[0] + v[1] + v[2]);
    if sum < v[0] {
        Paper
    } else if sum < v[0] + v[1] {
        Scissors
    } else {
        Rock
    }
}

fn main() {
    println!("Rock, paper, scissors!");
    let mut aichoice: Choice = Rock;
    aichoice.rand();
    let mut uchoice: Choice;
    let mut ucf: [usize; 3] = [0, 0, 0]; //user choice frequency
    let mut score: [usize; 2] = [0, 0];
    println!("Please input your move: r, p or s. Type q to quit");
    loop {
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("failed to read line");
        match input.to_lowercase().trim() {
            s if s.starts_with('r') => {
                uchoice = Rock;
                ucf[0] += 1
            }
            s if s.starts_with('p') => {
                uchoice = Paper;
                ucf[1] += 1
            }
            s if s.starts_with('s') => {
                uchoice = Scissors;
                ucf[2] += 1
            }
            s if s.starts_with('q') => break,
            _ => {
                println!("Please enter correct choice!");
                continue;
            }
        };
        println!("You chose {}, I chose {}.",
                 uchoice.print(),
                 aichoice.print());
        if beats(uchoice, aichoice) {
            score[0] += 1;
            println!("You win!");
        } else if uchoice == aichoice {
            println!("It's a tie!");
        } else {
            score[1] += 1;
            println!("I win!");
        }
        println!("-Score: You {}, Me {}", score[0], score[1]);

        aichoice = ai_move(ucf);
        // only after the 1st iteration ai knows the stats and can make
        // its weighted random move
    }
    println!("Thank you for the game!");
}
<commit_msg>update rock paper scissors<commit_after>extern crate rand;
#[macro_use]
extern crate rand_derive;

use std::io;
use rand::Rng;
use Choice::*;

#[derive(PartialEq, Clone, Copy, Rand, Debug)]
pub enum Choice {
    Rock,
    Paper,
    Scissors,
}

fn beats(c1: Choice, c2: Choice) -> bool {
    (c1 == Rock && c2 == Scissors) || (c1 == Scissors && c2 == Paper) || (c1 == Paper && c2 == Rock)
}

fn ai_move<R: Rng>(rng: &mut R, v: [usize; 3]) -> Choice {
    // weighted random choice, a dynamic version of `rand::distributions::WeightedChoice`
    let rand = rng.gen_range(0, v[0] + v[1] + v[2]);
    if rand < v[0] {
        Paper
    } else if rand < v[0] + v[1] {
        Scissors
    } else {
        Rock
    }
}

fn main() {
    let mut rng = rand::thread_rng();

    println!("Rock, paper, scissors!");
    let mut ai_choice: Choice = rng.gen();
    let mut ucf = [0, 0, 0]; // user choice frequency
    let mut score = [0, 0];

    loop {
        println!("Please input your move: 'r', 'p' or 's'. Type 'q' to quit");

        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("failed to read line");
        let u_choice = match input.to_lowercase().trim() {
            s if s.starts_with('r') => {
                ucf[0] += 1;
                Rock
            }
            s if s.starts_with('p') => {
                ucf[1] += 1;
                Paper
            }
            s if s.starts_with('s') => {
                ucf[2] += 1;
                Scissors
            }
            s if s.starts_with('q') => break,
            _ => {
                println!("Please enter a correct choice!");
                continue;
            }
        };
        println!("You chose {:?}, I chose {:?}.", u_choice, ai_choice);
        if beats(u_choice, ai_choice) {
            score[0] += 1;
            println!("You win!");
        } else if u_choice == ai_choice {
            println!("It's a tie!");
        } else {
            score[1] += 1;
            println!("I win!");
        }
        println!("-Score: You {}, Me {}", score[0], score[1]);

        // only after the 1st iteration the AI knows the stats and can make
        // its weighted random move
        ai_choice = ai_move(&mut rng, ucf);
    }
    println!("Thank you for the game!");
}

#[test]
fn test_victory() {
    assert!(beats(Scissors, Paper));
    assert!(beats(Rock, Scissors));
    assert!(beats(Paper, Rock));
}
<|endoftext|>


<commit_before>use rustc::{declare_lint, hir, lint, lint_array};
use crate::utils;
use std::fmt;

/// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
/// `isize`.
///
/// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
/// cast by using the `add` method instead.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.offset(offset as isize); }
/// ```
///
/// Could be written:
///
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.add(offset); }
/// ```
declare_clippy_lint! {
    pub PTR_OFFSET_WITH_CAST,
    complexity,
    "uneeded pointer offset cast"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl lint::LintPass for Pass {
    fn get_lints(&self) -> lint::LintArray {
        lint_array!(PTR_OFFSET_WITH_CAST)
    }
}

impl<'a, 'tcx> lint::LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &lint::LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
            Some(call_arg) => call_arg,
            None => return,
        };

        // Check if the argument to the method call is a cast from usize
        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
            Some(cast_lhs_expr) => cast_lhs_expr,
            None => return,
        };

        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
            utils::span_lint_and_sugg(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg, "try", sugg);
        } else {
            utils::span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
        }

    }
}

// If the given expression is a cast from a usize, return the lhs of the cast
fn expr_as_cast_from_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<&'tcx hir::Expr> {
    if let hir::ExprKind::Cast(ref cast_lhs_expr, _) = expr.node {
        if is_expr_ty_usize(cx, &cast_lhs_expr) {
            return Some(cast_lhs_expr);
        }
    }
    None
}

// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
// receiver, the arg of the method call, and the method.
fn expr_as_ptr_offset_call<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<(&'tcx hir::Expr, &'tcx hir::Expr, Method)> {
    if let hir::ExprKind::MethodCall(ref path_segment, _, ref args) = expr.node {
        if is_expr_ty_raw_ptr(cx, &args[0]) {
            if path_segment.ident.name == "offset" {
                return Some((&args[0], &args[1], Method::Offset));
            }
            if path_segment.ident.name == "wrapping_offset" {
                return Some((&args[0], &args[1], Method::WrappingOffset));
            }
        }
    }
    None
}

// Is the type of the expression a usize?
fn is_expr_ty_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr) == cx.tcx.types.usize
}

// Is the type of the expression a raw pointer?
fn is_expr_ty_raw_ptr<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr).is_unsafe_ptr()
}

fn build_suggestion<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    method: Method,
    receiver_expr: &hir::Expr,
    cast_lhs_expr: &hir::Expr,
) -> Option<String> {
    let receiver = utils::snippet_opt(cx, receiver_expr.span)?;
    let cast_lhs = utils::snippet_opt(cx, cast_lhs_expr.span)?;
    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
}

enum Method {
    Offset,
    WrappingOffset,
}

impl Method {
    fn suggestion(&self) -> &'static str {
        match *self {
            Method::Offset => "add",
            Method::WrappingOffset => "wrapping_add",
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Method::Offset => write!(f, "offset"),
            Method::WrappingOffset => write!(f, "wrapping_offset"),
        }
    }
}
<commit_msg>derive copy/clone<commit_after>use rustc::{declare_lint, hir, lint, lint_array};
use crate::utils;
use std::fmt;

/// **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an
/// `isize`.
///
/// **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric
/// cast by using the `add` method instead.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.offset(offset as isize); }
/// ```
///
/// Could be written:
///
/// ```rust
/// let vec = vec![b'a', b'b', b'c'];
/// let ptr = vec.as_ptr();
/// let offset = 1_usize;
///
/// unsafe { ptr.add(offset); }
/// ```
declare_clippy_lint! {
    pub PTR_OFFSET_WITH_CAST,
    complexity,
    "uneeded pointer offset cast"
}

#[derive(Copy, Clone, Debug)]
pub struct Pass;

impl lint::LintPass for Pass {
    fn get_lints(&self) -> lint::LintArray {
        lint_array!(PTR_OFFSET_WITH_CAST)
    }
}

impl<'a, 'tcx> lint::LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &lint::LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
        let (receiver_expr, arg_expr, method) = match expr_as_ptr_offset_call(cx, expr) {
            Some(call_arg) => call_arg,
            None => return,
        };

        // Check if the argument to the method call is a cast from usize
        let cast_lhs_expr = match expr_as_cast_from_usize(cx, arg_expr) {
            Some(cast_lhs_expr) => cast_lhs_expr,
            None => return,
        };

        let msg = format!("use of `{}` with a `usize` casted to an `isize`", method);
        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
            utils::span_lint_and_sugg(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg, "try", sugg);
        } else {
            utils::span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
        }

    }
}

// If the given expression is a cast from a usize, return the lhs of the cast
fn expr_as_cast_from_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<&'tcx hir::Expr> {
    if let hir::ExprKind::Cast(ref cast_lhs_expr, _) = expr.node {
        if is_expr_ty_usize(cx, &cast_lhs_expr) {
            return Some(cast_lhs_expr);
        }
    }
    None
}

// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
// receiver, the arg of the method call, and the method.
fn expr_as_ptr_offset_call<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &'tcx hir::Expr,
) -> Option<(&'tcx hir::Expr, &'tcx hir::Expr, Method)> {
    if let hir::ExprKind::MethodCall(ref path_segment, _, ref args) = expr.node {
        if is_expr_ty_raw_ptr(cx, &args[0]) {
            if path_segment.ident.name == "offset" {
                return Some((&args[0], &args[1], Method::Offset));
            }
            if path_segment.ident.name == "wrapping_offset" {
                return Some((&args[0], &args[1], Method::WrappingOffset));
            }
        }
    }
    None
}

// Is the type of the expression a usize?
fn is_expr_ty_usize<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr) == cx.tcx.types.usize
}

// Is the type of the expression a raw pointer?
fn is_expr_ty_raw_ptr<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    expr: &hir::Expr,
) -> bool {
    cx.tables.expr_ty(expr).is_unsafe_ptr()
}

fn build_suggestion<'a, 'tcx>(
    cx: &lint::LateContext<'a, 'tcx>,
    method: Method,
    receiver_expr: &hir::Expr,
    cast_lhs_expr: &hir::Expr,
) -> Option<String> {
    let receiver = utils::snippet_opt(cx, receiver_expr.span)?;
    let cast_lhs = utils::snippet_opt(cx, cast_lhs_expr.span)?;
    Some(format!("{}.{}({})", receiver, method.suggestion(), cast_lhs))
}

#[derive(Copy, Clone)]
enum Method {
    Offset,
    WrappingOffset,
}

impl Method {
    fn suggestion(&self) -> &'static str {
        match *self {
            Method::Offset => "add",
            Method::WrappingOffset => "wrapping_add",
        }
    }
}

impl fmt::Display for Method {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Method::Offset => write!(f, "offset"),
            Method::WrappingOffset => write!(f, "wrapping_offset"),
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>perfect-numbers stub<commit_after>#[derive(Debug, PartialEq, Eq)]
pub enum Classification {
    Abundant,
    Perfect,
    Deficient
}

pub fn classify(num: u64) -> Result<Classification, & 'static str> {
    unimplemented!();
}
<|endoftext|>


<commit_before>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
    mir::{self, interpret::ConstAlloc},
    ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
        bug!("intern_const_alloc_recursive should not error in this case")
    }
    ConstValue::Scalar(loc_place.ptr)
}

/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let ecx = mk_eval_cx(
        tcx, DUMMY_SP, param_env,
        // It is absolutely crucial for soundness that
        // we do not read from static items or other mutable memory.
        false,
    );
    let place = ecx.raw_const_to_mplace(raw).unwrap();
    const_to_valtree_inner(&ecx, &place)
}

fn const_to_valtree_inner<'tcx>(
    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
    place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let branches = |n, variant| {
        let place = match variant {
            Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
            None => *place,
        };
        let variant =
            variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
        let fields = (0..n).map(|i| {
            let field = ecx.mplace_field(&place, i).unwrap();
            const_to_valtree_inner(ecx, &field)
        });
        // For enums, we preped their variant index before the variant's fields so we can figure out
        // the variant again when just seeing a valtree.
        let branches = variant.into_iter().chain(fields);
        Some(ty::ValTree::Branch(
            ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
        ))
    };
    match place.layout.ty.kind() {
        ty::FnDef(..) => Some(ty::ValTree::zst()),
        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
            let val = ecx.read_immediate(&place.into()).unwrap();
            let val = val.to_scalar().unwrap();
            Some(ty::ValTree::Leaf(val.assert_int()))
        }

        // Raw pointers are not allowed in type level constants, as we cannot properly test them for
        // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
        // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
        // agree with runtime equality tests.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

        // Trait objects are not allowed in type level constants, as we have no concept for
        // resolving their backing type, even if we can do that at const eval time. We may
        // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
        // but it is unclear if this is useful.
        ty::Dynamic(..) => None,

        ty::Slice(_) | ty::Str => {
            unimplemented!("need to find the backing data of the slice/str and recurse on that")
        }
        ty::Tuple(substs) => branches(substs.len(), None),
        ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),

        ty::Adt(def, _) => {
            if def.variants.is_empty() {
                bug!("uninhabited types should have errored and never gotten converted to valtree")
            }

            let variant = ecx.read_discriminant(&place.into()).unwrap().1;

            branches(def.variants[variant].fields.len(), Some(variant))
        }

        ty::Never
        | ty::Error(_)
        | ty::Foreign(..)
        | ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Bound(..)
        | ty::Placeholder(..)
        // FIXME(oli-obk): we could look behind opaque types
        | ty::Opaque(..)
        | ty::Infer(_)
        // FIXME(oli-obk): we can probably encode closures just like structs
        | ty::Closure(..)
        | ty::Generator(..)
        | ty::GeneratorWitness(..) => None,
    }
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(&op).unwrap().1;
            let down = ecx.operand_downcast(&op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(&down, i).unwrap();
        let val = op_to_const(&ecx, &field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(&op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
<commit_msg>Only emit a discrimiant tag for enums<commit_after>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{
    mir::{self, interpret::ConstAlloc},
    ty::ScalarInt,
};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
        bug!("intern_const_alloc_recursive should not error in this case")
    }
    ConstValue::Scalar(loc_place.ptr)
}

/// Convert an evaluated constant to a type level constant
pub(crate) fn const_to_valtree<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    raw: ConstAlloc<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let ecx = mk_eval_cx(
        tcx, DUMMY_SP, param_env,
        // It is absolutely crucial for soundness that
        // we do not read from static items or other mutable memory.
        false,
    );
    let place = ecx.raw_const_to_mplace(raw).unwrap();
    const_to_valtree_inner(&ecx, &place)
}

fn const_to_valtree_inner<'tcx>(
    ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
    place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
    let branches = |n, variant| {
        let place = match variant {
            Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
            None => *place,
        };
        let variant =
            variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
        let fields = (0..n).map(|i| {
            let field = ecx.mplace_field(&place, i).unwrap();
            const_to_valtree_inner(ecx, &field)
        });
        // For enums, we preped their variant index before the variant's fields so we can figure out
        // the variant again when just seeing a valtree.
        let branches = variant.into_iter().chain(fields);
        Some(ty::ValTree::Branch(
            ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?),
        ))
    };
    match place.layout.ty.kind() {
        ty::FnDef(..) => Some(ty::ValTree::zst()),
        ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
            let val = ecx.read_immediate(&place.into()).unwrap();
            let val = val.to_scalar().unwrap();
            Some(ty::ValTree::Leaf(val.assert_int()))
        }

        // Raw pointers are not allowed in type level constants, as we cannot properly test them for
        // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
        // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
        // agree with runtime equality tests.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

        // Trait objects are not allowed in type level constants, as we have no concept for
        // resolving their backing type, even if we can do that at const eval time. We may
        // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
        // but it is unclear if this is useful.
        ty::Dynamic(..) => None,

        ty::Slice(_) | ty::Str => {
            unimplemented!("need to find the backing data of the slice/str and recurse on that")
        }
        ty::Tuple(substs) => branches(substs.len(), None),
        ty::Array(_, len) => branches(usize::try_from(len.eval_usize(ecx.tcx.tcx, ecx.param_env)).unwrap(), None),

        ty::Adt(def, _) => {
            if def.variants.is_empty() {
                bug!("uninhabited types should have errored and never gotten converted to valtree")
            }

            let variant = ecx.read_discriminant(&place.into()).unwrap().1;

            branches(def.variants[variant].fields.len(), def.is_enum().then_some(variant))
        }

        ty::Never
        | ty::Error(_)
        | ty::Foreign(..)
        | ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Bound(..)
        | ty::Placeholder(..)
        // FIXME(oli-obk): we could look behind opaque types
        | ty::Opaque(..)
        | ty::Infer(_)
        // FIXME(oli-obk): we can probably encode closures just like structs
        | ty::Closure(..)
        | ty::Generator(..)
        | ty::GeneratorWitness(..) => None,
    }
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(&op).unwrap().1;
            let down = ecx.operand_downcast(&op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(&down, i).unwrap();
        let val = op_to_const(&ecx, &field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(&op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast};
use dom::bindings::js::JS;
use dom::bindings::trace::RootedVec;
use dom::event::{Event, EventPhase};
use dom::eventtarget::{EventTarget, ListenerPhase};
use dom::node::Node;
use dom::virtualmethods::vtable_for;

fn dispatch_to_listeners(event: &Event, target: &EventTarget, chain: &[&EventTarget]) {
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    let type_ = event.Type();

    /* capturing */
    event.set_phase(EventPhase::Capturing);
    for cur_target in chain.iter().rev() {
        if let Some(listeners) = cur_target.get_listeners_for(&type_, ListenerPhase::Capturing) {
            event.set_current_target(cur_target);
            for listener in &listeners {
                // Explicitly drop any exception on the floor.
                listener.call_or_handle_event(*cur_target, event, Report);

                if event.stop_immediate() {
                    break;
                }
            }

            if event.stop_propagation() {
                break;
            }
        }
    }
    if event.stop_propagation() {
        return;
    }

    /* at target */
    event.set_phase(EventPhase::AtTarget);
    event.set_current_target(target.clone());

    if let Some(listeners) = target.get_listeners(&type_) {
        for listener in listeners {
            // Explicitly drop any exception on the floor.
            listener.call_or_handle_event(target, event, Report);

            if event.stop_immediate() {
                break;
            }
        }
    }
    if event.stop_propagation() {
        return;
    }

    /* bubbling */
    if event.bubbles() {
        event.set_phase(EventPhase::Bubbling);

        for cur_target in chain {
            if let Some(listeners) = cur_target.get_listeners_for(&type_, ListenerPhase::Bubbling) {
                event.set_current_target(cur_target);
                for listener in &listeners {
                    // Explicitly drop any exception on the floor.
                    listener.call_or_handle_event(*cur_target, event, Report);

                    if event.stop_immediate() {
                        break;
                    }
                }

                if event.stop_propagation() {
                    break;
                }
            }
        }
    }
}

// See https://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: &EventTarget, pseudo_target: Option<&EventTarget>,
                      event: &Event) -> bool {
    assert!(!event.dispatching());
    assert!(event.initialized());
    assert_eq!(event.phase(), EventPhase::None);
    assert!(event.GetCurrentTarget().is_none());

    event.set_target(match pseudo_target {
        Some(pseudo_target) => pseudo_target,
        None => target.clone(),
    });

    if event.stop_propagation() {
        return !event.DefaultPrevented();
    }

    event.set_dispatching(true);

    //TODO: no chain if not participating in a tree
    let mut chain: RootedVec<JS<EventTarget>> = RootedVec::new();
    if let Some(target_node) = NodeCast::to_ref(target) {
        for ancestor in target_node.ancestors() {
            let ancestor_target = EventTargetCast::from_ref(ancestor.r());
            chain.push(JS::from_ref(ancestor_target))
        }
    }

    dispatch_to_listeners(event, target, chain.r());

    /* default action */
    let target = event.GetTarget();
    match target {
        Some(ref target) => {
            let node: Option<&Node> = NodeCast::to_ref(target.r());
            match node {
                Some(node) => {
                    let vtable = vtable_for(&node);
                    vtable.handle_event(event);
                }
                None => {}
            }
        }
        None => {}
    }

    event.set_dispatching(false);
    event.set_phase(EventPhase::None);
    event.clear_current_target();

    !event.DefaultPrevented()
}
<commit_msg>Return immediately from dispatch_to_listeners if propagation is stopped.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast};
use dom::bindings::js::JS;
use dom::bindings::trace::RootedVec;
use dom::event::{Event, EventPhase};
use dom::eventtarget::{EventTarget, ListenerPhase};
use dom::node::Node;
use dom::virtualmethods::vtable_for;

fn dispatch_to_listeners(event: &Event, target: &EventTarget, chain: &[&EventTarget]) {
    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    let type_ = event.Type();

    /* capturing */
    event.set_phase(EventPhase::Capturing);
    for cur_target in chain.iter().rev() {
        if let Some(listeners) = cur_target.get_listeners_for(&type_, ListenerPhase::Capturing) {
            event.set_current_target(cur_target);
            for listener in &listeners {
                // Explicitly drop any exception on the floor.
                listener.call_or_handle_event(*cur_target, event, Report);

                if event.stop_immediate() {
                    return;
                }
            }

            if event.stop_propagation() {
                return;
            }
        }
    }

    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    /* at target */
    event.set_phase(EventPhase::AtTarget);
    event.set_current_target(target.clone());

    if let Some(listeners) = target.get_listeners(&type_) {
        for listener in listeners {
            // Explicitly drop any exception on the floor.
            listener.call_or_handle_event(target, event, Report);

            if event.stop_immediate() {
                return;
            }
        }
        if event.stop_propagation() {
            return;
        }
    }

    assert!(!event.stop_propagation());
    assert!(!event.stop_immediate());

    /* bubbling */
    if event.bubbles() {
        event.set_phase(EventPhase::Bubbling);

        for cur_target in chain {
            if let Some(listeners) = cur_target.get_listeners_for(&type_, ListenerPhase::Bubbling) {
                event.set_current_target(cur_target);
                for listener in &listeners {
                    // Explicitly drop any exception on the floor.
                    listener.call_or_handle_event(*cur_target, event, Report);

                    if event.stop_immediate() {
                        return;
                    }
                }

                if event.stop_propagation() {
                    return;
                }
            }
        }
    }
}

// See https://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: &EventTarget, pseudo_target: Option<&EventTarget>,
                      event: &Event) -> bool {
    assert!(!event.dispatching());
    assert!(event.initialized());
    assert_eq!(event.phase(), EventPhase::None);
    assert!(event.GetCurrentTarget().is_none());

    event.set_target(match pseudo_target {
        Some(pseudo_target) => pseudo_target,
        None => target.clone(),
    });

    if event.stop_propagation() {
        return !event.DefaultPrevented();
    }

    event.set_dispatching(true);

    //TODO: no chain if not participating in a tree
    let mut chain: RootedVec<JS<EventTarget>> = RootedVec::new();
    if let Some(target_node) = NodeCast::to_ref(target) {
        for ancestor in target_node.ancestors() {
            let ancestor_target = EventTargetCast::from_ref(ancestor.r());
            chain.push(JS::from_ref(ancestor_target))
        }
    }

    dispatch_to_listeners(event, target, chain.r());

    /* default action */
    let target = event.GetTarget();
    match target {
        Some(ref target) => {
            let node: Option<&Node> = NodeCast::to_ref(target.r());
            match node {
                Some(node) => {
                    let vtable = vtable_for(&node);
                    vtable.handle_event(event);
                }
                None => {}
            }
        }
        None => {}
    }

    event.set_dispatching(false);
    event.set_phase(EventPhase::None);
    event.clear_current_target();

    !event.DefaultPrevented()
}
<|endoftext|>


<commit_before>use crate::server::ServerData;
use crate::Database;

use actix_web::{error::ResponseError, get, http::StatusCode, web, HttpRequest, HttpResponse};
use mongodb::{oid::ObjectId, ordered::OrderedDocument, Bson};
use serde::{de, Deserialize, Deserializer};
use serde_qs::actix::QsQuery;

#[get("")]
pub fn get_courses(
    data: web::Data<ServerData>,
    query: QsQuery<GetCourses2>,
    req: HttpRequest,
) -> Result<String, GetCourses2Error> {
    data.get_courses2(query.into_inner())
}

#[derive(Deserialize, Debug)]
pub struct GetCourses2 {
    #[serde(default)]
    limit: Limit,
    skip: Option<u32>,
    id: Option<String>,
    ids: Option<Vec<String>>,
    title: Option<String>,
    owner: Option<String>,
    uploader: Option<String>,
    sort: Option<Vec<Sort>>,
}

impl GetCourses2 {
    pub fn into_ordered_document(
        self,
        database: &Database,
    ) -> Result<Vec<OrderedDocument>, GetCourses2Error> {
        let mut pipeline = vec![];

        if let Some(pipeline_match) = self.get_match(database)? {
            pipeline.push(doc! { "$match" => pipeline_match });
        }

        pipeline.push(self.get_sort_doc());

        let limit = self.get_limit();
        pipeline.push(doc! {
            "$limit" => limit
        });

        if let Some(skip) = self.skip {
            pipeline.push(doc! {
                "$skip" => skip
            });
        }

        Ok(pipeline)
    }

    fn get_match(&self, database: &Database) -> Result<Option<OrderedDocument>, GetCourses2Error> {
        let mut res = doc! {};
        if let Some(id) = &self.id {
            GetCourses2::insert_objectid(&mut res, "_id".to_string(), id)?;
        }

        if let Some(ids) = self.ids.clone() {
            let ids: Vec<Bson> = ids
                .iter()
                .map(|id| -> Result<Bson, GetCourses2Error> {
                    let object_id = ObjectId::with_string(id)
                        .map_err(|_| GetCourses2Error::DeserializeError("ids".to_string()))?;
                    Ok(Bson::ObjectId(object_id))
                })
                .filter_map(Result::ok)
                .collect();
            res.insert_bson(
                "_id".to_string(),
                Bson::Document(doc! {
                    "$in" => ids
                }),
            );
        }

        if let Some(title) = self.title.clone() {
            GetCourses2::insert_regexp(&mut res, "title".to_string(), title);
        }

        if let Some(owner) = &self.owner {
            GetCourses2::insert_objectid(&mut res, "owner".to_string(), owner)?;
        }

        if let Some(uploader) = &self.uploader {
            let filter = doc! {
                "username" => Bson::RegExp(format!("^{}$", uploader), "i".to_string())
            };
            match database.find_account(filter) {
                Some(account) => {
                    res.insert_bson("owner".to_string(), Bson::ObjectId(account.get_id()));
                }
                None => return Err(GetCourses2Error::UploaderUnknown(uploader.clone())),
            };
        }

        if res.is_empty() {
            Ok(None)
        } else {
            Ok(Some(res))
        }
    }

    fn get_sort_doc(&self) -> OrderedDocument {
        let mut query = OrderedDocument::new();
        for sort in self.get_sort() {
            query.insert(sort.val, sort.dir);
        }
        doc! {
            "$sort" => query
        }
    }

    fn get_sort(&self) -> Vec<Sort> {
        if self.sort.is_some() {
            self.sort.clone().unwrap()
        } else {
            vec![Sort::default()]
        }
    }

    fn get_limit(&self) -> u32 {
        self.limit.0 + self.skip.unwrap_or_default()
    }

    fn insert_regexp(doc: &mut OrderedDocument, key: String, regexp: String) {
        doc.insert_bson(
            key,
            Bson::RegExp(format!(".*{}.*", regexp), "i".to_string()),
        );
    }

    fn insert_objectid(
        doc: &mut OrderedDocument,
        key: String,
        oid: &str,
    ) -> Result<(), GetCourses2Error> {
        doc.insert_bson(
            key.clone(),
            Bson::ObjectId(
                ObjectId::with_string(oid).map_err(|_| GetCourses2Error::DeserializeError(key))?,
            ),
        );
        Ok(())
    }
}

#[derive(Deserialize, Debug)]
struct Limit(#[serde(deserialize_with = "deserialize_limit")] u32);

impl Default for Limit {
    fn default() -> Limit {
        Limit(120)
    }
}

fn deserialize_limit<'de, D>(de: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    let val = u32::deserialize(de)?;
    if val == 0 {
        Err(de::Error::invalid_value(
            de::Unexpected::Unsigned(val.into()),
            &"limit must be at least 1",
        ))
    } else if val > 120 {
        Err(de::Error::invalid_value(
            de::Unexpected::Unsigned(val.into()),
            &"limit must be at most 120",
        ))
    } else {
        Ok(val)
    }
}

#[derive(Clone, Deserialize, Debug)]
struct Sort {
    val: String,
    #[serde(deserialize_with = "deserialize_dir")]
    dir: i32,
}

impl Default for Sort {
    fn default() -> Self {
        Sort {
            val: "last_modified".to_string(),
            dir: -1,
        }
    }
}

fn deserialize_dir<'de, D>(de: D) -> Result<i32, D::Error>
where
    D: Deserializer<'de>,
{
    let val = i32::deserialize(de)?;
    if val != -1 && val != 1 {
        Err(de::Error::invalid_value(
            de::Unexpected::Signed(val.into()),
            &"sort direction must either be -1 or 1",
        ))
    } else {
        Ok(val)
    }
}

#[derive(Fail, Debug)]
pub enum GetCourses2Error {
    #[fail(display = "could not deserialize {} from hex string", _0)]
    DeserializeError(String),
    #[fail(display = "uploader with name {} unknown", _0)]
    UploaderUnknown(String),
    #[fail(display = "[PutCourses2Error::SerdeJson]: {}", _0)]
    SerdeJson(serde_json::Error),
    #[fail(display = "[GetCourses2Error::Mongo]: {}", _0)]
    Mongo(mongodb::Error),
}

impl From<serde_json::Error> for GetCourses2Error {
    fn from(err: serde_json::Error) -> Self {
        GetCourses2Error::SerdeJson(err)
    }
}

impl From<mongodb::Error> for GetCourses2Error {
    fn from(err: mongodb::Error) -> Self {
        GetCourses2Error::Mongo(err)
    }
}

impl ResponseError for GetCourses2Error {
    fn error_response(&self) -> HttpResponse {
        match *self {
            GetCourses2Error::DeserializeError(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::UploaderUnknown(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::SerdeJson(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::Mongo(_) => HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR),
        }
    }
}
<commit_msg>fix unused warning<commit_after>use crate::server::ServerData;
use crate::Database;

use actix_web::{error::ResponseError, get, http::StatusCode, web, HttpRequest, HttpResponse};
use mongodb::{oid::ObjectId, ordered::OrderedDocument, Bson};
use serde::{de, Deserialize, Deserializer};
use serde_qs::actix::QsQuery;

#[get("")]
pub fn get_courses(
    data: web::Data<ServerData>,
    query: QsQuery<GetCourses2>,
    _req: HttpRequest,
) -> Result<String, GetCourses2Error> {
    data.get_courses2(query.into_inner())
}

#[derive(Deserialize, Debug)]
pub struct GetCourses2 {
    #[serde(default)]
    limit: Limit,
    skip: Option<u32>,
    id: Option<String>,
    ids: Option<Vec<String>>,
    title: Option<String>,
    owner: Option<String>,
    uploader: Option<String>,
    sort: Option<Vec<Sort>>,
}

impl GetCourses2 {
    pub fn into_ordered_document(
        self,
        database: &Database,
    ) -> Result<Vec<OrderedDocument>, GetCourses2Error> {
        let mut pipeline = vec![];

        if let Some(pipeline_match) = self.get_match(database)? {
            pipeline.push(doc! { "$match" => pipeline_match });
        }

        pipeline.push(self.get_sort_doc());

        let limit = self.get_limit();
        pipeline.push(doc! {
            "$limit" => limit
        });

        if let Some(skip) = self.skip {
            pipeline.push(doc! {
                "$skip" => skip
            });
        }

        Ok(pipeline)
    }

    fn get_match(&self, database: &Database) -> Result<Option<OrderedDocument>, GetCourses2Error> {
        let mut res = doc! {};
        if let Some(id) = &self.id {
            GetCourses2::insert_objectid(&mut res, "_id".to_string(), id)?;
        }

        if let Some(ids) = self.ids.clone() {
            let ids: Vec<Bson> = ids
                .iter()
                .map(|id| -> Result<Bson, GetCourses2Error> {
                    let object_id = ObjectId::with_string(id)
                        .map_err(|_| GetCourses2Error::DeserializeError("ids".to_string()))?;
                    Ok(Bson::ObjectId(object_id))
                })
                .filter_map(Result::ok)
                .collect();
            res.insert_bson(
                "_id".to_string(),
                Bson::Document(doc! {
                    "$in" => ids
                }),
            );
        }

        if let Some(title) = self.title.clone() {
            GetCourses2::insert_regexp(&mut res, "title".to_string(), title);
        }

        if let Some(owner) = &self.owner {
            GetCourses2::insert_objectid(&mut res, "owner".to_string(), owner)?;
        }

        if let Some(uploader) = &self.uploader {
            let filter = doc! {
                "username" => Bson::RegExp(format!("^{}$", uploader), "i".to_string())
            };
            match database.find_account(filter) {
                Some(account) => {
                    res.insert_bson("owner".to_string(), Bson::ObjectId(account.get_id()));
                }
                None => return Err(GetCourses2Error::UploaderUnknown(uploader.clone())),
            };
        }

        if res.is_empty() {
            Ok(None)
        } else {
            Ok(Some(res))
        }
    }

    fn get_sort_doc(&self) -> OrderedDocument {
        let mut query = OrderedDocument::new();
        for sort in self.get_sort() {
            query.insert(sort.val, sort.dir);
        }
        doc! {
            "$sort" => query
        }
    }

    fn get_sort(&self) -> Vec<Sort> {
        if self.sort.is_some() {
            self.sort.clone().unwrap()
        } else {
            vec![Sort::default()]
        }
    }

    fn get_limit(&self) -> u32 {
        self.limit.0 + self.skip.unwrap_or_default()
    }

    fn insert_regexp(doc: &mut OrderedDocument, key: String, regexp: String) {
        doc.insert_bson(
            key,
            Bson::RegExp(format!(".*{}.*", regexp), "i".to_string()),
        );
    }

    fn insert_objectid(
        doc: &mut OrderedDocument,
        key: String,
        oid: &str,
    ) -> Result<(), GetCourses2Error> {
        doc.insert_bson(
            key.clone(),
            Bson::ObjectId(
                ObjectId::with_string(oid).map_err(|_| GetCourses2Error::DeserializeError(key))?,
            ),
        );
        Ok(())
    }
}

#[derive(Deserialize, Debug)]
struct Limit(#[serde(deserialize_with = "deserialize_limit")] u32);

impl Default for Limit {
    fn default() -> Limit {
        Limit(120)
    }
}

fn deserialize_limit<'de, D>(de: D) -> Result<u32, D::Error>
where
    D: Deserializer<'de>,
{
    let val = u32::deserialize(de)?;
    if val == 0 {
        Err(de::Error::invalid_value(
            de::Unexpected::Unsigned(val.into()),
            &"limit must be at least 1",
        ))
    } else if val > 120 {
        Err(de::Error::invalid_value(
            de::Unexpected::Unsigned(val.into()),
            &"limit must be at most 120",
        ))
    } else {
        Ok(val)
    }
}

#[derive(Clone, Deserialize, Debug)]
struct Sort {
    val: String,
    #[serde(deserialize_with = "deserialize_dir")]
    dir: i32,
}

impl Default for Sort {
    fn default() -> Self {
        Sort {
            val: "last_modified".to_string(),
            dir: -1,
        }
    }
}

fn deserialize_dir<'de, D>(de: D) -> Result<i32, D::Error>
where
    D: Deserializer<'de>,
{
    let val = i32::deserialize(de)?;
    if val != -1 && val != 1 {
        Err(de::Error::invalid_value(
            de::Unexpected::Signed(val.into()),
            &"sort direction must either be -1 or 1",
        ))
    } else {
        Ok(val)
    }
}

#[derive(Fail, Debug)]
pub enum GetCourses2Error {
    #[fail(display = "could not deserialize {} from hex string", _0)]
    DeserializeError(String),
    #[fail(display = "uploader with name {} unknown", _0)]
    UploaderUnknown(String),
    #[fail(display = "[PutCourses2Error::SerdeJson]: {}", _0)]
    SerdeJson(serde_json::Error),
    #[fail(display = "[GetCourses2Error::Mongo]: {}", _0)]
    Mongo(mongodb::Error),
}

impl From<serde_json::Error> for GetCourses2Error {
    fn from(err: serde_json::Error) -> Self {
        GetCourses2Error::SerdeJson(err)
    }
}

impl From<mongodb::Error> for GetCourses2Error {
    fn from(err: mongodb::Error) -> Self {
        GetCourses2Error::Mongo(err)
    }
}

impl ResponseError for GetCourses2Error {
    fn error_response(&self) -> HttpResponse {
        match *self {
            GetCourses2Error::DeserializeError(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::UploaderUnknown(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::SerdeJson(_) => HttpResponse::new(StatusCode::BAD_REQUEST),
            GetCourses2Error::Mongo(_) => HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR),
        }
    }
}
<|endoftext|>


<commit_before>use std::os::unix::net::UnixStream;
use std::sync::mpsc;
use std::thread;

use hyper::server::Server as HyperServer;
use hyperlocal::UnixSocketServer;
use ruru::{Proc, VM};

use config::{BindingType, Config};
use core::{Core, Handler, ResponseFuture};
use request::Request;

pub struct Server {
    config: Config,
}

impl Server {
    pub fn new(config: Config) -> Self {
        Server { config: config }
    }

    pub fn listen(self, ruby_handler: Proc) {
        println!("[rust] Start listening");

        let (handler_stream, core_stream) = UnixStream::pair().unwrap();

        let (request_sender, request_receiver) = mpsc::channel::<Request>();
        let (response_future_sender, response_future_receiver) = mpsc::channel::<ResponseFuture>();

        let handler = Handler::new(request_sender, response_future_receiver, handler_stream);

        let mut core = Core::new(response_future_sender,
                                 request_receiver,
                                 ruby_handler,
                                 core_stream,
                                 self.config.thread_pool_size());

        thread::spawn(move || {
            let handler_function = || -> () {
                println!("[hyper] GVL released for server thread");

                let thread_pool_size = self.config.thread_pool_size();

                println!("[hyper] Spawning {} native thread(s)", thread_pool_size);

                match *self.config.binding_type() {
                    BindingType::Unix => {
                        UnixSocketServer::new(self.config.listen_on()).unwrap()
                            .handle_threads(handler, thread_pool_size).unwrap();
                    },
                    BindingType::Tcp => {
                        HyperServer::http(self.config.listen_on()).unwrap()
                            .handle_threads(handler, thread_pool_size).unwrap();
                    }
                };
            };

            let unblock_function = || {
                ()
            };

            VM::thread_call_without_gvl(
                handler_function,
                Some(unblock_function)
            );
        });

        core.run();
    }
}
<commit_msg>Show binding address on startup<commit_after>use std::os::unix::net::UnixStream;
use std::sync::mpsc;
use std::thread;

use hyper::server::Server as HyperServer;
use hyperlocal::UnixSocketServer;
use ruru::{Proc, VM};

use config::{BindingType, Config};
use core::{Core, Handler, ResponseFuture};
use request::Request;

pub struct Server {
    config: Config,
}

impl Server {
    pub fn new(config: Config) -> Self {
        Server { config: config }
    }

    pub fn listen(self, ruby_handler: Proc) {
        println!("[rust] Start listening");

        let (handler_stream, core_stream) = UnixStream::pair().unwrap();

        let (request_sender, request_receiver) = mpsc::channel::<Request>();
        let (response_future_sender, response_future_receiver) = mpsc::channel::<ResponseFuture>();

        let handler = Handler::new(request_sender, response_future_receiver, handler_stream);

        let mut core = Core::new(response_future_sender,
                                 request_receiver,
                                 ruby_handler,
                                 core_stream,
                                 self.config.thread_pool_size());

        thread::spawn(move || {
            let handler_function = || -> () {
                println!("[hyper] GVL released for server thread");

                let address = self.config.listen_on();
                let thread_pool_size = self.config.thread_pool_size();

                println!("[hyper] Spawning {} native thread(s)", thread_pool_size);
                println!("[hyper] Listening on {}", address);

                match *self.config.binding_type() {
                    BindingType::Unix => {
                        UnixSocketServer::new(address).unwrap()
                            .handle_threads(handler, thread_pool_size).unwrap();
                    },
                    BindingType::Tcp => {
                        HyperServer::http(address).unwrap()
                            .handle_threads(handler, thread_pool_size).unwrap();
                    }
                };
            };

            let unblock_function = || {
                ()
            };

            VM::thread_call_without_gvl(
                handler_function,
                Some(unblock_function)
            );
        });

        core.run();
    }
}
<|endoftext|>