<commit_before>#[allow(unused_imports)]
use libc::{c_char, c_int, c_uchar, c_void};

use crate::object::*;
use crate::pyport::Py_ssize_t;

#[cfg(Py_3_8)]
#[repr(C)]
pub struct _PyOpcache {
    _private: [u8; 0],
}

#[derive(Copy)]
#[repr(C)]
#[cfg(Py_3_11)]
// The field orderings have completely changed in 3.11,
// so we seperate it out into a different type declaration
//
// the justification for the reordering was "optimization"
pub struct PyCodeObject {
    pub ob_base: PyObject,
    pub co_consts: *mut PyObject,
    pub co_names: *mut PyObject,
    pub co_exceptiontable: *mut PyObject,
    pub co_flags: c_int,
    pub co_warmup: c_int,
    pub co_argcount: c_int,
    pub co_posonlyargcount: c_int,
    pub co_kwonlyargcount: c_int,
    pub co_stacksize: c_int,
    pub co_firstlineno: c_int,
    pub co_nlocalsplus: c_int,
    pub co_nlocals: c_int,
    pub co_nplaincellvars: c_int,
    pub co_ncellvars: c_int,
    pub co_nfreevars: c_int,
    pub co_localsplusnames: *mut PyObject,
    pub co_localspluskinds: *mut PyObject,
    pub co_filename: *mut PyObject,
    pub co_name: *mut PyObject,
    pub co_qualname: *mut PyObject,
    pub co_linetable: *mut PyObject,
    pub co_weakreflist: *mut PyObject,
    pub co_extra: *mut c_void,
    pub co_code_adaptive: [c_char; 1],
}

#[repr(C)]
#[derive(Copy)]
#[cfg(not(Py_3_11))]
pub struct PyCodeObject {
    pub ob_base: PyObject,
    pub co_argcount: c_int,
    #[cfg(Py_3_8)]
    pub co_posonlyargcount: c_int,
    pub co_kwonlyargcount: c_int,
    pub co_nlocals: c_int,
    pub co_stacksize: c_int,
    pub co_flags: c_int,
    #[cfg(Py_3_6)]
    pub co_firstlineno: c_int,
    pub co_code: *mut PyObject,
    pub co_consts: *mut PyObject,
    pub co_names: *mut PyObject,
    pub co_varnames: *mut PyObject,
    pub co_freevars: *mut PyObject,
    pub co_cellvars: *mut PyObject,
    #[cfg(not(Py_3_7))]
    pub co_cell2arg: *mut c_uchar,
    #[cfg(Py_3_7)]
    pub co_cell2arg: *mut Py_ssize_t,
    pub co_filename: *mut PyObject,
    pub co_name: *mut PyObject,
    #[cfg(not(Py_3_6))]
    pub co_firstlineno: c_int,
    #[cfg(not(Py_3_10))]
    pub co_lnotab: *mut PyObject,
    #[cfg(Py_3_10)]
    pub co_linetable: *mut PyObject,
    pub co_zombieframe: *mut c_void,
    pub co_weakreflist: *mut PyObject,
    #[cfg(Py_3_6)]
    pub co_extra: *mut c_void,
    #[cfg(Py_3_8)]
    pub co_opcache_map: *mut c_uchar,
    #[cfg(Py_3_8)]
    pub co_opcache: *mut _PyOpcache,
    #[cfg(Py_3_8)]
    pub co_opcache_flag: c_int,
    #[cfg(Py_3_8)]
    pub co_opcache_size: c_uchar,
}
impl Clone for PyCodeObject {
    #[inline]
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for PyCodeObject {
    #[inline]
    fn default() -> Self {
        unsafe { core::mem::zeroed() }
    }
}

/* Masks for co_flags */
pub const CO_OPTIMIZED: c_int = 0x0001;
pub const CO_NEWLOCALS: c_int = 0x0002;
pub const CO_VARARGS: c_int = 0x0004;
pub const CO_VARKEYWORDS: c_int = 0x0008;
pub const CO_NESTED: c_int = 0x0010;
pub const CO_GENERATOR: c_int = 0x0020;
/* The CO_NOFREE flag is set if there are no free or cell variables.
   This information is redundant, but it allows a single flag test
   to determine whether there is any extra work to be done when the
   call frame it setup.
*/
pub const CO_NOFREE: c_int = 0x0040;
/* The CO_COROUTINE flag is set for coroutine functions (defined with
``async def`` keywords) */
#[cfg(Py_3_5)]
pub const CO_COROUTINE: c_int = 0x0080;
#[cfg(Py_3_5)]
pub const CO_ITERABLE_COROUTINE: c_int = 0x0100;
#[cfg(Py_3_6)]
pub const CO_ASYNC_GENERATOR: c_int = 0x0200;

pub const CO_FUTURE_DIVISION: c_int = 0x2000;
pub const CO_FUTURE_ABSOLUTE_IMPORT: c_int = 0x4000; /* do absolute imports by default */
pub const CO_FUTURE_WITH_STATEMENT: c_int = 0x8000;
pub const CO_FUTURE_PRINT_FUNCTION: c_int = 0x10000;
pub const CO_FUTURE_UNICODE_LITERALS: c_int = 0x20000;
pub const CO_FUTURE_BARRY_AS_BDFL: c_int = 0x40000;
#[cfg(Py_3_5)]
pub const CO_FUTURE_GENERATOR_STOP: c_int = 0x80000;
#[cfg(Py_3_7)]
pub const CO_FUTURE_ANNOTATIONS: c_int = 0x100000;

#[cfg(not(Py_3_7))]
pub const CO_CELL_NOT_AN_ARG: c_uchar = 255;
#[cfg(Py_3_7)]
pub const CO_CELL_NOT_AN_ARG: Py_ssize_t = -1;

pub const CO_MAXBLOCKS: usize = 20;

#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
    pub static mut PyCode_Type: PyTypeObject;

    pub fn PyCode_New(
        argcount: c_int,
        kwonlyargcount: c_int,
        nlocals: c_int,
        stacksize: c_int,
        flags: c_int,
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        varnames: *mut PyObject,
        freevars: *mut PyObject,
        cellvars: *mut PyObject,
        filename: *mut PyObject,
        name: *mut PyObject,
        firstlineno: c_int,
        lnotab: *mut PyObject,
        #[cfg(Py_3_11)]
        exceptiontable: *mut PyObject,
    ) -> *mut PyCodeObject;

    #[cfg(Py_3_8)]
    pub fn PyCode_NewWithPosOnlyArgs(
        argcount: c_int,
        posonlyargcount: c_int,
        kwonlyargcount: c_int,
        nlocals: c_int,
        stacksize: c_int,
        flags: c_int,
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        varnames: *mut PyObject,
        freevars: *mut PyObject,
        cellvars: *mut PyObject,
        filename: *mut PyObject,
        name: *mut PyObject,
        firstlineno: c_int,
        lnotab: *mut PyObject,
        #[cfg(Py_3_11)]
        exceptiontable: *mut PyObject,
    ) -> *mut PyCodeObject;

    pub fn PyCode_NewEmpty(
        filename: *const c_char,
        funcname: *const c_char,
        firstlineno: c_int,
    ) -> *mut PyCodeObject;
    pub fn PyCode_Addr2Line(arg1: *mut PyCodeObject, arg2: c_int) -> c_int;
    #[cfg(Py_3_11)]
    pub fn PyCode_Addr2Location(
        co: *mut PyCodeObject,
        byte_offset: c_int,
        start_line: *mut c_int,
        start_column: *mut c_int,
        end_line: *mut c_int,
        end_column: *mut c_int
    ) -> c_int;
    #[cfg(Py_3_11)]
    pub fn PyCode_GetCode(co: *mut PyCodeObject) -> *mut PyObject;
    pub fn PyCode_Optimize(
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        lnotab: *mut PyObject,
    ) -> *mut PyObject;
}

#[inline]
pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int {
    (Py_TYPE(op) == &mut PyCode_Type) as c_int
}

#[inline]
#[cfg(not(Py_3_11))] // TODO: Implemengt again (fields are private)
pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t {
    crate::tupleobject::PyTuple_GET_SIZE((*op).co_freevars)
}
<commit_msg>Adjust PyCodeObject to Python 3.11 changes<commit_after>#[allow(unused_imports)]
use libc::{c_char, c_short, c_int, c_uchar, c_void};

use crate::object::*;
use crate::pyport::Py_ssize_t;

#[cfg(Py_3_8)]
#[repr(C)]
pub struct _PyOpcache {
    _private: [u8; 0],
}

