<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_infallible::Mutex;
use diem_rate_limiter::{
    async_lib::AsyncRateLimiter,
    rate_limit::{Bucket, SharedBucket},
};
use futures::{channel::mpsc::channel, executor::block_on, Future, SinkExt, StreamExt};
use std::{
    sync::Arc,
    time::{Duration, Instant},
};
use tokio::{
    io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
    net::{TcpListener, TcpStream},
    runtime::Runtime,
};

// Parameters for running the tests, change the port if there's a conflict
/// Where to run the test read/write
const ADDRESS: &str = "localhost:7777";
/// Total # of bytes to transfer.  May transfer more, but won't transfer less
const BYTES_TO_TRANSFER: usize = 1_000_000;
/// Expected throughput to be configured on the rate limiter
const ALLOWED_BYTES_PER_SEC: usize = 10_000;
/// Chunk size to write bytes to the stream at a time
const CHUNK_SIZE: usize = 10_000;

/// Holder for statistics of a single experiment and reader/writer
struct RunStats {
    pub label: &'static str,
    pub num_actions: usize,
    pub num_bytes: usize,
    pub actions_per_s: f64,
    pub bytes_per_s: f64,
    pub elapsed: Duration,
}

/// A main function for testing rate limiting throughput and backpressure
fn main() {
    diem_logger::DiemLogger::init_for_testing();
    println!("Starting experiments");
    println!(
        "Bytes to test: {}, Expected Throughput(actions/s): {}, Expected Throughput(bytes/s): {}, ",
        BYTES_TO_TRANSFER,
        ALLOWED_BYTES_PER_SEC / CHUNK_SIZE,
        ALLOWED_BYTES_PER_SEC
    );

    let runtime = Runtime::new().unwrap();

    run_experiment(
        "No Read or Write Limiting",
        &runtime,
        test_no_rate_limit(BYTES_TO_TRANSFER),
    );
    run_experiment(
        "Read Limited",
        &runtime,
        test_rate_limit_read(BYTES_TO_TRANSFER, ALLOWED_BYTES_PER_SEC),
    );
    run_experiment(
        "Write Limited",
        &runtime,
        test_rate_limit_write(BYTES_TO_TRANSFER, ALLOWED_BYTES_PER_SEC),
    );
    run_experiment(
        "Read & Write Limited",
        &runtime,
        test_rate_limit_read_write(BYTES_TO_TRANSFER, ALLOWED_BYTES_PER_SEC),
    );
}

/// Runs an experiment and prints out the run stats
fn run_experiment<F: Future<Output = Vec<RunStats>> + 'static + Send>(
    label: &'static str,
    runtime: &Runtime,
    experiment: F,
) {
    println!("\n== {} ==", label);
    let stats = block_on(runtime.spawn(experiment)).expect("Expect experiment to finish");
    for run in stats {
        println!(
            "{} stats\t|\tTotal actions: {}\t|\tTotal bytes: {}\t|\tThroughput(actions/s): {}\t|\tThroughput(bytes/s): {}\t|\tTime elapsed: {:?}",
            run.label, run.num_actions, run.num_bytes, run.actions_per_s, run.bytes_per_s, run.elapsed
        );
    }
}

/// A helper to get stats of a read / write operation
async fn stats<F: Future<Output = (usize, usize)>, Block: FnOnce() -> F>(
    label: &'static str,
    block: Block,
) -> RunStats {
    let start = Instant::now();
    let (num_actions, num_bytes) = block().await;
    let end = Instant::now();

    let elapsed = end - start;
    let elapsed_s = (elapsed.as_millis() as f64) / 1000.0;

    let actions_per_s = num_actions as f64 / elapsed_s;
    let bytes_per_s = num_bytes as f64 / elapsed_s;

    RunStats {
        label,
        num_actions,
        num_bytes,
        actions_per_s,
        bytes_per_s,
        elapsed,
    }
}

/// A generalized test of the rate limiter, allowing for turning off rate limiting on either
/// read or write sides.  `total_test_bytes` is the lower bound of send / receive bytes
async fn test_rate_limiter<
    R: AsyncRead + Unpin + Send,
    W: AsyncWrite + Unpin + Send,
    Reader: FnOnce(TcpStream) -> R + Send + 'static,
    Writer: FnOnce(TcpStream) -> W + Send + 'static,
>(
    total_test_bytes: usize,
    reader: Reader,
    writer: Writer,
) -> Vec<RunStats> {
    // Startup the socket connections first, and the stats line
    let mut listener = TcpListener::bind(ADDRESS)
        .await
        .expect("Must bind to address");
    let out_stream = TcpStream::connect(ADDRESS)
        .await
        .expect("Must connect to address");

    // Shrink the buffer to increase the affects of backpressure
    out_stream
        .set_send_buffer_size(CHUNK_SIZE)
        .expect("Should be able to change buffer size");
    let mut writer = writer(out_stream);

    let (stats_sender, mut stats_receiver) = channel(2);
    let mut read_stats = stats_sender.clone();
    let mut write_stats = stats_sender.clone();

    // Ensure we're listening first
    let read_future = async move {
        let in_stream = listener
            .next()
            .await
            .expect("Should not have an error connecting")
            .expect("Should have an incoming connection");

        // Shrink the buffer to increase the affects of backpressure
        in_stream
            .set_recv_buffer_size(CHUNK_SIZE)
            .expect("Should be able to change buffer size");

        // Read in the expected number of bytes
        let mut reader = reader(in_stream);
        let stats = stats("Read", || async {
            let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
            let mut num_reads: usize = 0;
            let mut num_bytes: usize = 0;
            loop {
                num_bytes = num_bytes.saturating_add(reader.read(&mut buf).await.unwrap());
                num_reads = num_reads.saturating_add(1);
                if num_bytes >= total_test_bytes {
                    break;
                }
            }
            (num_reads, num_bytes)
        })
        .await;
        read_stats.send(stats).await.unwrap();
    };

    let write_future = async move {
        // Write the expected number of bytes
        let stats = stats("Write", || async {
            let source: [u8; CHUNK_SIZE] = [1; CHUNK_SIZE];
            let mut num_writes: usize = 0;
            let mut num_bytes: usize = 0;
            loop {
                num_bytes = num_bytes.saturating_add(writer.write(&source).await.unwrap());
                num_writes = num_writes.saturating_add(1);
                if num_bytes >= total_test_bytes {
                    break;
                }
            }
            (num_writes, num_bytes)
        })
        .await;
        write_stats.send(stats).await.unwrap();
    };

    // Wait for both to finish
    futures::future::join(read_future, write_future).await;

    // Output statistics from the reader & writer
    let mut results: Vec<RunStats> = vec![];
    for _ in 0u8..2u8 {
        results.push(stats_receiver.next().await.unwrap());
    }
    results
}

fn simple_shared_bucket(label: &'static str, size: usize) -> SharedBucket {
    Arc::new(Mutex::new(Bucket::new(label.to_string(), size, size, size)))
}

/// For comparison, no rate limiting
async fn test_no_rate_limit(num_bytes: usize) -> Vec<RunStats> {
    test_rate_limiter(num_bytes, |reader| reader, |writer| writer).await
}

/// Tests backpressure, where only the reader is limited.  Using a socket adds a buffer
/// that causes the backpressure only to occur after a large amount of traffic first. (> 500k bytes)
/// > 55 chunks could cause backpressure, for a proper test > 100 or > 200 give good results.
async fn test_rate_limit_read(num_bytes: usize, throughput: usize) -> Vec<RunStats> {
    let inbound_rate_limiter = simple_shared_bucket("read", throughput);
    test_rate_limiter(
        num_bytes,
        |reader| AsyncRateLimiter::new(reader, Some(inbound_rate_limiter)),
        |writer| writer,
    )
    .await
}

/// Tests only if the writer is rate limited, which should provide a smoother read rate
async fn test_rate_limit_write(num_bytes: usize, throughput: usize) -> Vec<RunStats> {
    let outbound_rate_limiter = simple_shared_bucket("write", throughput);
    test_rate_limiter(
        num_bytes,
        |reader| reader,
        |writer| AsyncRateLimiter::new(writer, Some(outbound_rate_limiter)),
    )
    .await
}

/// Tests if both are rate limited.  Results should be roughly the same in the long term as
/// the other two
async fn test_rate_limit_read_write(num_bytes: usize, throughput: usize) -> Vec<RunStats> {
    let inbound_rate_limiter = simple_shared_bucket("read", throughput);
    let outbound_rate_limiter = simple_shared_bucket("write", throughput);
    test_rate_limiter(
        num_bytes,
        |reader| AsyncRateLimiter::new(reader, Some(inbound_rate_limiter)),
        |writer| AsyncRateLimiter::new(writer, Some(outbound_rate_limiter)),
    )
    .await
}
<commit_msg>[rate-limiter] Update tests for more scenarios<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_infallible::Mutex;
use diem_rate_limiter::{
    async_lib::AsyncRateLimiter,
    rate_limit::{Bucket, SharedBucket},
};
use futures::{channel::mpsc::channel, executor::block_on, Future, SinkExt, StreamExt};
use std::{
    sync::Arc,
    time::{Duration, Instant},
};
use tokio::{
    io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
    net::{TcpListener, TcpStream},
    runtime::Runtime,
};

// Parameters for running the tests, change the port if there's a conflict
/// Where to run the test read/write
const ADDRESS: &str = "localhost:7777";
/// Total # of bytes to transfer.  May transfer more, but won't transfer less
const BYTES_TO_TRANSFER: usize = 100_000_000;
/// Expected throughput to be configured on the rate limiter
const ALLOWED_BYTES_PER_SEC: usize = 10_000;
/// Chunk size to write bytes to the stream at a time
const CHUNK_SIZE: usize = 10_000;
/// Buffer size used by Tokio when reading, backpressure only occurs after this is full
const READ_BUFFER_SIZE: usize = 100;
/// Buffer size used by Tokio when writing
const WRITE_BUFFER_SIZE: usize = 100;

/// Holder for statistics of a single experiment and reader/writer
struct RunStats {
    pub label: &'static str,
    pub num_actions: usize,
    pub num_bytes: usize,
    pub actions_per_s: f64,
    pub bytes_per_s: f64,
    pub elapsed: Duration,
}

/// A main function for testing rate limiting throughput and backpressure
fn main() {
    diem_logger::DiemLogger::init_for_testing();
    println!("Starting experiments");
    println!(
        "Bytes to test: {}, Expected Throughput(actions/s): {}, Expected Throughput(bytes/s): {}",
        BYTES_TO_TRANSFER,
        ALLOWED_BYTES_PER_SEC as f64 / CHUNK_SIZE as f64,
        ALLOWED_BYTES_PER_SEC
    );

    let runtime = Runtime::new().unwrap();

    run_experiment(
        "No Read or Write Limiting",
        &runtime,
        test_no_rate_limit(BYTES_TO_TRANSFER),
    );
    run_experiment(
        "Read Limited",
        &runtime,
        test_rate_limit_read(BYTES_TO_TRANSFER, ALLOWED_BYTES_PER_SEC),
    );
    run_experiment(
        "Write Limited",
        &runtime,
        test_rate_limit_write(BYTES_TO_TRANSFER, ALLOWED_BYTES_PER_SEC),
    );
    run_experiment(
        "Read & Write Limited",
        &runtime,
        test_rate_limit_read_write(
            BYTES_TO_TRANSFER,
            ALLOWED_BYTES_PER_SEC,
            ALLOWED_BYTES_PER_SEC,
        ),
    );
    run_experiment(
        "Write 2x Read Limited",
        &runtime,
        test_rate_limit_read_write(
            BYTES_TO_TRANSFER,
            ALLOWED_BYTES_PER_SEC,
            ALLOWED_BYTES_PER_SEC.saturating_mul(2),
        ),
    );
    run_experiment(
        "Read 2x Write Limited",
        &runtime,
        test_rate_limit_read_write(
            BYTES_TO_TRANSFER,
            ALLOWED_BYTES_PER_SEC.saturating_mul(2),
            ALLOWED_BYTES_PER_SEC,
        ),
    );
}

/// Runs an experiment and prints out the run stats
fn run_experiment<F: Future<Output = Vec<RunStats>> + 'static + Send>(
    label: &'static str,
    runtime: &Runtime,
    experiment: F,
) {
    println!("\n== {} ==", label);
    let stats = block_on(runtime.spawn(experiment)).expect("Expect experiment to finish");
    println!("-- Stats --");
    for run in stats {
        println!(
            "{} stats\t|\tTotal actions: {}\t|\tTotal bytes: {}\t|\tThroughput(actions/s): {}\t|\tThroughput(bytes/s): {}\t|\tTime elapsed: {:?}",
            run.label, run.num_actions, run.num_bytes, run.actions_per_s, run.bytes_per_s, run.elapsed
        );
    }
}

/// A helper to get stats of a read / write operation
async fn stats<F: Future<Output = (usize, usize)>, Block: FnOnce() -> F>(
    label: &'static str,
    block: Block,
) -> RunStats {
    let start = Instant::now();
    let (num_actions, num_bytes) = block().await;
    let end = Instant::now();

    let elapsed = end - start;
    let elapsed_s = (elapsed.as_millis() as f64) / 1000.0;

    let actions_per_s = num_actions as f64 / elapsed_s;
    let bytes_per_s = num_bytes as f64 / elapsed_s;

    RunStats {
        label,
        num_actions,
        num_bytes,
        actions_per_s,
        bytes_per_s,
        elapsed,
    }
}

/// A generalized test of the rate limiter, allowing for turning off rate limiting on either
/// read or write sides.  `total_test_bytes` is the lower bound of send / receive bytes
async fn test_rate_limiter<
    R: AsyncRead + Unpin + Send,
    W: AsyncWrite + Unpin + Send,
    Reader: FnOnce(TcpStream) -> R + Send + 'static,
    Writer: FnOnce(TcpStream) -> W + Send + 'static,
>(
    total_test_bytes: usize,
    reader: Reader,
    writer: Writer,
) -> Vec<RunStats> {
    // Startup the socket connections first, and the stats line
    let mut listener = TcpListener::bind(ADDRESS)
        .await
        .expect("Must bind to address");
    let out_stream = TcpStream::connect(ADDRESS)
        .await
        .expect("Must connect to address");

    // Shrink the buffer to increase the affects of backpressure
    out_stream
        .set_send_buffer_size(WRITE_BUFFER_SIZE)
        .expect("Should be able to change buffer size");
    let mut writer = writer(out_stream);

    let (stats_sender, mut stats_receiver) = channel(2);
    let mut read_stats = stats_sender.clone();
    let mut write_stats = stats_sender.clone();

    // Ensure we're listening first
    let read_future = async move {
        let in_stream = listener
            .next()
            .await
            .expect("Should not have an error connecting")
            .expect("Should have an incoming connection");

        // Shrink the buffer to increase the affects of backpressure
        in_stream
            .set_recv_buffer_size(READ_BUFFER_SIZE)
            .expect("Should be able to change buffer size");

        // Read in the expected number of bytes
        let mut reader = reader(in_stream);
        let stats = stats("Read", || async {
            let mut buf: [u8; CHUNK_SIZE] = [0; CHUNK_SIZE];
            let mut num_reads: usize = 0;
            let mut num_bytes: usize = 0;
            loop {
                num_bytes = num_bytes.saturating_add(reader.read(&mut buf).await.unwrap());
                num_reads = num_reads.saturating_add(1);
                if num_bytes >= total_test_bytes {
                    break;
                }
            }
            (num_reads, num_bytes)
        })
        .await;
        read_stats.send(stats).await.unwrap();
    };

    let write_future = async move {
        // Write the expected number of bytes
        let stats = stats("Write", || async {
            let source: [u8; CHUNK_SIZE] = [1; CHUNK_SIZE];
            let mut num_writes: usize = 0;
            let mut num_bytes: usize = 0;
            loop {
                num_bytes = num_bytes.saturating_add(writer.write(&source).await.unwrap());
                num_writes = num_writes.saturating_add(1);
                if num_bytes >= total_test_bytes {
                    break;
                }
            }
            (num_writes, num_bytes)
        })
        .await;
        write_stats.send(stats).await.unwrap();
    };

    // Wait for both to finish
    futures::future::join(read_future, write_future).await;

    // Output statistics from the reader & writer
    let mut results: Vec<RunStats> = vec![];
    for _ in 0u8..2u8 {
        results.push(stats_receiver.next().await.unwrap());
    }
    results
}