#[derive(Copy)]
#[repr(C)]
#[cfg(Py_3_11)]
// The field orderings have completely changed in 3.11,
// so we seperate it out into a different type declaration
//
// the justification for the reordering was "optimization"
pub struct PyCodeObject {
    pub ob_base: PyVarObject,
    pub co_consts: *mut PyObject,
    pub co_names: *mut PyObject,
    pub co_exceptiontable: *mut PyObject,
    pub co_flags: c_int,
    pub co_warmup: c_short,
    co_linearray_entry_size: c_short,
    pub co_argcount: c_int,
    pub co_posonlyargcount: c_int,
    pub co_kwonlyargcount: c_int,
    pub co_stacksize: c_int,
    pub co_firstlineno: c_int,
    pub co_nlocalsplus: c_int,
    pub co_nlocals: c_int,
    pub co_nplaincellvars: c_int,
    pub co_ncellvars: c_int,
    pub co_nfreevars: c_int,
    pub co_localsplusnames: *mut PyObject,
    pub co_localspluskinds: *mut PyObject,
    pub co_filename: *mut PyObject,
    pub co_name: *mut PyObject,
    pub co_qualname: *mut PyObject,
    pub co_linetable: *mut PyObject,
    pub co_weakreflist: *mut PyObject,
    // Intently omitting some internal fields at the end of this structure
}

#[repr(C)]
#[derive(Copy)]
#[cfg(not(Py_3_11))]
pub struct PyCodeObject {
    pub ob_base: PyObject,
    pub co_argcount: c_int,
    #[cfg(Py_3_8)]
    pub co_posonlyargcount: c_int,
    pub co_kwonlyargcount: c_int,
    pub co_nlocals: c_int,
    pub co_stacksize: c_int,
    pub co_flags: c_int,
    #[cfg(Py_3_6)]
    pub co_firstlineno: c_int,
    pub co_code: *mut PyObject,
    pub co_consts: *mut PyObject,
    pub co_names: *mut PyObject,
    pub co_varnames: *mut PyObject,
    pub co_freevars: *mut PyObject,
    pub co_cellvars: *mut PyObject,
    #[cfg(not(Py_3_7))]
    pub co_cell2arg: *mut c_uchar,
    #[cfg(Py_3_7)]
    pub co_cell2arg: *mut Py_ssize_t,
    pub co_filename: *mut PyObject,
    pub co_name: *mut PyObject,
    #[cfg(not(Py_3_6))]
    pub co_firstlineno: c_int,
    #[cfg(not(Py_3_10))]
    pub co_lnotab: *mut PyObject,
    #[cfg(Py_3_10)]
    pub co_linetable: *mut PyObject,
    pub co_zombieframe: *mut c_void,
    pub co_weakreflist: *mut PyObject,
    #[cfg(Py_3_6)]
    pub co_extra: *mut c_void,
    #[cfg(Py_3_8)]
    pub co_opcache_map: *mut c_uchar,
    #[cfg(Py_3_8)]
    pub co_opcache: *mut _PyOpcache,
    #[cfg(Py_3_8)]
    pub co_opcache_flag: c_int,
    #[cfg(Py_3_8)]
    pub co_opcache_size: c_uchar,
}
impl Clone for PyCodeObject {
    #[inline]
    fn clone(&self) -> Self {
        *self
    }
}
impl Default for PyCodeObject {
    #[inline]
    fn default() -> Self {
        unsafe { core::mem::zeroed() }
    }
}

/* Masks for co_flags */
pub const CO_OPTIMIZED: c_int = 0x0001;
pub const CO_NEWLOCALS: c_int = 0x0002;
pub const CO_VARARGS: c_int = 0x0004;
pub const CO_VARKEYWORDS: c_int = 0x0008;
pub const CO_NESTED: c_int = 0x0010;
pub const CO_GENERATOR: c_int = 0x0020;
/* The CO_NOFREE flag is set if there are no free or cell variables.
   This information is redundant, but it allows a single flag test
   to determine whether there is any extra work to be done when the
   call frame it setup.
*/
pub const CO_NOFREE: c_int = 0x0040;
/* The CO_COROUTINE flag is set for coroutine functions (defined with
``async def`` keywords) */
#[cfg(Py_3_5)]
pub const CO_COROUTINE: c_int = 0x0080;
#[cfg(Py_3_5)]
pub const CO_ITERABLE_COROUTINE: c_int = 0x0100;
#[cfg(Py_3_6)]
pub const CO_ASYNC_GENERATOR: c_int = 0x0200;

pub const CO_FUTURE_DIVISION: c_int = 0x2000;
pub const CO_FUTURE_ABSOLUTE_IMPORT: c_int = 0x4000; /* do absolute imports by default */
pub const CO_FUTURE_WITH_STATEMENT: c_int = 0x8000;
pub const CO_FUTURE_PRINT_FUNCTION: c_int = 0x10000;
pub const CO_FUTURE_UNICODE_LITERALS: c_int = 0x20000;
pub const CO_FUTURE_BARRY_AS_BDFL: c_int = 0x40000;
#[cfg(Py_3_5)]
pub const CO_FUTURE_GENERATOR_STOP: c_int = 0x80000;
#[cfg(Py_3_7)]
pub const CO_FUTURE_ANNOTATIONS: c_int = 0x100000;

#[cfg(not(Py_3_7))]
pub const CO_CELL_NOT_AN_ARG: c_uchar = 255;
#[cfg(Py_3_7)]
pub const CO_CELL_NOT_AN_ARG: Py_ssize_t = -1;

pub const CO_MAXBLOCKS: usize = 20;

#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
    pub static mut PyCode_Type: PyTypeObject;

    pub fn PyCode_New(
        argcount: c_int,
        kwonlyargcount: c_int,
        nlocals: c_int,
        stacksize: c_int,
        flags: c_int,
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        varnames: *mut PyObject,
        freevars: *mut PyObject,
        cellvars: *mut PyObject,
        filename: *mut PyObject,
        name: *mut PyObject,
        firstlineno: c_int,
        lnotab: *mut PyObject,
        #[cfg(Py_3_11)]
        exceptiontable: *mut PyObject,
    ) -> *mut PyCodeObject;

    #[cfg(Py_3_8)]
    pub fn PyCode_NewWithPosOnlyArgs(
        argcount: c_int,
        posonlyargcount: c_int,
        kwonlyargcount: c_int,
        nlocals: c_int,
        stacksize: c_int,
        flags: c_int,
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        varnames: *mut PyObject,
        freevars: *mut PyObject,
        cellvars: *mut PyObject,
        filename: *mut PyObject,
        name: *mut PyObject,
        firstlineno: c_int,
        lnotab: *mut PyObject,
        #[cfg(Py_3_11)]
        exceptiontable: *mut PyObject,
    ) -> *mut PyCodeObject;

    pub fn PyCode_NewEmpty(
        filename: *const c_char,
        funcname: *const c_char,
        firstlineno: c_int,
    ) -> *mut PyCodeObject;
    pub fn PyCode_Addr2Line(arg1: *mut PyCodeObject, arg2: c_int) -> c_int;
    #[cfg(Py_3_11)]
    pub fn PyCode_Addr2Location(
        co: *mut PyCodeObject,
        byte_offset: c_int,
        start_line: *mut c_int,
        start_column: *mut c_int,
        end_line: *mut c_int,
        end_column: *mut c_int
    ) -> c_int;
    #[cfg(Py_3_11)]
    pub fn PyCode_GetCode(co: *mut PyCodeObject) -> *mut PyObject;
    pub fn PyCode_Optimize(
        code: *mut PyObject,
        consts: *mut PyObject,
        names: *mut PyObject,
        lnotab: *mut PyObject,
    ) -> *mut PyObject;
}

#[inline]
pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int {
    (Py_TYPE(op) == &mut PyCode_Type) as c_int
}

#[inline]
#[cfg(Py_3_11)]
pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t {
    (*op).co_nfreevars as Py_ssize_t
}

#[inline]
#[cfg(not(Py_3_11))]
pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t {
    crate::tupleobject::PyTuple_GET_SIZE((*op).co_freevars)
}
<|endoftext|>


<commit_before>use rustc_ast::ast;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;

use crate::transform::{MirPass, MirSource};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, Body, BodyAndCache, Local, Location};
use rustc_middle::ty::{self, Ty, TyCtxt};

use crate::dataflow::move_paths::{HasMoveData, MoveData};
use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
use crate::dataflow::MaybeMutBorrowedLocals;
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{Analysis, Results, ResultsCursor};
use crate::dataflow::{
    DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};

pub struct SanityCheck;

impl<'tcx> MirPass<'tcx> for SanityCheck {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        use crate::dataflow::has_rustc_mir_with;
        let def_id = src.def_id();
        if !tcx.has_attr(def_id, sym::rustc_mir) {
            debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
            return;
        } else {
            debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
        }

        let attributes = tcx.get_attrs(def_id);
        let param_env = tcx.param_env(def_id);
        let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
        let mdpe = MoveDataParamEnv { move_data, param_env };

        let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();

        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed);
        }
        if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
            tcx.sess.fatal("stop_after_dataflow ended compilation");
        }
    }
}

/// This function scans `mir` for all calls to the intrinsic
/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
///
/// For each such call, determines what the dataflow bit-state is for
/// the L-value corresponding to `expr`; if the bit-state is a 1, then
/// that call to `rustc_peek` is ignored by the sanity check. If the
/// bit-state is a 0, then this pass emits a error message saying
/// "rustc_peek: bit not set".
///
/// The intention is that one can write unit tests for dataflow by
/// putting code into a compile-fail test and using `rustc_peek` to
/// make observations about the results of dataflow static analyses.
///
/// (If there are any calls to `rustc_peek` that do not match the
/// expression form above, then that emits an error as well, but those
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, A>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    _attributes: &[ast::Attribute],
    results: &Results<'tcx, A>,
) where
    A: RustcPeekAt<'tcx>,
{
    debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);

    let mut cursor = ResultsCursor::new(body, results);

    let peek_calls = body.basic_blocks().iter_enumerated().filter_map(|(bb, block_data)| {
        PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
    });

    for (bb, block_data, call) in peek_calls {
        // Look for a sequence like the following to indicate that we should be peeking at `_1`:
        //    _2 = &_1;
        //    rustc_peek(_2);
        //
        //    /* or */
        //
        //    _2 = _1;
        //    rustc_peek(_2);
        let (statement_index, peek_rval) = block_data
            .statements
            .iter()
            .enumerate()
            .filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
            .next()
            .expect(
                "call to rustc_peek should be preceded by \
                    assignment to temporary holding its argument",
            );

        match (call.kind, peek_rval) {
            (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place)))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
                let loc = Location { block: bb, statement_index };
                cursor.seek_before(loc);
                let state = cursor.get();
                results.analysis.peek_at(tcx, place, state, call);
            }

            _ => {
                let msg = "rustc_peek: argument expression \
                           must be either `place` or `&place`";
                tcx.sess.span_err(call.span, msg);
            }
        }
    }
}

/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
/// RHS of the assignment.
fn value_assigned_to_local<'a, 'tcx>(
    stmt: &'a mir::Statement<'tcx>,
    local: Local,
) -> Option<&'a mir::Rvalue<'tcx>> {
    if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
        if let Some(l) = place.as_local() {
            if local == l {
                return Some(&*rvalue);
            }
        }
    }

    None
}

#[derive(Clone, Copy, Debug)]
enum PeekCallKind {
    ByVal,
    ByRef,
}

impl PeekCallKind {
    fn from_arg_ty(arg: Ty<'_>) -> Self {
        match arg.kind {
            ty::Ref(_, _, _) => PeekCallKind::ByRef,
            _ => PeekCallKind::ByVal,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub struct PeekCall {
    arg: Local,
    kind: PeekCallKind,
    span: Span,
}

impl PeekCall {
    fn from_terminator<'tcx>(
        tcx: TyCtxt<'tcx>,
        terminator: &mir::Terminator<'tcx>,
    ) -> Option<Self> {
        use mir::Operand;

        let span = terminator.source_info.span;
        if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
            &terminator.kind
        {
            if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
                let sig = tcx.fn_sig(def_id);
                let name = tcx.item_name(def_id);
                if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
                    return None;
                }

                assert_eq!(args.len(), 1);
                let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
                let arg = match &args[0] {
                    Operand::Copy(place) | Operand::Move(place) => {
                        if let Some(local) = place.as_local() {
                            local
                        } else {
                            tcx.sess.diagnostic().span_err(
                                span,
                                "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
                            );
                            return None;
                        }
                    }
                    _ => {
                        tcx.sess.diagnostic().span_err(
                            span,
                            "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
                        );
                        return None;
                    }
                };

                return Some(PeekCall { arg, kind, span });
            }
        }

        None
    }
}

pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: &mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    );
}

impl<'tcx, A> RustcPeekAt<'tcx> for A
where
    A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
{
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: &mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    ) {
        match self.move_data().rev_lookup.find(place.as_ref()) {
            LookupResult::Exact(peek_mpi) => {
                let bit_state = flow_state.contains(peek_mpi);
                debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
                if !bit_state {
                    tcx.sess.span_err(call.span, "rustc_peek: bit not set");
                }
            }

            LookupResult::Parent(..) => {
                tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
            }
        }
    }
}

impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> {
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: &mir::Place<'tcx>,
        flow_state: &BitSet<Local>,
        call: PeekCall,
    ) {
        warn!("peek_at: place={:?}", place);
        let local = if let Some(l) = place.as_local() {
            l
        } else {
            tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
            return;
        };

        if !flow_state.contains(local) {
            tcx.sess.span_err(call.span, "rustc_peek: bit not set");
        }
    }
}
<commit_msg>Use Place directly in peek_at, it's Copy<commit_after>use rustc_ast::ast;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;

use crate::transform::{MirPass, MirSource};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, Body, BodyAndCache, Local, Location};
use rustc_middle::ty::{self, Ty, TyCtxt};

use crate::dataflow::move_paths::{HasMoveData, MoveData};
use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
use crate::dataflow::MaybeMutBorrowedLocals;
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{Analysis, Results, ResultsCursor};
use crate::dataflow::{
    DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};

pub struct SanityCheck;

impl<'tcx> MirPass<'tcx> for SanityCheck {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
        use crate::dataflow::has_rustc_mir_with;
        let def_id = src.def_id();
        if !tcx.has_attr(def_id, sym::rustc_mir) {
            debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
            return;
        } else {
            debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
        }

        let attributes = tcx.get_attrs(def_id);
        let param_env = tcx.param_env(def_id);
        let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
        let mdpe = MoveDataParamEnv { move_data, param_env };

        let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();
        let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
            .into_engine(tcx, body, def_id)
            .iterate_to_fixpoint();

        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
        }
        if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed);
        }
        if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
            tcx.sess.fatal("stop_after_dataflow ended compilation");
        }
    }
}

/// This function scans `mir` for all calls to the intrinsic
/// `rustc_peek` that have the expression form `rustc_peek(&expr)`.
///
/// For each such call, determines what the dataflow bit-state is for
/// the L-value corresponding to `expr`; if the bit-state is a 1, then
/// that call to `rustc_peek` is ignored by the sanity check. If the
/// bit-state is a 0, then this pass emits a error message saying
/// "rustc_peek: bit not set".
///
/// The intention is that one can write unit tests for dataflow by
/// putting code into a compile-fail test and using `rustc_peek` to
/// make observations about the results of dataflow static analyses.
///
/// (If there are any calls to `rustc_peek` that do not match the
/// expression form above, then that emits an error as well, but those
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, A>(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    _attributes: &[ast::Attribute],
    results: &Results<'tcx, A>,
) where
    A: RustcPeekAt<'tcx>,
{
    debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);

    let mut cursor = ResultsCursor::new(body, results);

    let peek_calls = body.basic_blocks().iter_enumerated().filter_map(|(bb, block_data)| {
        PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
    });

    for (bb, block_data, call) in peek_calls {
        // Look for a sequence like the following to indicate that we should be peeking at `_1`:
        //    _2 = &_1;
        //    rustc_peek(_2);
        //
        //    /* or */
        //
        //    _2 = _1;
        //    rustc_peek(_2);
        let (statement_index, peek_rval) = block_data
            .statements
            .iter()
            .enumerate()
            .filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
            .next()
            .expect(
                "call to rustc_peek should be preceded by \
                    assignment to temporary holding its argument",
            );

        match (call.kind, peek_rval) {
            (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place)))
            | (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
                let loc = Location { block: bb, statement_index };
                cursor.seek_before(loc);
                let state = cursor.get();
                results.analysis.peek_at(tcx, *place, state, call);
            }

            _ => {
                let msg = "rustc_peek: argument expression \
                           must be either `place` or `&place`";
                tcx.sess.span_err(call.span, msg);
            }
        }
    }
}

/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
/// RHS of the assignment.
fn value_assigned_to_local<'a, 'tcx>(
    stmt: &'a mir::Statement<'tcx>,
    local: Local,
) -> Option<&'a mir::Rvalue<'tcx>> {
    if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
        if let Some(l) = place.as_local() {
            if local == l {
                return Some(&*rvalue);
            }
        }
    }

    None
}

#[derive(Clone, Copy, Debug)]
enum PeekCallKind {
    ByVal,
    ByRef,
}