fn simple_shared_bucket(label: &'static str, size: usize) -> SharedBucket {
    Arc::new(Mutex::new(Bucket::new(label.to_string(), size, size, size)))
}

/// For comparison, no rate limiting
async fn test_no_rate_limit(num_bytes: usize) -> Vec<RunStats> {
    test_rate_limiter(num_bytes, |reader| reader, |writer| writer).await
}

/// Tests backpressure, where only the reader is limited.  Using a socket adds a buffer
/// that causes the backpressure only to occur after a large amount of traffic first. (> 500k bytes)
/// > 55 chunks could cause backpressure, for a proper test > 100 or > 200 give good results.
async fn test_rate_limit_read(num_bytes: usize, throughput: usize) -> Vec<RunStats> {
    let inbound_rate_limiter = simple_shared_bucket("read", throughput);
    test_rate_limiter(
        num_bytes,
        |reader| AsyncRateLimiter::new(reader, Some(inbound_rate_limiter)),
        |writer| writer,
    )
    .await
}

/// Tests only if the writer is rate limited, which should provide a smoother read rate
async fn test_rate_limit_write(num_bytes: usize, throughput: usize) -> Vec<RunStats> {
    let outbound_rate_limiter = simple_shared_bucket("write", throughput);
    test_rate_limiter(
        num_bytes,
        |reader| reader,
        |writer| AsyncRateLimiter::new(writer, Some(outbound_rate_limiter)),
    )
    .await
}

/// Tests if both are rate limited.  Results should be roughly the same in the long term as
/// the other two
async fn test_rate_limit_read_write(
    num_bytes: usize,
    read_throughput: usize,
    write_throughput: usize,
) -> Vec<RunStats> {
    let inbound_rate_limiter = simple_shared_bucket("read", read_throughput);
    let outbound_rate_limiter = simple_shared_bucket("write", write_throughput);
    test_rate_limiter(
        num_bytes,
        |reader| AsyncRateLimiter::new(reader, Some(inbound_rate_limiter)),
        |writer| AsyncRateLimiter::new(writer, Some(outbound_rate_limiter)),
    )
    .await
}
<|endoftext|>


<commit_before>use rustc_hir::Mutability;
use rustc_index::bit_set::HybridBitSet;
use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, BasicBlock, Local, Location};
use rustc_middle::ty::TyCtxt;

use crate::transform::{MirPass, MirSource};

/// This pass looks for MIR that always copies the same local into the return place and eliminates
/// the copy by renaming all uses of that local to `_0`.
///
/// This allows LLVM to perform an optimization similar to the named return value optimization
/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the
/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by
/// value like so:
///
/// ```rust
/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
///     let mut buf = [0; 1024];
///     init(&mut buf);
///     buf
/// }
/// ```
///
/// For now, this pass is very simple and only capable of eliminating a single copy. A more general
/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and
/// [#71003], could yield even more benefits.
///
/// [#47954]: https://github.com/rust-lang/rust/pull/47954
/// [#71003]: https://github.com/rust-lang/rust/pull/71003
pub struct RenameReturnPlace;

impl<'tcx> MirPass<'tcx> for RenameReturnPlace {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut mir::Body<'tcx>) {
        if tcx.sess.opts.debugging_opts.mir_opt_level == 0 {
            return;
        }

        if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 {
            // The `DestinationPropagation` pass runs at level 2, so this pass is redundant (and
            // fails some asserts).
            return;
        }

        let returned_local = match local_eligible_for_nrvo(body) {
            Some(l) => l,
            None => {
                debug!("`{:?}` was ineligible for NRVO", src.def_id());
                return;
            }
        };

        debug!(
            "`{:?}` was eligible for NRVO, making {:?} the return place",
            src.def_id(),
            returned_local
        );

        RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body(body);

        // Clean up the `NOP`s we inserted for statements made useless by our renaming.
        for block_data in body.basic_blocks_mut() {
            block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop);
        }

        // Overwrite the debuginfo of `_0` with that of the renamed local.
        let (renamed_decl, ret_decl) =
            body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE);

        // Sometimes, the return place is assigned a local of a different but coercable type, for
        // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means
        // its type may no longer match the return type of its function. This doesn't cause a
        // problem in codegen because these two types are layout-compatible, but may be unexpected.
        debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty);
        ret_decl.clone_from(renamed_decl);

        // The return place is always mutable.
        ret_decl.mutability = Mutability::Mut;
    }
}

/// MIR that is eligible for the NRVO must fulfill two conditions:
///   1. The return place must not be read prior to the `Return` terminator.
///   2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the
///      only definition of the return place reaching the `Return` terminator.
///
/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned
/// to the return place along all possible paths through the control-flow graph.
fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option<Local> {
    if IsReturnPlaceRead::run(body) {
        return None;
    }

    let mut copied_to_return_place = None;
    for block in body.basic_blocks().indices() {
        // Look for blocks with a `Return` terminator.
        if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) {
            continue;
        }

        // Look for an assignment of a single local to the return place prior to the `Return`.
        let returned_local = find_local_assigned_to_return_place(block, body)?;
        match body.local_kind(returned_local) {
            // FIXME: Can we do this for arguments as well?
            mir::LocalKind::Arg => return None,

            mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"),
            mir::LocalKind::Var | mir::LocalKind::Temp => {}
        }

        // If multiple different locals are copied to the return place. We can't pick a
        // single one to rename.
        if copied_to_return_place.map_or(false, |old| old != returned_local) {
            return None;
        }

        copied_to_return_place = Some(returned_local);
    }

    copied_to_return_place
}

fn find_local_assigned_to_return_place(
    start: BasicBlock,
    body: &mut mir::Body<'_>,
) -> Option<Local> {
    let mut block = start;
    let mut seen = HybridBitSet::new_empty(body.basic_blocks().len());

    // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
    while seen.insert(block) {
        trace!("Looking for assignments to `_0` in {:?}", block);

        let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place);
        if local.is_some() {
            return local;
        }

        match body.predecessors()[block].as_slice() {
            &[pred] => block = pred,
            _ => return None,
        }
    }

    None
}

// If this statement is an assignment of an unprojected local to the return place,
// return that local.
fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option<Local> {
    if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind {
        if lhs.as_local() == Some(mir::RETURN_PLACE) {
            if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs {
                return rhs.as_local();
            }
        }
    }

    None
}

struct RenameToReturnPlace<'tcx> {
    to_rename: Local,
    tcx: TyCtxt<'tcx>,
}

/// Replaces all uses of `self.to_rename` with `_0`.
impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
    fn tcx(&self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) {
        // Remove assignments of the local being replaced to the return place, since it is now the
        // return place:
        //     _0 = _1
        if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) {
            stmt.kind = mir::StatementKind::Nop;
            return;
        }

        // Remove storage annotations for the local being replaced:
        //     StorageLive(_1)
        if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) =
            stmt.kind
        {
            if local == self.to_rename {
                stmt.kind = mir::StatementKind::Nop;
                return;
            }
        }

        self.super_statement(stmt, loc)
    }

    fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) {
        // Ignore the implicit "use" of the return place in a `Return` statement.
        if let mir::TerminatorKind::Return = terminator.kind {
            return;
        }

        self.super_terminator(terminator, loc);
    }

    fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) {
        if *l == mir::RETURN_PLACE {
            assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo));
        } else if *l == self.to_rename {
            *l = mir::RETURN_PLACE;
        }
    }
}

struct IsReturnPlaceRead(bool);

impl IsReturnPlaceRead {
    fn run(body: &mir::Body<'_>) -> bool {
        let mut vis = IsReturnPlaceRead(false);
        vis.visit_body(body);
        vis.0
    }
}

impl Visitor<'tcx> for IsReturnPlaceRead {
    fn visit_local(&mut self, &l: &Local, ctxt: PlaceContext, _: Location) {
        if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() {
            self.0 = true;
        }
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) {
        // Ignore the implicit "use" of the return place in a `Return` statement.
        if let mir::TerminatorKind::Return = terminator.kind {
            return;
        }

        self.super_terminator(terminator, loc);
    }
}
<commit_msg>Enable RenameReturnPlace MIR optimization on mir-opt-level >= 2<commit_after>use rustc_hir::Mutability;
use rustc_index::bit_set::HybridBitSet;
use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, BasicBlock, Local, Location};
use rustc_middle::ty::TyCtxt;