impl PeekCallKind {
    fn from_arg_ty(arg: Ty<'_>) -> Self {
        match arg.kind {
            ty::Ref(_, _, _) => PeekCallKind::ByRef,
            _ => PeekCallKind::ByVal,
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub struct PeekCall {
    arg: Local,
    kind: PeekCallKind,
    span: Span,
}

impl PeekCall {
    fn from_terminator<'tcx>(
        tcx: TyCtxt<'tcx>,
        terminator: &mir::Terminator<'tcx>,
    ) -> Option<Self> {
        use mir::Operand;

        let span = terminator.source_info.span;
        if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
            &terminator.kind
        {
            if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
                let sig = tcx.fn_sig(def_id);
                let name = tcx.item_name(def_id);
                if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
                    return None;
                }

                assert_eq!(args.len(), 1);
                let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
                let arg = match &args[0] {
                    Operand::Copy(place) | Operand::Move(place) => {
                        if let Some(local) = place.as_local() {
                            local
                        } else {
                            tcx.sess.diagnostic().span_err(
                                span,
                                "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
                            );
                            return None;
                        }
                    }
                    _ => {
                        tcx.sess.diagnostic().span_err(
                            span,
                            "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
                        );
                        return None;
                    }
                };

                return Some(PeekCall { arg, kind, span });
            }
        }

        None
    }
}

pub trait RustcPeekAt<'tcx>: Analysis<'tcx> {
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    );
}

impl<'tcx, A> RustcPeekAt<'tcx> for A
where
    A: Analysis<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
{
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: mir::Place<'tcx>,
        flow_state: &BitSet<Self::Idx>,
        call: PeekCall,
    ) {
        match self.move_data().rev_lookup.find(place.as_ref()) {
            LookupResult::Exact(peek_mpi) => {
                let bit_state = flow_state.contains(peek_mpi);
                debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
                if !bit_state {
                    tcx.sess.span_err(call.span, "rustc_peek: bit not set");
                }
            }

            LookupResult::Parent(..) => {
                tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
            }
        }
    }
}

impl<'tcx> RustcPeekAt<'tcx> for MaybeMutBorrowedLocals<'_, 'tcx> {
    fn peek_at(
        &self,
        tcx: TyCtxt<'tcx>,
        place: mir::Place<'tcx>,
        flow_state: &BitSet<Local>,
        call: PeekCall,
    ) {
        warn!("peek_at: place={:?}", place);
        let local = if let Some(l) = place.as_local() {
            l
        } else {
            tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
            return;
        };

        if !flow_state.contains(local) {
            tcx.sess.span_err(call.span, "rustc_peek: bit not set");
        }
    }
}
<|endoftext|>


<commit_before>use satyrs::cnf::{CNF, Assignment, PartialAssignment};
use std::io;

/**
 * DPLL Algorithm implementation
 * Start by cloning the cnf so we can modify the HashMap
 * Clone might be slow, consider a faster option
 * TODO: Consider using HashSet instead of Vec for the clauses
 */
#[allow(non_snake_case)]
pub fn DPLL(cnf: &CNF) -> Option<Assignment> {
    //let _cnf = cnf.clone();
    //println!("DPLL {:?}", _cnf);
    let mut p_assn = PartialAssignment::new(cnf.nvar as usize);
    match _dpll(cnf, &mut p_assn) {
        Some(assn) => Some(assn.assignment.iter().map(|a| {
            println!("WE HERE ");
            match *a {
                Some(a) => a,
                None => true,
            }
        }).collect()),
        None => None,
    }
}

#[allow(unused_variables)]
fn _dpll(cnf: &CNF, p_assn: &mut PartialAssignment) -> Option<PartialAssignment> {
    // If consistent set of literals, return True
    println!("{}\n{}",cnf,p_assn);
    if cnf.clauses.is_empty() {
        let _p_assn = p_assn.clone();
        return Some(_p_assn);  // Display optional value
    }

    // If contains an empty clause return None
    // TODO: Consider optimizing
    for clause in cnf.clauses.values() {
        if clause.len() == 0 {
            return None;
        }
    }

    // For every unit-clause, unit-propogate
    let mut _cnf = cnf.clone();
    for unit in cnf.units.iter() {
        let clause = cnf.clauses.get(&unit).expect("Clause not found");
        let lit : i32 = zeroth!(clause);
        p_assn.assign_literal(lit);
        _cnf.unit_propagate(*unit); // Only propogate in the clone
    } // FIXME: Unit propogate might be the end of things
    println!("After Prop! {}",_cnf);
    // Clone for the right
    let mut r_cnf = _cnf.clone();

    // TODO: For every pure literal, pure literal assign
    // Choose literal L for split
    //let literal: i32 = zeroth!(_cnf.clauses.values().next().unwrap());
    let literal = match _cnf.clauses.values().next() {
        Some(thing) => Some(zeroth!(thing)),
        None => None,
    };

    if literal.is_none() {
        let _p_assn = p_assn.clone();
        return Some(_p_assn);
    }
    let lit = literal.unwrap();
    println!("Split on {} ({})",lit,lit/2);
    // Propagate literal
    _cnf.propagate(lit);

    // Return DPLL with L and -L
    p_assn.assign_literal(lit);

    //let mut buffer = String::new();
    //let _ = io::stdin().read_line(&mut buffer);

    let left = _dpll(&_cnf, p_assn);
    if left.is_some() { return left; }
    p_assn.unassign_literal(lit);
    p_assn.assign_literal(lit ^ 1);
    r_cnf.propagate(lit^1);
    _dpll(&_cnf, p_assn)
}
<commit_msg>Naive literal choice<commit_after>use satyrs::cnf::{CNF, Assignment, PartialAssignment};
use std::io;

/**
 * DPLL Algorithm implementation
 * Start by cloning the cnf so we can modify the HashMap
 * Clone might be slow, consider a faster option
 * TODO: Consider using HashSet instead of Vec for the clauses
 */
#[allow(non_snake_case)]
pub fn DPLL(cnf: &CNF) -> Option<Assignment> {
    //let _cnf = cnf.clone();
    //println!("DPLL {:?}", _cnf);
    let mut p_assn = PartialAssignment::new(cnf.nvar as usize);
    match _dpll(cnf, &mut p_assn) {
        Some(assn) => Some(assn.assignment.iter().map(|a| {
            println!("WE HERE ");
            match *a {
                Some(a) => a,
                None => true,
            }
        }).collect()),
        None => None,
    }
}

#[allow(unused_variables)]
fn _dpll(cnf: &CNF, p_assn: &mut PartialAssignment) -> Option<PartialAssignment> {
    // If consistent set of literals, return True
    println!("{}\n{}",cnf,p_assn);
    if cnf.clauses.is_empty() {
        let _p_assn = p_assn.clone();
        return Some(_p_assn);  // Display optional value
    }

    // If contains an empty clause return None
    // TODO: Consider optimizing
    for clause in cnf.clauses.values() {
        if clause.len() == 0 {
            return None;
        }
    }

    // For every unit-clause, unit-propogate
    let mut _cnf = cnf.clone();
    for unit in cnf.units.iter() {
        let clause = cnf.clauses.get(&unit).expect("Clause not found");
        let lit : i32 = zeroth!(clause);
        p_assn.assign_literal(lit);
        _cnf.unit_propagate(*unit); // Only propogate in the clone
    } // FIXME: Unit propogate might be the end of things
    println!("After Prop! {}",_cnf);
    // Clone for the right
    let mut r_cnf = _cnf.clone();

    // TODO: For every pure literal, pure literal assign
    // Choose literal L for split
    //let literal: i32 = zeroth!(_cnf.clauses.values().next().unwrap());
    let literal = match p_assn.unassigned.iter().next();
	/*
	match _cnf.clauses.values().next() {
        Some(thing) => Some(zeroth!(thing)),
        None => None,
    };
    */
    if literal.is_none() {
        let _p_assn = p_assn.clone();
        return Some(_p_assn);
    }
    let lit = literal.unwrap();
    println!("Split on {} ({})",lit,lit/2);
    // Propagate literal
    _cnf.propagate(lit);

    // Return DPLL with L and -L
    p_assn.assign_literal(lit);

    //let mut buffer = String::new();
    //let _ = io::stdin().read_line(&mut buffer);

    let left = _dpll(&_cnf, p_assn);
    if left.is_some() { return left; }
    p_assn.unassign_literal(lit);
    p_assn.assign_literal(lit ^ 1);
    r_cnf.propagate(lit^1);
    _dpll(&_cnf, p_assn)
}
<|endoftext|>


<commit_before>extern crate termion;
extern crate chrono;

use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{color, style};
use termion::event::Key;
use termion::terminal_size;
use std::io::{Read, Write, Stdout, Stdin};
use std::io::{stdout, stdin};
use std;

use chrono::*;
use utility::string::*;
use model::ShowReplyItem;
use model::ShowItem;
use reply_model::*;
use screen::common::*;

pub struct Show {
    scrollY: usize,
    y: usize,
    replier_max_width: usize,
    time_max_width: usize,
    is_scroll_to_end: bool
}

impl Show {
    pub fn new() -> Self {
        Show {
            scrollY: 0,
            y: 0,
            replier_max_width: 14,
            time_max_width: 5,
            is_scroll_to_end: false
        }
    }
    pub fn print(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, title: &str, item: &ShowItem) {

        self.y = 2;

        self.print_header(stdout, &format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(stdout, &item);
    }

    fn print_separator_top(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, reply: &ShowReplyItem) {
        if self.can_print() {
            let (replier_name, time) = make_separator_content(&reply);
            let s = self.build_separator_top(&replier_name, &time);
            self.print_separator_line(stdout, &s);
        }
    }

    fn print_separator_bottom(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>) {
        if self.can_print() {
            let s = self.build_separator_bottom();
            self.print_separator_line(stdout, &s);
        }
    }

    fn print_separator_line(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, s: &str) {

        write!(stdout, "{}{}{}{}{}",
                termion::cursor::Goto(1, (self.scrolledY() + 1) as u16),
                color::Fg(color::Green),
                s,
                style::Reset,
                termion::cursor::Hide);
    }

    fn print_header(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, text: &str) {
        let title_len = jks_len(text);
        let w = terminal_size().unwrap().0 as usize;
        let padding = ((if w >= title_len {
            w - title_len
        } else {
            0
        }) / 2) as u16;

        let header_bottom = seq_str_gen(0, w, "─", "");

        write!(stdout, "{}{}{}{}{}{}",
                termion::cursor::Goto(padding + 1, 1),
                color::Fg(color::White),
                style::Bold,
                text, style::Reset,
                termion::cursor::Hide);

        write!(stdout, "{}{}{}{}{}{}",
                termion::cursor::Goto(1, 2),
                color::Fg(color::Yellow),
                style::Bold,
                header_bottom,
                style::Reset,
                termion::cursor::Hide);
    }

    pub fn print_body(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            self.print_reply(stdout, &reply.body, 0);

            self.print_separator_top(stdout, &reply);
            self.y += 1;

            self.print_separator_bottom(stdout);
            self.y += 1;
        }

        self.is_scroll_to_end = self.scrolledY() < self.body_height();
    }

    fn print_reply_line(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, s: String) {

       write!(stdout, "{}{}{}{}{}",
               termion::cursor::Goto(1, (self.scrolledY() + 1) as u16),
               color::Fg(color::White),
               s,
               style::Reset,
               termion::cursor::Hide);
    }

    fn print_reply(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, vec: &Vec<NodeType>, depth: usize) {

        let icon_width = 2;
        let clown = imgcat(&"data/img/clown.gif", icon_width);
        let padding = seq_str_gen(0, depth, "├─", "");
        let mut line = String::new();
        let mut is_first = true;

        let vec_clean = clean_reply_body(vec);
        for (j, node) in vec_clean.iter().enumerate() {
            match node.clone() {
                NodeType::Text(n) => {
                    if n.data != "" {
                        line = format!("{}{}", line, n.data);
                    }
                }
                NodeType::Image(n) => {
                    if n.data != "" {
                        if self.can_print() {

                            line = format!("{}{}",
                                    line,
                                    clown);
                        }
                    }
                }
                NodeType::BlockQuote(n) => {
                    self.print_reply(stdout, &n.data, depth + 1);
                    is_first = false;
                }
                NodeType::Br(n) => {
                    if !line.is_empty() {
                        if self.can_print() {
                            self.print_reply_line(stdout, format!(" {}{}", padding, line));
                        }
                        line = String::new();
                        is_first = false;
                    }

                    // prevent first line empty
                    if !is_first {
                        self.y += 1;
                    }

                }
            }
        }

        if !line.is_empty() {
            if self.can_print() {
                self.print_reply_line(stdout, format!(" {}{}", padding, line));
            }
            line = String::new();
            self.y += 1;
        }
    }

    fn build_separator_arguments(&mut self) -> (usize, usize, String) {
        let separator_width = self.body_width();
        let w = terminal_size().unwrap().0 as usize;

        let separator_padding_width = if w > separator_width {
            w - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        (separator_width, separator_padding_width, separator_padding)
    }

    fn build_separator_top(&mut self, replier_name: &str, time: &str) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_top(separator_width,
                           &separator_padding,
                           self.replier_max_width,
                           &replier_name,
                           self.time_max_width,
                           &time)
    }

    fn build_separator_bottom(&mut self) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_bottom(separator_width, &separator_padding)
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {

        if !self.is_scroll_to_end {
            let min_to_scroll = self.scrollY + self.body_height();
            if self.y >= min_to_scroll + value {
                self.scrollY += value;
                return true;
            } else if self.y >= min_to_scroll {
                self.scrollY += self.y - min_to_scroll;
                return true;
            }
        }

        false
    }