use crate::transform::{MirPass, MirSource};

/// This pass looks for MIR that always copies the same local into the return place and eliminates
/// the copy by renaming all uses of that local to `_0`.
///
/// This allows LLVM to perform an optimization similar to the named return value optimization
/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the
/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by
/// value like so:
///
/// ```rust
/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
///     let mut buf = [0; 1024];
///     init(&mut buf);
///     buf
/// }
/// ```
///
/// For now, this pass is very simple and only capable of eliminating a single copy. A more general
/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and
/// [#71003], could yield even more benefits.
///
/// [#47954]: https://github.com/rust-lang/rust/pull/47954
/// [#71003]: https://github.com/rust-lang/rust/pull/71003
pub struct RenameReturnPlace;

impl<'tcx> MirPass<'tcx> for RenameReturnPlace {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut mir::Body<'tcx>) {
        if tcx.sess.opts.debugging_opts.mir_opt_level == 0 {
            return;
        }

        let returned_local = match local_eligible_for_nrvo(body) {
            Some(l) => l,
            None => {
                debug!("`{:?}` was ineligible for NRVO", src.def_id());
                return;
            }
        };

        debug!(
            "`{:?}` was eligible for NRVO, making {:?} the return place",
            src.def_id(),
            returned_local
        );

        RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body(body);

        // Clean up the `NOP`s we inserted for statements made useless by our renaming.
        for block_data in body.basic_blocks_mut() {
            block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop);
        }

        // Overwrite the debuginfo of `_0` with that of the renamed local.
        let (renamed_decl, ret_decl) =
            body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE);

        // Sometimes, the return place is assigned a local of a different but coercable type, for
        // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means
        // its type may no longer match the return type of its function. This doesn't cause a
        // problem in codegen because these two types are layout-compatible, but may be unexpected.
        debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty);
        ret_decl.clone_from(renamed_decl);

        // The return place is always mutable.
        ret_decl.mutability = Mutability::Mut;
    }
}

/// MIR that is eligible for the NRVO must fulfill two conditions:
///   1. The return place must not be read prior to the `Return` terminator.
///   2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the
///      only definition of the return place reaching the `Return` terminator.
///
/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned
/// to the return place along all possible paths through the control-flow graph.
fn local_eligible_for_nrvo(body: &mut mir::Body<'_>) -> Option<Local> {
    if IsReturnPlaceRead::run(body) {
        return None;
    }

    let mut copied_to_return_place = None;
    for block in body.basic_blocks().indices() {
        // Look for blocks with a `Return` terminator.
        if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) {
            continue;
        }

        // Look for an assignment of a single local to the return place prior to the `Return`.
        let returned_local = find_local_assigned_to_return_place(block, body)?;
        match body.local_kind(returned_local) {
            // FIXME: Can we do this for arguments as well?
            mir::LocalKind::Arg => return None,

            mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"),
            mir::LocalKind::Var | mir::LocalKind::Temp => {}
        }

        // If multiple different locals are copied to the return place. We can't pick a
        // single one to rename.
        if copied_to_return_place.map_or(false, |old| old != returned_local) {
            return None;
        }

        copied_to_return_place = Some(returned_local);
    }

    copied_to_return_place
}

fn find_local_assigned_to_return_place(
    start: BasicBlock,
    body: &mut mir::Body<'_>,
) -> Option<Local> {
    let mut block = start;
    let mut seen = HybridBitSet::new_empty(body.basic_blocks().len());

    // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
    while seen.insert(block) {
        trace!("Looking for assignments to `_0` in {:?}", block);

        let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place);
        if local.is_some() {
            return local;
        }

        match body.predecessors()[block].as_slice() {
            &[pred] => block = pred,
            _ => return None,
        }
    }

    None
}

// If this statement is an assignment of an unprojected local to the return place,
// return that local.
fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option<Local> {
    if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind {
        if lhs.as_local() == Some(mir::RETURN_PLACE) {
            if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs {
                return rhs.as_local();
            }
        }
    }

    None
}

struct RenameToReturnPlace<'tcx> {
    to_rename: Local,
    tcx: TyCtxt<'tcx>,
}

/// Replaces all uses of `self.to_rename` with `_0`.
impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
    fn tcx(&self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) {
        // Remove assignments of the local being replaced to the return place, since it is now the
        // return place:
        //     _0 = _1
        if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) {
            stmt.kind = mir::StatementKind::Nop;
            return;
        }

        // Remove storage annotations for the local being replaced:
        //     StorageLive(_1)
        if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) =
            stmt.kind
        {
            if local == self.to_rename {
                stmt.kind = mir::StatementKind::Nop;
                return;
            }
        }

        self.super_statement(stmt, loc)
    }

    fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) {
        // Ignore the implicit "use" of the return place in a `Return` statement.
        if let mir::TerminatorKind::Return = terminator.kind {
            return;
        }

        self.super_terminator(terminator, loc);
    }

    fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) {
        if *l == mir::RETURN_PLACE {
            assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo));
        } else if *l == self.to_rename {
            *l = mir::RETURN_PLACE;
        }
    }
}

struct IsReturnPlaceRead(bool);

impl IsReturnPlaceRead {
    fn run(body: &mir::Body<'_>) -> bool {
        let mut vis = IsReturnPlaceRead(false);
        vis.visit_body(body);
        vis.0
    }
}

impl Visitor<'tcx> for IsReturnPlaceRead {
    fn visit_local(&mut self, &l: &Local, ctxt: PlaceContext, _: Location) {
        if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() {
            self.0 = true;
        }
    }

    fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) {
        // Ignore the implicit "use" of the return place in a `Return` statement.
        if let mir::TerminatorKind::Return = terminator.kind {
            return;
        }

        self.super_terminator(terminator, loc);
    }
}
<|endoftext|>


<commit_before>#![allow(deprecated)] // keysyms

//! Callback methods for rustwlc
use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::*;
use rustwlc::input::{pointer, keyboard};

use rustc_serialize::json::Json;
use std::sync::Arc;
use std::thread;

use registry::{self, RegistryGetData};
use super::keys::{self, KeyPress, KeyEvent};
use super::layout::{Action, try_lock_tree, try_lock_action, ContainerType, MovementError, TreeError};
use super::layout::commands::set_performing_action;
use super::lua::{self, LuaQuery};
use super::background;

/// If the event is handled by way-cooler
const EVENT_HANDLED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

// wlc callbacks

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    if let Ok(mut tree) = try_lock_tree() {
        tree.add_output(output).and_then(|_|{
            tree.switch_to_workspace(&"1")
        }).is_ok()
    } else {
        false
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                            old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
           output, *old_size_ptr, *new_size_ptr);
    // Update the resolution of the output and its children
    output.set_resolution(*new_size_ptr, 1);
    if let Ok(mut tree) = try_lock_tree() {
        tree.layout_active_of(ContainerType::Output)
            .expect("Could not layout active output");
    }
}

pub extern fn view_created(view: WlcView) -> bool {
    trace!("view_created: {:?}: \"{}\"", view, view.get_title());
    if let Ok(mut tree) = try_lock_tree() {
        tree.add_view(view.clone()).and_then(|_| {
            tree.set_active_view(view)
        }).is_ok()
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = try_lock_tree() {
        tree.remove_view(view.clone()).and_then(|_| {
            tree.layout_active_of(ContainerType::Workspace)
        }).unwrap_or_else(|err| {
            match err {
                TreeError::ViewNotFound(_) => {},
                _ => {
                    error!("Error in view_destroyed: {:?}", err);
                }
            }
        });
    } else {
        error!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    // set the focus view in the tree
    // If tree is already grabbed,
    // it should have the active container all set
    if let Ok(mut tree) = try_lock_tree() {
        if tree.set_active_view(current.clone()).is_err() {
            error!("Could not layout {:?}", current);
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_state(view: WlcView, state: ViewState, handled: bool) {
    view.set_state(state, handled);
}

pub extern fn view_request_move(view: WlcView, _dest: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.set_active_view(view) {
            error!("view_request_move error: {:?}", err);
        }
    }
}

pub extern fn view_request_resize(_view: WlcView, _edge: ResizeEdge, _location: &Point) {

}

#[allow(non_snake_case)] // EMPTY_MODS will be a static once we have KEY_LED_NONE
pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                           key: u32, state: KeyState) -> bool {
    let EMPTY_MODS: KeyboardModifiers = KeyboardModifiers {
            mods: MOD_NONE,
            leds: KeyboardLed::empty()
    };
    let sym = keyboard::get_keysym_for_key(key, EMPTY_MODS);
    let press = KeyPress::new(mods.mods, sym);

    if state == KeyState::Pressed {
        if let Some(action) = keys::get(&press) {
            debug!("[key] Found an action for {}", press);
            match action {
                KeyEvent::Command(func) => {
                    func();
                },
                KeyEvent::Lua => {
                    match lua::send(LuaQuery::HandleKey(press)) {
                        Ok(_) => {},
                        Err(err) => {
                            // We may want to wait for Lua's reply from
                            // keypresses; for example if the table is tampered
                            // with or Lua is restarted or Lua has an error.
                            // ATM Lua asynchronously logs this but in the future
                            // an error popup/etc is a good idea.
                            error!("Error sending keypress: {:?}", err);
                        }
                    }
                }
            }
            return EVENT_HANDLED
        }
    }

    return EVENT_PASS_THROUGH
}

pub extern fn view_request_geometry(_view: WlcView, _geometry: &Geometry) {
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, _button: u32,
                             state: ButtonState, point: &Point) -> bool {
    if state == ButtonState::Pressed && !view.is_root() {
        if let Ok(mut tree) = try_lock_tree() {
            tree.set_active_view(view)
                .unwrap_or_else(|err| {
                    error!("Could not set active container {:?}", err);
                });
            if mods.mods.contains(MOD_CTRL) {
                let action = Action {
                    view: view,
                    grab: *point,
                    edges: ResizeEdge::empty()
                };
                if let Err(err) = set_performing_action(Some(action)) {
                    warn!("{:#?}", err);
                }
            }
        }
    } else {
        if let Err(err) = set_performing_action(None) {
            warn!("{:#?}", err);
        }
    }
    false
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, _axis: ScrollAxis,
                         _heights: [f64; 2]) -> bool {
    false
}

pub extern fn pointer_motion(_view: WlcView, _time: u32, point: &Point) -> bool {
    pointer::set_position(*point);
    if let Ok(action) = try_lock_action() {
        match *action {
            None => return false,
            _ => {}
        }
    }
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.try_drag_active(*point) {
            match err {
                TreeError::PerformingAction(_) => {},
                TreeError::Movement(MovementError::NotFloating(_)) => {}
                err => {
                    error!("Error: {:#?}", err);
                }
            }
        }
    }
    false
}

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
    info!("Loading background...");
    let maybe_color: Result<Arc<Json>, ()> = registry::get_data("background")
        .map(RegistryGetData::resolve).and_then(|(_, data)| {
            Ok(data)
        }).map_err(|_| ());
    if let Ok(color) = maybe_color {
        match *color {
            Json::F64(hex_color) => {
                for output in WlcOutput::list() {
                    let color = background::Color::from_u32(hex_color as u32);
                    // different thread for each output.
                    thread::spawn(move || {background::generate_solid_background(color, output.clone());});
                }
            }
            _ => {
                error!("Non-solid color backgrounds not yet supported, {:?}", color);
            }
        }
    } else {
        warn!("Couldn't read background value");
    }
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
    if let Ok(mut tree) = try_lock_tree() {
        if tree.destroy_tree().is_err() {
            error!("Could not destroy tree");
        }
    }

}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<commit_msg>Click is ignored if moving floating window around<commit_after>#![allow(deprecated)] // keysyms

//! Callback methods for rustwlc
use rustwlc::handle::{WlcOutput, WlcView};
use rustwlc::types::*;
use rustwlc::input::{pointer, keyboard};

use rustc_serialize::json::Json;
use std::sync::Arc;
use std::thread;

use registry::{self, RegistryGetData};
use super::keys::{self, KeyPress, KeyEvent};
use super::layout::{Action, try_lock_tree, try_lock_action, ContainerType, MovementError, TreeError};
use super::layout::commands::set_performing_action;
use super::lua::{self, LuaQuery};
use super::background;

/// If the event is handled by way-cooler
const EVENT_HANDLED: bool = true;

/// If the event should be passed through to clients
const EVENT_PASS_THROUGH: bool = false;

// wlc callbacks

pub extern fn output_created(output: WlcOutput) -> bool {
    trace!("output_created: {:?}: {}", output, output.get_name());
    if let Ok(mut tree) = try_lock_tree() {
        tree.add_output(output).and_then(|_|{
            tree.switch_to_workspace(&"1")
        }).is_ok()
    } else {
        false
    }
}

pub extern fn output_destroyed(output: WlcOutput) {
    trace!("output_destroyed: {:?}", output);
}

pub extern fn output_focus(output: WlcOutput, focused: bool) {
    trace!("output_focus: {:?} focus={}", output, focused);
}

pub extern fn output_resolution(output: WlcOutput,
                            old_size_ptr: &Size, new_size_ptr: &Size) {
    trace!("output_resolution: {:?} from  {:?} to {:?}",
           output, *old_size_ptr, *new_size_ptr);
    // Update the resolution of the output and its children
    output.set_resolution(*new_size_ptr, 1);
    if let Ok(mut tree) = try_lock_tree() {
        tree.layout_active_of(ContainerType::Output)
            .expect("Could not layout active output");
    }
}

pub extern fn view_created(view: WlcView) -> bool {
    trace!("view_created: {:?}: \"{}\"", view, view.get_title());
    if let Ok(mut tree) = try_lock_tree() {
        tree.add_view(view.clone()).and_then(|_| {
            tree.set_active_view(view)
        }).is_ok()
    } else {
        false
    }
}

pub extern fn view_destroyed(view: WlcView) {
    trace!("view_destroyed: {:?}", view);
    if let Ok(mut tree) = try_lock_tree() {
        tree.remove_view(view.clone()).and_then(|_| {
            tree.layout_active_of(ContainerType::Workspace)
        }).unwrap_or_else(|err| {
            match err {
                TreeError::ViewNotFound(_) => {},
                _ => {
                    error!("Error in view_destroyed: {:?}", err);
                }
            }
        });
    } else {
        error!("Could not delete view {:?}", view);
    }
}

pub extern fn view_focus(current: WlcView, focused: bool) {
    trace!("view_focus: {:?} {}", current, focused);
    current.set_state(VIEW_ACTIVATED, focused);
    // set the focus view in the tree
    // If tree is already grabbed,
    // it should have the active container all set
    if let Ok(mut tree) = try_lock_tree() {
        if tree.set_active_view(current.clone()).is_err() {
            error!("Could not layout {:?}", current);
        }
    }
}

pub extern fn view_move_to_output(current: WlcView,
                                  o1: WlcOutput, o2: WlcOutput) {
    trace!("view_move_to_output: {:?}, {:?}, {:?}", current, o1, o2);
}

pub extern fn view_request_state(view: WlcView, state: ViewState, handled: bool) {
    view.set_state(state, handled);
}

pub extern fn view_request_move(view: WlcView, _dest: &Point) {
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.set_active_view(view) {
            error!("view_request_move error: {:?}", err);
        }
    }
}

pub extern fn view_request_resize(_view: WlcView, _edge: ResizeEdge, _location: &Point) {

}

#[allow(non_snake_case)] // EMPTY_MODS will be a static once we have KEY_LED_NONE
pub extern fn keyboard_key(_view: WlcView, _time: u32, mods: &KeyboardModifiers,
                           key: u32, state: KeyState) -> bool {
    let EMPTY_MODS: KeyboardModifiers = KeyboardModifiers {
            mods: MOD_NONE,
            leds: KeyboardLed::empty()
    };
    let sym = keyboard::get_keysym_for_key(key, EMPTY_MODS);
    let press = KeyPress::new(mods.mods, sym);

    if state == KeyState::Pressed {
        if let Some(action) = keys::get(&press) {
            debug!("[key] Found an action for {}", press);
            match action {
                KeyEvent::Command(func) => {
                    func();
                },
                KeyEvent::Lua => {
                    match lua::send(LuaQuery::HandleKey(press)) {
                        Ok(_) => {},
                        Err(err) => {
                            // We may want to wait for Lua's reply from
                            // keypresses; for example if the table is tampered
                            // with or Lua is restarted or Lua has an error.
                            // ATM Lua asynchronously logs this but in the future
                            // an error popup/etc is a good idea.
                            error!("Error sending keypress: {:?}", err);
                        }
                    }
                }
            }
            return EVENT_HANDLED
        }
    }

    return EVENT_PASS_THROUGH
}