    pub fn body_height(&self) -> usize {

        let h = terminal_size().unwrap().1;

        if h >= 3 {
            h as usize - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {

        let w = terminal_size().unwrap().0;

        if w >= 2 {
            w as usize - 2
        } else {
            0
        }
    }

    fn can_print(&self) -> bool {
        self.y > self.scrollY + 1 && self.y < self.scrollY + 1 + self.body_height()
    }

    fn scrolledY(&self) -> usize {
        self.y - self.scrollY
    }

}

fn make_separator_content(reply: &ShowReplyItem) -> (String, String) {
    let now = Local::now();

    let replier_name = reply.username.clone();

    let published_at = reply.published_at.clone();

    let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
        Ok(v) => v,
        Err(e) => now,
    };
    let time = published_at_format(&(now - published_at_dt));
    (replier_name, time)
}

fn clean_reply_body(vec: &Vec<NodeType>) -> Vec<NodeType> {
    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    vec3
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<commit_msg>show screen - bold reply separator<commit_after>extern crate termion;
extern crate chrono;

use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{color, style};
use termion::event::Key;
use termion::terminal_size;
use std::io::{Read, Write, Stdout, Stdin};
use std::io::{stdout, stdin};
use std;

use chrono::*;
use utility::string::*;
use model::ShowReplyItem;
use model::ShowItem;
use reply_model::*;
use screen::common::*;

pub struct Show {
    scrollY: usize,
    y: usize,
    replier_max_width: usize,
    time_max_width: usize,
    is_scroll_to_end: bool
}

impl Show {
    pub fn new() -> Self {
        Show {
            scrollY: 0,
            y: 0,
            replier_max_width: 14,
            time_max_width: 5,
            is_scroll_to_end: false
        }
    }
    pub fn print(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, title: &str, item: &ShowItem) {

        self.y = 2;

        self.print_header(stdout, &format!("{} - {} [{}/{}]",
                                   item.title,
                                   title,
                                   item.page,
                                   item.max_page));
        self.print_body(stdout, &item);
    }

    fn print_separator_top(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, reply: &ShowReplyItem) {
        if self.can_print() {
            let (replier_name, time) = make_separator_content(&reply);
            let s = self.build_separator_top(&replier_name, &time);
            self.print_separator_line(stdout, &s);
        }
    }

    fn print_separator_bottom(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>) {
        if self.can_print() {
            let s = self.build_separator_bottom();
            self.print_separator_line(stdout, &s);
        }
    }

    fn print_separator_line(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, s: &str) {

        write!(stdout, "{}{}{}{}{}{}",
                termion::cursor::Goto(1, (self.scrolledY() + 1) as u16),
                color::Fg(color::Green),
                style::Bold,
                s,
                style::Reset,
                termion::cursor::Hide);
    }

    fn print_header(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, text: &str) {
        let title_len = jks_len(text);
        let w = terminal_size().unwrap().0 as usize;
        let padding = ((if w >= title_len {
            w - title_len
        } else {
            0
        }) / 2) as u16;

        let header_bottom = seq_str_gen(0, w, "─", "");

        write!(stdout, "{}{}{}{}{}{}",
                termion::cursor::Goto(padding + 1, 1),
                color::Fg(color::White),
                style::Bold,
                text, style::Reset,
                termion::cursor::Hide);

        write!(stdout, "{}{}{}{}{}{}",
                termion::cursor::Goto(1, 2),
                color::Fg(color::Yellow),
                style::Bold,
                header_bottom,
                style::Reset,
                termion::cursor::Hide);
    }

    pub fn print_body(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, item: &ShowItem) {
        let width = self.body_width();
        let rows = self.body_height();

        for (i, reply) in item.replies.iter().take(rows).enumerate() {

            self.print_reply(stdout, &reply.body, 0);

            self.print_separator_top(stdout, &reply);
            self.y += 1;

            self.print_separator_bottom(stdout);
            self.y += 1;
        }

        self.is_scroll_to_end = self.scrolledY() < self.body_height();
    }

    fn print_reply_line(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, s: String) {

       write!(stdout, "{}{}{}{}{}",
               termion::cursor::Goto(1, (self.scrolledY() + 1) as u16),
               color::Fg(color::White),
               s,
               style::Reset,
               termion::cursor::Hide);
    }

    fn print_reply(&mut self, stdout: &mut termion::raw::RawTerminal<std::io::StdoutLock>, vec: &Vec<NodeType>, depth: usize) {

        let icon_width = 2;
        let clown = imgcat(&"data/img/clown.gif", icon_width);
        let padding = seq_str_gen(0, depth, "├─", "");
        let mut line = String::new();
        let mut is_first = true;

        let vec_clean = clean_reply_body(vec);
        for (j, node) in vec_clean.iter().enumerate() {
            match node.clone() {
                NodeType::Text(n) => {
                    if n.data != "" {
                        line = format!("{}{}", line, n.data);
                    }
                }
                NodeType::Image(n) => {
                    if n.data != "" {
                        if self.can_print() {

                            line = format!("{}{}",
                                    line,
                                    clown);
                        }
                    }
                }
                NodeType::BlockQuote(n) => {
                    self.print_reply(stdout, &n.data, depth + 1);
                    is_first = false;
                }
                NodeType::Br(n) => {
                    if !line.is_empty() {
                        if self.can_print() {
                            self.print_reply_line(stdout, format!(" {}{}", padding, line));
                        }
                        line = String::new();
                        is_first = false;
                    }

                    // prevent first line empty
                    if !is_first {
                        self.y += 1;
                    }

                }
            }
        }

        if !line.is_empty() {
            if self.can_print() {
                self.print_reply_line(stdout, format!(" {}{}", padding, line));
            }
            line = String::new();
            self.y += 1;
        }
    }

    fn build_separator_arguments(&mut self) -> (usize, usize, String) {
        let separator_width = self.body_width();
        let w = terminal_size().unwrap().0 as usize;

        let separator_padding_width = if w > separator_width {
            w - separator_width
        } else {
            0
        } / 2;

        let separator_padding = seq_str_gen(0, separator_padding_width, " ", "");

        (separator_width, separator_padding_width, separator_padding)
    }

    fn build_separator_top(&mut self, replier_name: &str, time: &str) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_top(separator_width,
                           &separator_padding,
                           self.replier_max_width,
                           &replier_name,
                           self.time_max_width,
                           &time)
    }

    fn build_separator_bottom(&mut self) -> String {
        let (separator_width, separator_padding_width, separator_padding) =
            self.build_separator_arguments();
        make_separator_bottom(separator_width, &separator_padding)
    }

    pub fn resetY(&mut self) {
        self.scrollY = 0;
    }

    pub fn scrollUp(&mut self, value: usize) -> bool {
        let tmp = self.scrollY;
        if tmp > value {
            self.scrollY = tmp - value;
            true
        } else if tmp != 0 {
            self.scrollY = 0;
            true
        } else {
            false
        }
    }

    pub fn scrollDown(&mut self, value: usize) -> bool {

        if !self.is_scroll_to_end {
            let min_to_scroll = self.scrollY + self.body_height();
            if self.y >= min_to_scroll + value {
                self.scrollY += value;
                return true;
            } else if self.y >= min_to_scroll {
                self.scrollY += self.y - min_to_scroll;
                return true;
            }
        }

        false
    }

    pub fn body_height(&self) -> usize {

        let h = terminal_size().unwrap().1;

        if h >= 3 {
            h as usize - 3
        } else {
            0
        }
    }

    pub fn body_width(&self) -> usize {

        let w = terminal_size().unwrap().0;

        if w >= 2 {
            w as usize - 2
        } else {
            0
        }
    }

    fn can_print(&self) -> bool {
        self.y > self.scrollY + 1 && self.y < self.scrollY + 1 + self.body_height()
    }

    fn scrolledY(&self) -> usize {
        self.y - self.scrollY
    }

}

fn make_separator_content(reply: &ShowReplyItem) -> (String, String) {
    let now = Local::now();

    let replier_name = reply.username.clone();

    let published_at = reply.published_at.clone();

    let published_at_dt = match Local.datetime_from_str(&published_at, "%d/%m/%Y %H:%M") {
        Ok(v) => v,
        Err(e) => now,
    };
    let time = published_at_format(&(now - published_at_dt));
    (replier_name, time)
}

fn clean_reply_body(vec: &Vec<NodeType>) -> Vec<NodeType> {
    // clean up lines (end)
    let vec2 = {
        let vec_length = vec.len();
        let vec_check_cleanup = vec.clone();

        // check if last 4 elements match the EMPTY PATTERN
        let is_last4_empty = vec_check_cleanup.iter()
                                              .rev()
                                              .take(4)
                                              .enumerate()
                                              .all(|(j, node)| match node.clone() {
                                                  NodeType::Br(n) => j == 1 || j == 2 || j == 3,
                                                  NodeType::Text(n) => j == 0 && n.data.is_empty(),
                                                  _ => false,
                                              });

        let vec_short_length = if vec_length > 4 && is_last4_empty {
            vec_length - 4
        } else {
            vec_length
        };

        vec.iter().take(vec_short_length)
    };

    // clean up lines (start)
    let vec3 = {
        let vec2_cloned = vec2.clone();
        let mut result: Vec<NodeType> = Vec::new();
        for (j, node) in vec2_cloned.enumerate() {
            let node2 = node.clone();
            let node3 = node.clone();
            match node2 {
                NodeType::Br(n) => {
                    if !result.is_empty() {
                        result.push(node3);
                    }
                }
                _ => result.push(node3),
            }
        }
        result.clone()
    };

    vec3
}

fn make_separator_replier_name(separator_width: usize,
                               separator_padding: &str,
                               replier_max_width: usize,
                               replier_name: &str)
                               -> String {
    let replier_name_len = jks_len(&replier_name);
    let replier_name_spacing_width = replier_max_width - replier_name_len;
    let is_replier_name_spacing_width_odd = replier_name_spacing_width & 1 == 1;
    let replier_name_right_spacing_width = replier_name_spacing_width / 2;
    let replier_name_left_spacing_width = if is_replier_name_spacing_width_odd {
        replier_name_right_spacing_width + 1
    } else {
        replier_name_right_spacing_width
    };

    let replier_name_left_spacing = seq_str_gen(0, replier_name_left_spacing_width, "─", "");
    let replier_name_right_spacing = seq_str_gen(0, replier_name_right_spacing_width, "─", "");

    let separator_replier = format!("{}{}{}{}{}",
                                    "╭",
                                    replier_name_left_spacing,
                                    replier_name,
                                    replier_name_right_spacing,
                                    "");

    return separator_replier;
}

fn make_separator_time(separator_width: usize,
                       separator_padding: &str,
                       time_max_width: usize,
                       time: &str)
                       -> String {
    let time_len = jks_len(&time);
    let time_spacing_width = if time_max_width > time_len {
        time_max_width - time_len
    } else {
        0
    };

    let is_time_spacing_width_odd = time_spacing_width & 1 == 1;
    let time_right_spacing_width = time_spacing_width / 2;
    let time_left_spacing_width = if is_time_spacing_width_odd {
        time_right_spacing_width + 1
    } else {
        time_right_spacing_width
    };

    let time_left_spacing = seq_str_gen(0, time_left_spacing_width, "─", "");
    let time_right_spacing = seq_str_gen(0, time_right_spacing_width, "─", "");

    let separator_time = format!("{}{}{}{}{}",
                                 "",
                                 time_left_spacing,
                                 time,
                                 time_right_spacing,
                                 "╮");

    return separator_time;
}

fn make_separator_top(separator_width: usize,
                      separator_padding: &str,
                      replier_max_width: usize,
                      replier_name: &str,
                      time_max_width: usize,
                      time: &str)
                      -> String {

    let separator_replier = make_separator_replier_name(separator_width,
                                                        &separator_padding,
                                                        replier_max_width,
                                                        &replier_name);

    let separator_replier_width = jks_len(&separator_replier);

    let separator_time = make_separator_time(separator_width,
                                             &separator_padding,
                                             time_max_width,
                                             &time);

    let separator_time_width = jks_len(&separator_time);

    let separator_top_middle_width = if separator_width >=
                                        (separator_replier_width + separator_time_width) {
        separator_width - separator_replier_width - separator_time_width
    } else {
        0
    };

    let separator_top_middle = seq_str_gen(0, separator_top_middle_width, " ", "");
    let separator_top = format!("{}{}{}{}{}",
                                separator_padding,
                                separator_top_middle,
                                separator_replier,
                                separator_time,
                                separator_padding);
    return separator_top;
}

fn make_separator_bottom(separator_width: usize, separator_padding: &str) -> String {
    let style_box_width = 1;
    let separator_bottom_middle_width = if separator_width > style_box_width {
        separator_width - style_box_width
    } else {
        0
    };
    let separator_bottom_middle = seq_str_gen(0, separator_bottom_middle_width, "─", "");

    let separator_bottom = format!("{}{}{}{}",
                                   separator_padding,
                                   separator_bottom_middle,
                                   "╯",
                                   separator_padding);
    return separator_bottom;
}

fn published_at_format(duration: &Duration) -> String {
    let weeks = duration.num_weeks();
    let days = duration.num_days();
    let hours = duration.num_hours();
    let minutes = duration.num_minutes();

    if weeks > 0 {
        format!("{}w", weeks)
    } else if days > 0 {
        format!("{}d", days)
    } else if hours > 0 {
        format!("{}h", hours)
    } else if minutes > 0 {
        format!("{}m", minutes)
    } else {
        String::from("1m")
    }
}

fn seq_str_gen(start: usize, end: usize, sym: &str, join_sym: &str) -> String {
    (start..end).map(|_| sym.clone()).collect::<Vec<_>>().join(&join_sym)
}
<|endoftext|>


<commit_before>import comm::*;
import libc::c_double;
import azure::*;
import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;
import parser::html;
import parser::html::methods;
import result::extensions;

fn parse(filename: str) {
    let file_data = io::read_whole_file(filename).get();
    let reader = io::bytes_reader(file_data);
    let parser = html::parser(reader);
    loop {
        let t = parser.parse();
        log(error, #fmt("token: %?", t));
        if t == html::to_eof { break; }
    }
}

fn main(args: [str]) {
    if args.len() >= 2u {
        parse(args[1]);
    }

    // The platform event handler thread
    let osmain = platform::osmain::osmain();

    // The compositor
    let renderer = gfx::renderer::renderer(osmain);

    // The layout task
    let layout = layout::layout::layout(renderer);

    // The content task
    let content = content::content(layout);

    // The keyboard handler
    let key_po = port();
    send(osmain, platform::osmain::add_key_handler(chan(key_po)));

    key_po.recv();
    comm::send(content, content::exit);
    comm::send(layout, layout::layout::exit);

    let draw_exit_confirm_po = comm::port();
    comm::send(renderer, gfx::renderer::exit(comm::chan(draw_exit_confirm_po)));

    comm::recv(draw_exit_confirm_po);
    comm::send(osmain, platform::osmain::exit);
}
<commit_msg>Modernize servo.rs<commit_after>import comm::*;
import libc::c_double;
import azure::*;
import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;
import parser::html;
import parser::html::methods;
import result::extensions;

fn parse(filename: str) {
    let file_data = io::read_whole_file(filename).get();
    let reader = io::bytes_reader(file_data);
    let parser = html::parser(reader);
    loop {
        let t = parser.parse();
        log(error, #fmt("token: %?", t));
        if t == html::to_eof { break; }
    }
}

fn main(args: [str]) {
    if args.len() >= 2u {
        parse(args[1]);
    }

    // The platform event handler thread
    let osmain = platform::osmain::osmain();

    // The compositor
    let renderer = gfx::renderer::renderer(osmain);

    // The layout task
    let layout = layout::layout::layout(renderer);

    // The content task
    let content = content::content(layout);

    // Wait for keypress
    listen {|key_ch|
        send(osmain, platform::osmain::add_key_handler(key_ch));

        key_ch.recv();
    }

    // Shut everything down
    content.send(content::exit);
    layout.send(layout::layout::exit);

    listen {|wait_ch|
        renderer.send(gfx::renderer::exit(wait_ch));
        wait_ch.recv();
    }
    osmain.send(platform::osmain::exit);
}
<|endoftext|>


<commit_before>use error::{Result, SassError, ErrorKind};
use evaluator::Evaluator;
use event::Event;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::mixin::{SassMixin, SassMixinParameter, SassMixinArgument};

use std::collections::HashMap;
use std::borrow::Cow::{self, Borrowed, Owned};

pub struct Substituter<'vm, I> {
    tokenizer: I,
    variables: HashMap<String, ValuePart<'vm>>,
    mixins:    HashMap<String, SassMixin<'vm>>,
}

impl<'vm, I> Substituter<'vm, I> {
    pub fn new(tokenizer: I) -> Substituter<'vm, I> {
        Substituter {
            tokenizer: tokenizer,
            variables: HashMap::new(),
            mixins:    HashMap::new(),
        }
    }

    fn replace_children_in_scope(
        &mut self,
        children: Vec<Event<'vm>>,
        passed_variables: Option<HashMap<String, ValuePart<'vm>>>,
        passed_mixins: Option<HashMap<String, SassMixin<'vm>>>) -> Result<Vec<Event<'vm>>> {

        let mut results = Vec::new();
        let mut local_variables = match passed_variables {
            Some(v) => v,
            None => HashMap::new(),
        };
        let local_mixins = match passed_mixins {
            Some(m) => m,
            None => self.mixins.clone(),
        };

        for c in children.into_iter() {
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());
                    let val = try!(owned_evaluated_value(value, &lvs));

                    let val = match val {
                        ValuePart::List(mut list) => {
                            match list.pop() {
                                Some(ValuePart::String(s)) => {
                                    if s == "!global" {
                                        self.variables.insert((*name).to_string(), ValuePart::List(list.clone()));
                                        ValuePart::List(list)
                                    } else {
                                        list.push(ValuePart::String(s));
                                        ValuePart::List(list)
                                    }
                                },
                                Some(other) => {
                                    list.push(other);
                                    ValuePart::List(list)
                                },
                                None => ValuePart::List(list)
                            }
                        }
                        other => other,
                    };

                    local_variables.insert((*name).to_string(), val);
                },
                Event::UnevaluatedProperty(name, value) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());

                    let mut ev = Evaluator::new_from_string(&value);
                    let ev_res = try!(ev.evaluate(&lvs)).into_owned();

                    results.push(Event::Property(
                        name,
                        ev_res,
                    ));
                },
                Event::Rule(rule) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());

                    let res = try!(self.replace_children_in_scope(
                        rule.children, Some(lvs), Some(local_mixins.clone())
                    ));
                    results.push(Event::Rule(SassRule {
                        children: res, ..rule
                    }));
                },
                Event::MixinCall(mixin_call) => {
                    let mixin_name = mixin_call.name.into_owned();
                    let mixin_definition = match local_mixins.get(&mixin_name) {
                        Some(mixin) => mixin,
                        None => return Err(SassError {
                            kind: ErrorKind::ExpectedMixin,
                            message: format!("Cannot find mixin named `{}`", mixin_name),
                        }),
                    };

                    let mut mixin_replacements = self.variables.clone();
                    mixin_replacements.extend(local_variables.clone());
                    mixin_replacements.extend(try!(collate_mixin_args(
                        &mixin_definition.parameters,
                        &mixin_call.arguments,
                    )));

                    let mut res = try!(self.replace_children_in_scope(
                        mixin_definition.children.clone(), Some(mixin_replacements), Some(local_mixins.clone())
                    ));

                    results.append(&mut res);
                },
                other => results.push(other),
            }
        }
        Ok(results)
    }
}

impl<'a, I> Iterator for Substituter<'a, I>
    where I: Iterator<Item = Result<Event<'a>>>
{
    type Item = Result<Event<'a>>;

    fn next(&mut self) -> Option<Result<Event<'a>>> {
        match self.tokenizer.next() {
            Some(Ok(Event::Variable(SassVariable { name, value }))) => {
                let val = match owned_evaluated_value(value, &self.variables) {
                    Ok(v) => v,
                    Err(e) => return Some(Err(e)),
                };
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(Ok(Event::Mixin(mixin))) => {
                self.mixins.insert((mixin.name).to_string(), mixin);
                self.next()
            },
            Some(Ok(Event::Rule(sass_rule))) => {
                let replaced = match self.replace_children_in_scope(
                    sass_rule.children, None, None
                ) {
                    Ok(children) => children,
                    Err(e) => return Some(Err(e)),
                };

                Some(Ok(Event::Rule(SassRule {
                    children: replaced, ..sass_rule
                })))
            },
            Some(Ok(Event::MixinCall(mixin_call))) => {
                let replaced = match self.replace_children_in_scope(
                    vec![Event::MixinCall(mixin_call)], None, None
                ) {
                    Ok(children) => children,
                    Err(e) => return Some(Err(e)),
                };

                Some(Ok(Event::List(replaced)))
            },
            other => other,
        }
    }
}