pub extern fn view_request_geometry(_view: WlcView, _geometry: &Geometry) {
}

pub extern fn pointer_button(view: WlcView, _time: u32,
                         mods: &KeyboardModifiers, _button: u32,
                             state: ButtonState, point: &Point) -> bool {
    if state == ButtonState::Pressed && !view.is_root() {
        if let Ok(mut tree) = try_lock_tree() {
            tree.set_active_view(view)
                .unwrap_or_else(|err| {
                    error!("Could not set active container {:?}", err);
                });
            if mods.mods.contains(MOD_CTRL) {
                let action = Action {
                    view: view,
                    grab: *point,
                    edges: ResizeEdge::empty()
                };
                if let Err(err) = set_performing_action(Some(action)) {
                    warn!("{:#?}", err);
                }
            }
        }
    } else {
        if let Err(err) = set_performing_action(None) {
            warn!("{:#?}", err);
        }
    }
    false
}

pub extern fn pointer_scroll(_view: WlcView, _time: u32,
                         _mods_ptr: &KeyboardModifiers, _axis: ScrollAxis,
                         _heights: [f64; 2]) -> bool {
    false
}

pub extern fn pointer_motion(_view: WlcView, _time: u32, point: &Point) -> bool {
    pointer::set_position(*point);
    if let Ok(action) = try_lock_action() {
        match *action {
            None => return false,
            _ => {}
        }
    }
    if let Ok(mut tree) = try_lock_tree() {
        if let Err(err) = tree.try_drag_active(*point) {
            match err {
                TreeError::PerformingAction(_) => {},
                TreeError::Movement(MovementError::NotFloating(_)) => {}
                err => {
                    error!("Error: {:#?}", err);
                }
            }
        } else {
            return true
        }
    }
    false
}

pub extern fn compositor_ready() {
    info!("Preparing compositor!");
    info!("Initializing Lua...");
    lua::init();
    info!("Loading background...");
    let maybe_color: Result<Arc<Json>, ()> = registry::get_data("background")
        .map(RegistryGetData::resolve).and_then(|(_, data)| {
            Ok(data)
        }).map_err(|_| ());
    if let Ok(color) = maybe_color {
        match *color {
            Json::F64(hex_color) => {
                for output in WlcOutput::list() {
                    let color = background::Color::from_u32(hex_color as u32);
                    // different thread for each output.
                    thread::spawn(move || {background::generate_solid_background(color, output.clone());});
                }
            }
            _ => {
                error!("Non-solid color backgrounds not yet supported, {:?}", color);
            }
        }
    } else {
        warn!("Couldn't read background value");
    }
}

pub extern fn compositor_terminating() {
    info!("Compositor terminating!");
    lua::send(lua::LuaQuery::Terminate).ok();
    if let Ok(mut tree) = try_lock_tree() {
        if tree.destroy_tree().is_err() {
            error!("Could not destroy tree");
        }
    }

}

pub fn init() {
    use rustwlc::callback;

    callback::output_created(output_created);
    callback::output_destroyed(output_destroyed);
    callback::output_focus(output_focus);
    callback::output_resolution(output_resolution);
    callback::view_created(view_created);
    callback::view_destroyed(view_destroyed);
    callback::view_focus(view_focus);
    callback::view_move_to_output(view_move_to_output);
    callback::view_request_geometry(view_request_geometry);
    callback::view_request_state(view_request_state);
    callback::view_request_move(view_request_move);
    callback::view_request_resize(view_request_resize);
    callback::keyboard_key(keyboard_key);
    callback::pointer_button(pointer_button);
    callback::pointer_scroll(pointer_scroll);
    callback::pointer_motion(pointer_motion);
    callback::compositor_ready(compositor_ready);
    callback::compositor_terminate(compositor_terminating);
    trace!("Registered wlc callbacks");
}
<|endoftext|>


<commit_before>#![crate_name="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]
#![feature(default_type_params)]

extern crate debug;
extern crate term;
extern crate collections;
extern crate url;
extern crate serialize;
extern crate semver;
#[phase(plugin, link)] extern crate log;

extern crate toml;
extern crate docopt;
#[cfg(test)] extern crate hamcrest;

use std::os;
use std::io::stdio::{stdout_raw, stderr_raw};
use std::io::{stdout, stderr};
use std::io;
use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use docopt::FlagParser;

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub fn execute_main<'a,
                    T: FlagParser,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                        options_first: bool) {
    process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
}

pub fn call_main<'a,
        T: FlagParser,
        U: RepresentsJSON,
        V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<'a,
                                  T: FlagParser,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                                      options_first: bool) {
    process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
                                                       options_first));
}

pub fn call_main_without_stdin<'a,
                               T: FlagParser,
                               V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    exec(flags, shell)
}

fn process<'a, V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {
    let mut shell = shell(true);
    let mut args = os::args();
    args.remove(0);
    process_executed(callback(args.as_slice(), &mut shell), &mut shell)
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell) {
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else if error.to_string().len() > 0 {
        let _ = shell.error(error.to_string());
    }

    if error.cause().is_some() || unknown {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        if unknown {
            let _ = shell.error(error.to_string());
        }
        error.detail().map(|detail| {
            let _ = shell.err().say(format!("{}", detail), BLACK);
        });
        error.cause().map(|err| {
            let _ = handle_cause(err, shell);
        });
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

pub fn version() -> String {
    (env!("CFG_VERSION")).to_string()
}

fn flags_from_args<T: FlagParser>(args: &[String],
                                  options_first: bool) -> CliResult<T> {
    let args = args.iter().map(|a| a.as_slice()).collect::<Vec<&str>>();
    let config = docopt::Config {
        options_first: options_first,
        help: true,
        version: Some(version()),
    };
    FlagParser::parse_args(config, args.as_slice()).map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(e, code)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_string().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<commit_msg>Deny all warnings when building cargo<commit_after>#![crate_name="cargo"]
#![crate_type="rlib"]

#![feature(macro_rules, phase)]
#![feature(default_type_params)]
#![deny(warnings)]

extern crate debug;
extern crate term;
extern crate collections;
extern crate url;
extern crate serialize;
extern crate semver;
#[phase(plugin, link)] extern crate log;

extern crate toml;
extern crate docopt;
#[cfg(test)] extern crate hamcrest;

use std::os;
use std::io::stdio::{stdout_raw, stderr_raw};
use std::io::{stdout, stderr};
use std::io;
use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use docopt::FlagParser;

use core::{Shell, MultiShell, ShellConfig};
use term::color::{BLACK};

pub use util::{CargoError, CliError, CliResult, human};

macro_rules! some(
    ($e:expr) => (
        match $e {
            Some(e) => e,
            None => return None
        }
    )
)

// Added so that the try! macro below can refer to cargo::util, while
// other external importers of this macro can use it as well.
//
// "Hygiene strikes again" - @acrichton
mod cargo {
    pub use super::util;
}

#[macro_export]
macro_rules! try (
    ($expr:expr) => ({
        use cargo::util::FromError;
        match $expr.map_err(FromError::from_error) {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

macro_rules! raw_try (
    ($expr:expr) => ({
        match $expr {
            Ok(val) => val,
            Err(err) => return Err(err)
        }
    })
)

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

trait RepresentsJSON : Decodable<json::Decoder, json::DecoderError> {}
impl<T: Decodable<json::Decoder, json::DecoderError>> RepresentsJSON for T {}

pub fn execute_main<'a,
                    T: FlagParser,
                    U: RepresentsJSON,
                    V: Encodable<json::Encoder<'a>, io::IoError>>(
                        exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
                        options_first: bool) {
    process::<V>(|rest, shell| call_main(exec, shell, rest, options_first));
}

pub fn call_main<'a,
        T: FlagParser,
        U: RepresentsJSON,
        V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<'a,
                                  T: FlagParser,
                                  V: Encodable<json::Encoder<'a>, io::IoError>>(
                                      exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
                                      options_first: bool) {
    process::<V>(|rest, shell| call_main_without_stdin(exec, shell, rest,
                                                       options_first));
}

pub fn call_main_without_stdin<'a,
                               T: FlagParser,
                               V: Encodable<json::Encoder<'a>, io::IoError>>(
            exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
            shell: &mut MultiShell,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>> {
    let flags = try!(flags_from_args::<T>(args, options_first));
    exec(flags, shell)
}

fn process<'a, V: Encodable<json::Encoder<'a>, io::IoError>>(
               callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {
    let mut shell = shell(true);
    let mut args = os::args();
    args.remove(0);
    process_executed(callback(args.as_slice(), &mut shell), &mut shell)
}

pub fn process_executed<'a,
                        T: Encodable<json::Encoder<'a>, io::IoError>>(
                            result: CliResult<Option<T>>,
                            shell: &mut MultiShell) {
    match result {
        Err(e) => handle_error(e, shell),
        Ok(encodable) => {
            encodable.map(|encodable| {
                let encoded = json::encode(&encodable);
                println!("{}", encoded);
            });
        }
    }
}

pub fn shell(verbose: bool) -> MultiShell {
    let tty = stderr_raw().isatty();
    let stderr = box stderr() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = stdout_raw().isatty();
    let stdout = box stdout() as Box<Writer>;

    let config = ShellConfig { color: true, verbose: verbose, tty: tty };
    let out = Shell::create(stdout, config);

    MultiShell::new(out, err, verbose)
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    log!(4, "handle_error; err={}", err);

    let CliError { error, exit_code, unknown } = err;

    if unknown {
        let _ = shell.error("An unknown error occurred");
    } else if error.to_string().len() > 0 {
        let _ = shell.error(error.to_string());
    }

    if error.cause().is_some() || unknown {
        let _ = shell.concise(|shell| {
            shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK)
        });
    }

    let _ = shell.verbose(|shell| {
        if unknown {
            let _ = shell.error(error.to_string());
        }
        error.detail().map(|detail| {
            let _ = shell.err().say(format!("{}", detail), BLACK);
        });
        error.cause().map(|err| {
            let _ = handle_cause(err, shell);
        });
        Ok(())
      });

    std::os::set_exit_status(exit_code as int);
}

fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
    let _ = shell.err().say("\nCaused by:", BLACK);
    let _ = shell.err().say(format!("  {}", err.description()), BLACK);

    err.cause().map(|e| handle_cause(e, shell));
}

pub fn version() -> String {
    (env!("CFG_VERSION")).to_string()
}

fn flags_from_args<T: FlagParser>(args: &[String],
                                  options_first: bool) -> CliResult<T> {
    let args = args.iter().map(|a| a.as_slice()).collect::<Vec<&str>>();
    let config = docopt::Config {
        options_first: options_first,
        help: true,
        version: Some(version()),
    };
    FlagParser::parse_args(config, args.as_slice()).map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(e, code)
    })
}

fn json_from_stdin<T: RepresentsJSON>() -> CliResult<T> {
    let mut reader = io::stdin();
    let input = try!(reader.read_to_string().map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(json::from_str(input.as_slice()).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<|endoftext|>


<commit_before>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]

#[cfg(test)] extern crate hamcrest;
#[macro_use] extern crate log;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate git2;
extern crate glob;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate regex;
extern crate rustc_serialize;
extern crate semver;
extern crate tar;
extern crate term;
extern crate time;
extern crate toml;
extern crate url;

use std::env;
use std::error::Error;
use std::io::prelude::*;
use std::io;
use rustc_serialize::{Decodable, Encodable};
use rustc_serialize::json::{self, Json};
use docopt::Docopt;

use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
use core::shell::Verbosity::{Verbose};
use core::shell::ColorConfig::{Auto};
use term::color::{BLACK, RED};

pub use util::{CargoError, CliError, CliResult, human, Config, ChainError};

macro_rules! bail {
    ($($fmt:tt)*) => (
        return Err(::util::human(&format_args!($($fmt)*)))
    )
}

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub fn execute_main<T, U, V>(
                        exec: fn(T, U, &Config) -> CliResult<Option<V>>,
                        options_first: bool,
                        usage: &str)
    where V: Encodable, T: Decodable, U: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main<T, U, V>(
            exec: fn(T, U, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable, U: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<T, V>(
                                      exec: fn(T, &Config) -> CliResult<Option<V>>,
                                      options_first: bool,
                                      usage: &str)
    where V: Encodable, T: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main_without_stdin(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main_without_stdin<T, V>(
            exec: fn(T, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    exec(flags, shell)
}

fn process<V, F>(mut callback: F)
    where F: FnMut(&[String], &Config) -> CliResult<Option<V>>,
          V: Encodable
{
    let mut config = None;
    let result = (|| {
        config = Some(try!(Config::default()));
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}

pub fn process_executed<T>(result: CliResult<Option<T>>, shell: &mut MultiShell)
    where T: Encodable
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(Some(encodable)) => {
            let encoded = json::encode(&encodable).unwrap();
            println!("{}", encoded);
        }
        Ok(None) => {}
    }
}

pub fn shell(verbosity: Verbosity, color_config: ColorConfig) -> MultiShell {
    let tty = isatty(libc::STDERR_FILENO);
    let stderr = Box::new(io::stderr());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = isatty(libc::STDOUT_FILENO);
    let stdout = Box::new(io::stdout());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let out = Shell::create(stdout, config);

    return MultiShell::new(out, err, verbosity);

    #[cfg(unix)]
    fn isatty(fd: libc::c_int) -> bool {
        unsafe { libc::isatty(fd) != 0 }
    }
    #[cfg(windows)]
    fn isatty(fd: libc::c_int) -> bool {
        extern crate kernel32;
        extern crate winapi;
        unsafe {
            let handle = kernel32::GetStdHandle(if fd == libc::STDOUT_FILENO {
                winapi::winbase::STD_OUTPUT_HANDLE
            } else {
                winapi::winbase::STD_ERROR_HANDLE
            });
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
        }
    }
}

// `output` print variant error strings to either stderr or stdout.
// For fatal errors, print to stderr;
// and for others, e.g. docopt version info, print to stdout.
fn output(err: String, shell: &mut MultiShell, fatal: bool) {
    let std_shell = if fatal {shell.err()} else {shell.out()};
    let color = if fatal {RED} else {BLACK};
    let _ = std_shell.say(err, color);
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    debug!("handle_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    let fatal = exit_code != 0; // exit_code == 0 is non-fatal error

    let hide = unknown && shell.get_verbose() != Verbose;
    if hide {
        let _ = shell.err().say("An unknown error occurred", RED);
    } else {
        output(error.to_string(), shell, fatal);
    }
    if !handle_cause(&error, shell) || hide {
        let _ = shell.err().say("\nTo learn more, run the command again \
                                 with --verbose.".to_string(), BLACK);
    }

    std::process::exit(exit_code);
}

fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
    let verbose = shell.get_verbose();
    let mut err;
    loop {
        cargo_err = match cargo_err.cargo_cause() {
            Some(cause) => cause,
            None => { err = cargo_err.cause(); break }
        };
        if verbose != Verbose && !cargo_err.is_human() { return false }
        print(cargo_err.to_string(), shell);
    }
    loop {
        let cause = match err { Some(err) => err, None => return true };
        if verbose != Verbose { return false }
        print(cause.to_string(), shell);
        err = cause.cause();
    }

    fn print(error: String, shell: &mut MultiShell) {
        let _ = shell.err().say("\nCaused by:", BLACK);
        let _ = shell.err().say(format!("  {}", error), BLACK);
    }
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<'a, T>(usage: &str, args: &[String],
                          options_first: bool) -> CliResult<T>
    where T: Decodable
{
    let docopt = Docopt::new(usage).unwrap()
                                   .options_first(options_first)
                                   .argv(args.iter().map(|s| &s[..]))
                                   .help(true)
                                   .version(Some(version()));
    docopt.decode().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(human(e.to_string()), code)
    })
}

fn json_from_stdin<T: Decodable>() -> CliResult<T> {
    let mut reader = io::stdin();
    let mut input = String::new();
    try!(reader.read_to_string(&mut input).map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(Json::from_str(&input).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<commit_msg>Fix windows<commit_after>#![deny(unused)]
#![cfg_attr(test, deny(warnings))]

#[cfg(test)] extern crate hamcrest;
#[macro_use] extern crate log;
extern crate crates_io as registry;
extern crate crossbeam;
extern crate curl;
extern crate docopt;
extern crate filetime;
extern crate flate2;
extern crate git2;
extern crate glob;
extern crate libc;
extern crate libgit2_sys;
extern crate num_cpus;
extern crate regex;
extern crate rustc_serialize;
extern crate semver;
extern crate tar;
extern crate term;
extern crate time;
extern crate toml;
extern crate url;

use std::env;
use std::error::Error;
use std::io::prelude::*;
use std::io;
use rustc_serialize::{Decodable, Encodable};
use rustc_serialize::json::{self, Json};
use docopt::Docopt;

use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
use core::shell::Verbosity::{Verbose};
use core::shell::ColorConfig::{Auto};
use term::color::{BLACK, RED};

pub use util::{CargoError, CliError, CliResult, human, Config, ChainError};

macro_rules! bail {
    ($($fmt:tt)*) => (
        return Err(::util::human(&format_args!($($fmt)*)))
    )
}

pub mod core;
pub mod ops;
pub mod sources;
pub mod util;

pub fn execute_main<T, U, V>(
                        exec: fn(T, U, &Config) -> CliResult<Option<V>>,
                        options_first: bool,
                        usage: &str)
    where V: Encodable, T: Decodable, U: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main<T, U, V>(
            exec: fn(T, U, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable, U: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    let json = try!(json_from_stdin::<U>());

    exec(flags, json, shell)
}

pub fn execute_main_without_stdin<T, V>(
                                      exec: fn(T, &Config) -> CliResult<Option<V>>,
                                      options_first: bool,
                                      usage: &str)
    where V: Encodable, T: Decodable
{
    process::<V, _>(|rest, shell| {
        call_main_without_stdin(exec, shell, usage, rest, options_first)
    });
}

pub fn call_main_without_stdin<T, V>(
            exec: fn(T, &Config) -> CliResult<Option<V>>,
            shell: &Config,
            usage: &str,
            args: &[String],
            options_first: bool) -> CliResult<Option<V>>
    where V: Encodable, T: Decodable
{
    let flags = try!(flags_from_args::<T>(usage, args, options_first));
    exec(flags, shell)
}

fn process<V, F>(mut callback: F)
    where F: FnMut(&[String], &Config) -> CliResult<Option<V>>,
          V: Encodable
{
    let mut config = None;
    let result = (|| {
        config = Some(try!(Config::default()));
        let args: Vec<_> = try!(env::args_os().map(|s| {
            s.into_string().map_err(|s| {
                human(format!("invalid unicode in argument: {:?}", s))
            })
        }).collect());
        callback(&args, config.as_ref().unwrap())
    })();
    let mut verbose_shell = shell(Verbose, Auto);
    let mut shell = config.as_ref().map(|s| s.shell());
    let shell = shell.as_mut().map(|s| &mut **s).unwrap_or(&mut verbose_shell);
    process_executed(result, shell)
}

pub fn process_executed<T>(result: CliResult<Option<T>>, shell: &mut MultiShell)
    where T: Encodable
{
    match result {
        Err(e) => handle_error(e, shell),
        Ok(Some(encodable)) => {
            let encoded = json::encode(&encodable).unwrap();
            println!("{}", encoded);
        }
        Ok(None) => {}
    }
}

pub fn shell(verbosity: Verbosity, color_config: ColorConfig) -> MultiShell {
    enum Output {
        Stdout,
        Stderr,
    }

    let tty = isatty(Output::Stderr);
    let stderr = Box::new(io::stderr());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let err = Shell::create(stderr, config);

    let tty = isatty(Output::Stdout);
    let stdout = Box::new(io::stdout());

    let config = ShellConfig { color_config: color_config, tty: tty };
    let out = Shell::create(stdout, config);

    return MultiShell::new(out, err, verbosity);

    #[cfg(unix)]
    fn isatty(output: Output) -> bool {
        let fd = match output {
            Output::Stdout => libc::STDOUT_FILENO,
            Output::Stderr => libc::STDERR_FILENO,
        };

        unsafe { libc::isatty(fd) != 0 }
    }
    #[cfg(windows)]
    fn isatty(output: Output) -> bool {
        extern crate kernel32;
        extern crate winapi;

        let handle = match output {
            Output::Stdout => winapi::winbase::STD_OUTPUT_HANDLE,
            Output::Stderr => winapi::winbase::STD_ERROR_HANDLE,
        };

        unsafe {
            let handle = kernel32::GetStdHandle(handle);
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
        }
    }
}

// `output` print variant error strings to either stderr or stdout.
// For fatal errors, print to stderr;
// and for others, e.g. docopt version info, print to stdout.
fn output(err: String, shell: &mut MultiShell, fatal: bool) {
    let std_shell = if fatal {shell.err()} else {shell.out()};
    let color = if fatal {RED} else {BLACK};
    let _ = std_shell.say(err, color);
}

pub fn handle_error(err: CliError, shell: &mut MultiShell) {
    debug!("handle_error; err={:?}", err);

    let CliError { error, exit_code, unknown } = err;
    let fatal = exit_code != 0; // exit_code == 0 is non-fatal error

    let hide = unknown && shell.get_verbose() != Verbose;
    if hide {
        let _ = shell.err().say("An unknown error occurred", RED);
    } else {
        output(error.to_string(), shell, fatal);
    }
    if !handle_cause(&error, shell) || hide {
        let _ = shell.err().say("\nTo learn more, run the command again \
                                 with --verbose.".to_string(), BLACK);
    }

    std::process::exit(exit_code);
}

fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
    let verbose = shell.get_verbose();
    let mut err;
    loop {
        cargo_err = match cargo_err.cargo_cause() {
            Some(cause) => cause,
            None => { err = cargo_err.cause(); break }
        };
        if verbose != Verbose && !cargo_err.is_human() { return false }
        print(cargo_err.to_string(), shell);
    }
    loop {
        let cause = match err { Some(err) => err, None => return true };
        if verbose != Verbose { return false }
        print(cause.to_string(), shell);
        err = cause.cause();
    }

    fn print(error: String, shell: &mut MultiShell) {
        let _ = shell.err().say("\nCaused by:", BLACK);
        let _ = shell.err().say(format!("  {}", error), BLACK);
    }
}

pub fn version() -> String {
    format!("cargo {}", match option_env!("CFG_VERSION") {
        Some(s) => s.to_string(),
        None => format!("{}.{}.{}{}",
                        env!("CARGO_PKG_VERSION_MAJOR"),
                        env!("CARGO_PKG_VERSION_MINOR"),
                        env!("CARGO_PKG_VERSION_PATCH"),
                        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""))
    })
}

fn flags_from_args<'a, T>(usage: &str, args: &[String],
                          options_first: bool) -> CliResult<T>
    where T: Decodable
{
    let docopt = Docopt::new(usage).unwrap()
                                   .options_first(options_first)
                                   .argv(args.iter().map(|s| &s[..]))
                                   .help(true)
                                   .version(Some(version()));
    docopt.decode().map_err(|e| {
        let code = if e.fatal() {1} else {0};
        CliError::from_error(human(e.to_string()), code)
    })
}

fn json_from_stdin<T: Decodable>() -> CliResult<T> {
    let mut reader = io::stdin();
    let mut input = String::new();
    try!(reader.read_to_string(&mut input).map_err(|_| {
        CliError::new("Standard in did not exist or was not UTF-8", 1)
    }));

    let json = try!(Json::from_str(&input).map_err(|_| {
        CliError::new("Could not parse standard in as JSON", 1)
    }));
    let mut decoder = json::Decoder::new(json);

    Decodable::decode(&mut decoder).map_err(|_| {
        CliError::new("Could not process standard in as input", 1)
    })
}
<|endoftext|>