fn collate_mixin_args<'a>(
    parameters: &Vec<SassMixinParameter<'a>>,
    arguments: &Vec<SassMixinArgument<'a>>) -> Result<HashMap<String, ValuePart<'a>>> {

    let mut named_arguments = HashMap::new();

    for a in arguments.iter() {
        match a.name {
            Some(ref name) => {
                named_arguments.insert(name.clone(), a.value.clone().into_owned());
            },
            None => {},
        }
    }

    let mut replacements = HashMap::new();

    for (i, p) in parameters.iter().enumerate() {
        let replacement_name = p.name.to_string();

        let replacement_value =
            try!(named_arguments.get(&p.name).and_then( |v| Some(v.to_string()) )
                .or(arguments.get(i).and_then( |a| Some(a.value.clone().into_owned()) ))
                .or(p.default.clone().and_then( |d| Some(d.into_owned()) ))
                .ok_or(SassError {
                    kind: ErrorKind::ExpectedMixinArgument,
                    message: format!("Cannot find argument for mixin parameter named `{}` in arguments `{:?}`", p.name, arguments),
                }));

        replacements.insert(replacement_name, ValuePart::String(Owned(replacement_value)));
    }

    Ok(replacements)
}

fn owned_evaluated_value<'a>(
    value: Cow<'a, str>,
    variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {

    let value_part = match value {
        Cow::Borrowed(v) => {
            try!(Evaluator::new_from_string(&v).evaluate(variables))
        },
        Cow::Owned(v) => {
            try!(Evaluator::new_from_string(&v).evaluate(variables)).into_owned()
        },
    };
    Ok(match value_part {
        ValuePart::Number(nv) => ValuePart::Number(NumberValue { computed: true, ..nv }),
        other => other,
    })
}
<commit_msg>Evaluate mixin arguments' values too<commit_after>use error::{Result, SassError, ErrorKind};
use evaluator::Evaluator;
use event::Event;
use sass::rule::SassRule;
use sass::variable::SassVariable;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::mixin::{SassMixin, SassMixinParameter, SassMixinArgument};

use std::collections::HashMap;
use std::borrow::Cow::{self, Borrowed, Owned};

pub struct Substituter<'vm, I> {
    tokenizer: I,
    variables: HashMap<String, ValuePart<'vm>>,
    mixins:    HashMap<String, SassMixin<'vm>>,
}

impl<'vm, I> Substituter<'vm, I> {
    pub fn new(tokenizer: I) -> Substituter<'vm, I> {
        Substituter {
            tokenizer: tokenizer,
            variables: HashMap::new(),
            mixins:    HashMap::new(),
        }
    }

    fn replace_children_in_scope(
        &mut self,
        children: Vec<Event<'vm>>,
        passed_variables: Option<HashMap<String, ValuePart<'vm>>>,
        passed_mixins: Option<HashMap<String, SassMixin<'vm>>>) -> Result<Vec<Event<'vm>>> {

        let mut results = Vec::new();
        let mut local_variables = match passed_variables {
            Some(v) => v,
            None => HashMap::new(),
        };
        let local_mixins = match passed_mixins {
            Some(m) => m,
            None => self.mixins.clone(),
        };

        for c in children.into_iter() {
            match c {
                Event::Variable(SassVariable { name, value }) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());
                    let val = try!(owned_evaluated_value(value, &lvs));

                    let val = match val {
                        ValuePart::List(mut list) => {
                            match list.pop() {
                                Some(ValuePart::String(s)) => {
                                    if s == "!global" {
                                        self.variables.insert((*name).to_string(), ValuePart::List(list.clone()));
                                        ValuePart::List(list)
                                    } else {
                                        list.push(ValuePart::String(s));
                                        ValuePart::List(list)
                                    }
                                },
                                Some(other) => {
                                    list.push(other);
                                    ValuePart::List(list)
                                },
                                None => ValuePart::List(list)
                            }
                        }
                        other => other,
                    };

                    local_variables.insert((*name).to_string(), val);
                },
                Event::UnevaluatedProperty(name, value) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());

                    let mut ev = Evaluator::new_from_string(&value);
                    let ev_res = try!(ev.evaluate(&lvs)).into_owned();

                    results.push(Event::Property(
                        name,
                        ev_res,
                    ));
                },
                Event::Rule(rule) => {
                    let mut lvs = self.variables.clone();
                    lvs.extend(local_variables.clone());

                    let res = try!(self.replace_children_in_scope(
                        rule.children, Some(lvs), Some(local_mixins.clone())
                    ));
                    results.push(Event::Rule(SassRule {
                        children: res, ..rule
                    }));
                },
                Event::MixinCall(mixin_call) => {
                    let mixin_name = mixin_call.name.into_owned();
                    let mixin_definition = match local_mixins.get(&mixin_name) {
                        Some(mixin) => mixin,
                        None => return Err(SassError {
                            kind: ErrorKind::ExpectedMixin,
                            message: format!("Cannot find mixin named `{}`", mixin_name),
                        }),
                    };

                    let mut mixin_replacements = self.variables.clone();
                    mixin_replacements.extend(local_variables.clone());
                    let collated_args = try!(collate_mixin_args(
                        &mixin_definition.parameters,
                        &mixin_call.arguments,
                        &mixin_replacements,
                    ));
                    mixin_replacements.extend(collated_args);

                    let mut res = try!(self.replace_children_in_scope(
                        mixin_definition.children.clone(), Some(mixin_replacements), Some(local_mixins.clone())
                    ));

                    results.append(&mut res);
                },
                other => results.push(other),
            }
        }
        Ok(results)
    }
}

impl<'a, I> Iterator for Substituter<'a, I>
    where I: Iterator<Item = Result<Event<'a>>>
{
    type Item = Result<Event<'a>>;

    fn next(&mut self) -> Option<Result<Event<'a>>> {
        match self.tokenizer.next() {
            Some(Ok(Event::Variable(SassVariable { name, value }))) => {
                let val = match owned_evaluated_value(value, &self.variables) {
                    Ok(v) => v,
                    Err(e) => return Some(Err(e)),
                };
                self.variables.insert((*name).to_string(), val);
                self.next()
            },
            Some(Ok(Event::Mixin(mixin))) => {
                self.mixins.insert((mixin.name).to_string(), mixin);
                self.next()
            },
            Some(Ok(Event::Rule(sass_rule))) => {
                let replaced = match self.replace_children_in_scope(
                    sass_rule.children, None, None
                ) {
                    Ok(children) => children,
                    Err(e) => return Some(Err(e)),
                };

                Some(Ok(Event::Rule(SassRule {
                    children: replaced, ..sass_rule
                })))
            },
            Some(Ok(Event::MixinCall(mixin_call))) => {
                let replaced = match self.replace_children_in_scope(
                    vec![Event::MixinCall(mixin_call)], None, None
                ) {
                    Ok(children) => children,
                    Err(e) => return Some(Err(e)),
                };

                Some(Ok(Event::List(replaced)))
            },
            other => other,
        }
    }
}

fn collate_mixin_args<'a>(
    parameters: &Vec<SassMixinParameter<'a>>,
    arguments: &Vec<SassMixinArgument<'a>>,
    passed_variables: &HashMap<String, ValuePart<'a>>,
) -> Result<HashMap<String, ValuePart<'a>>> {

    let mut named_arguments = HashMap::new();

    for a in arguments.iter() {
        match a.name {
            Some(ref name) => {
                named_arguments.insert(name.clone(), a.value.clone().into_owned());
            },
            None => {},
        }
    }

    let mut replacements = HashMap::new();

    for (i, p) in parameters.iter().enumerate() {
        let replacement_name = p.name.to_string();

        let replacement_value =
            try!(named_arguments.get(&p.name).and_then( |v| Some(v.to_string()) )
                .or(arguments.get(i).and_then( |a| Some(a.value.clone().into_owned()) ))
                .or(p.default.clone().and_then( |d| Some(d.into_owned()) ))
                .ok_or(SassError {
                    kind: ErrorKind::ExpectedMixinArgument,
                    message: format!("Cannot find argument for mixin parameter named `{}` in arguments `{:?}`", p.name, arguments),
                }));

        let mut ev = Evaluator::new_from_string(&replacement_value);
        let ev_res = try!(ev.evaluate(&passed_variables)).into_owned();

        replacements.insert(replacement_name, ev_res);
    }

    Ok(replacements)
}

fn owned_evaluated_value<'a>(
    value: Cow<'a, str>,
    variables: &HashMap<String, ValuePart<'a>>) -> Result<ValuePart<'a>> {

    let value_part = match value {
        Cow::Borrowed(v) => {
            try!(Evaluator::new_from_string(&v).evaluate(variables))
        },
        Cow::Owned(v) => {
            try!(Evaluator::new_from_string(&v).evaluate(variables)).into_owned()
        },
    };
    Ok(match value_part {
        ValuePart::Number(nv) => ValuePart::Number(NumberValue { computed: true, ..nv }),
        other => other,
    })
}
<|endoftext|>