<commit_before>use rustc_errors::DiagnosticBuilder;
use rustc_span::Span;
use smallvec::smallvec;
use smallvec::SmallVec;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};

use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::util::*;

///////////////////////////////////////////////////////////////////////////
// `TraitAliasExpander` iterator
///////////////////////////////////////////////////////////////////////////

/// "Trait alias expansion" is the process of expanding a sequence of trait
/// references into another sequence by transitively following all trait
/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias
/// `trait Foo = Bar + Sync;`, and another trait alias
/// `trait Bar = Read + Write`, then the bounds would expand to
/// `Read + Write + Sync + Send`.
/// Expansion is done via a DFS (depth-first search), and the `visited` field
/// is used to avoid cycles.
pub struct TraitAliasExpander<'tcx> {
    tcx: TyCtxt<'tcx>,
    stack: Vec<TraitAliasExpansionInfo<'tcx>>,
}

/// Stores information about the expansion of a trait via a path of zero or more trait aliases.
#[derive(Debug, Clone)]
pub struct TraitAliasExpansionInfo<'tcx> {
    pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>,
}

impl<'tcx> TraitAliasExpansionInfo<'tcx> {
    fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
        Self { path: smallvec![(trait_ref, span)] }
    }

    /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
    /// trait aliases.
    pub fn label_with_exp_info(
        &self,
        diag: &mut DiagnosticBuilder<'_>,
        top_label: &str,
        use_desc: &str,
    ) {
        diag.span_label(self.top().1, top_label);
        if self.path.len() > 1 {
            for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
                diag.span_label(*sp, format!("referenced here ({})", use_desc));
            }
        }
        diag.span_label(
            self.bottom().1,
            format!("trait alias used in trait object type ({})", use_desc),
        );
    }

    pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> {
        &self.top().0
    }

    pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
        self.path.last().unwrap()
    }

    pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
        self.path.first().unwrap()
    }

    fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
        let mut path = self.path.clone();
        path.push((trait_ref, span));

        Self { path }
    }
}

pub fn expand_trait_aliases<'tcx>(
    tcx: TyCtxt<'tcx>,
    trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>,
) -> TraitAliasExpander<'tcx> {
    let items: Vec<_> =
        trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect();
    TraitAliasExpander { tcx, stack: items }
}

impl<'tcx> TraitAliasExpander<'tcx> {
    /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item`
    /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`.
    /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a
    /// trait alias.
    /// The return value indicates whether `item` should be yielded to the user.
    fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
        let tcx = self.tcx;
        let trait_ref = item.trait_ref();
        let pred = trait_ref.without_const().to_predicate();

        debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);

        // Don't recurse if this bound is not a trait alias.
        let is_alias = tcx.is_trait_alias(trait_ref.def_id());
        if !is_alias {
            return true;
        }

        // Don't recurse if this trait alias is already on the stack for the DFS search.
        let anon_pred = anonymize_predicate(tcx, &pred);
        if item.path.iter().rev().skip(1).any(|(tr, _)| {
            anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred
        }) {
            return false;
        }

        // Get components of trait alias.
        let predicates = tcx.super_predicates_of(trait_ref.def_id());

        let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
            pred.subst_supertrait(tcx, &trait_ref)
                .to_opt_poly_trait_ref()
                .map(|trait_ref| item.clone_and_push(trait_ref, *span))
        });
        debug!("expand_trait_aliases: items={:?}", items.clone());

        self.stack.extend(items);

        false
    }
}

impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
    type Item = TraitAliasExpansionInfo<'tcx>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.stack.len(), None)
    }

    fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> {
        while let Some(item) = self.stack.pop() {
            if self.expand(&item) {
                return Some(item);
            }
        }
        None
    }
}

///////////////////////////////////////////////////////////////////////////
// Iterator over def-IDs of supertraits
///////////////////////////////////////////////////////////////////////////

pub struct SupertraitDefIds<'tcx> {
    tcx: TyCtxt<'tcx>,
    stack: Vec<DefId>,
    visited: FxHashSet<DefId>,
}

pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> {
    SupertraitDefIds {
        tcx,
        stack: vec![trait_def_id],
        visited: Some(trait_def_id).into_iter().collect(),
    }
}

impl Iterator for SupertraitDefIds<'tcx> {
    type Item = DefId;

    fn next(&mut self) -> Option<DefId> {
        let def_id = self.stack.pop()?;
        let predicates = self.tcx.super_predicates_of(def_id);
        let visited = &mut self.visited;
        self.stack.extend(
            predicates
                .predicates
                .iter()
                .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref())
                .map(|trait_ref| trait_ref.def_id())
                .filter(|&super_def_id| visited.insert(super_def_id)),
        );
        Some(def_id)
    }
}

///////////////////////////////////////////////////////////////////////////
// Other
///////////////////////////////////////////////////////////////////////////

/// Instantiate all bound parameters of the impl with the given substs,
/// returning the resulting trait ref and all obligations that arise.
/// The obligations are closed under normalization.
pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
    selcx: &mut SelectionContext<'a, 'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    impl_def_id: DefId,
    impl_substs: SubstsRef<'tcx>,
) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
    let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
    let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
    let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
        super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref);

    let predicates = selcx.tcx().predicates_of(impl_def_id);
    let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
    let Normalized { value: predicates, obligations: normalization_obligations2 } =
        super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates);
    let impl_obligations =
        predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates);

    let impl_obligations = impl_obligations
        .chain(normalization_obligations1.into_iter())
        .chain(normalization_obligations2.into_iter());

    (impl_trait_ref, impl_obligations)
}

/// See [`super::obligations_for_generics`].
pub fn predicates_for_generics<'tcx>(
    cause: ObligationCause<'tcx>,
    recursion_depth: usize,
    param_env: ty::ParamEnv<'tcx>,
    generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
    debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);

    generic_bounds.predicates.into_iter().map(move |predicate| Obligation {
        cause: cause.clone(),
        recursion_depth,
        param_env,
        predicate,
    })
}

pub fn predicate_for_trait_ref<'tcx>(
    cause: ObligationCause<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    trait_ref: ty::TraitRef<'tcx>,
    recursion_depth: usize,
) -> PredicateObligation<'tcx> {
    Obligation {
        cause,
        param_env,
        recursion_depth,
        predicate: trait_ref.without_const().to_predicate(),
    }
}

pub fn predicate_for_trait_def(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    cause: ObligationCause<'tcx>,
    trait_def_id: DefId,
    recursion_depth: usize,
    self_ty: Ty<'tcx>,
    params: &[GenericArg<'tcx>],
) -> PredicateObligation<'tcx> {
    let trait_ref =
        ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) };
    predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth)
}

/// Casts a trait reference into a reference to one of its super
/// traits; returns `None` if `target_trait_def_id` is not a
/// supertrait.
pub fn upcast_choices(
    tcx: TyCtxt<'tcx>,
    source_trait_ref: ty::PolyTraitRef<'tcx>,
    target_trait_def_id: DefId,
) -> Vec<ty::PolyTraitRef<'tcx>> {
    if source_trait_ref.def_id() == target_trait_def_id {
        return vec![source_trait_ref]; // Shortcut the most common case.
    }

    supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
}

/// Given a trait `trait_ref`, returns the number of vtable entries
/// that come from `trait_ref`, excluding its supertraits. Used in
/// computing the vtable base for an upcast trait of a trait object.
pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
    let mut entries = 0;
    // Count number of methods and add them to the total offset.
    // Skip over associated types and constants.
    for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() {
        if trait_item.kind == ty::AssocKind::Fn {
            entries += 1;
        }
    }
    entries
}

/// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<N>(
    tcx: TyCtxt<'tcx>,
    object: &super::VtableObjectData<'tcx, N>,
    method_def_id: DefId,
) -> usize {
    // Count number of methods preceding the one we are selecting and
    // add them to the total offset.
    // Skip over associated types and constants.
    let mut entries = object.vtable_base;
    for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() {
        if trait_item.def_id == method_def_id {
            // The item with the ID we were given really ought to be a method.
            assert_eq!(trait_item.kind, ty::AssocKind::Fn);
            return entries;
        }
        if trait_item.kind == ty::AssocKind::Fn {
            entries += 1;
        }
    }

    bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id);
}

pub fn closure_trait_ref_and_return_type(
    tcx: TyCtxt<'tcx>,
    fn_trait_def_id: DefId,
    self_ty: Ty<'tcx>,
    sig: ty::PolyFnSig<'tcx>,
    tuple_arguments: TupleArgumentsFlag,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> {
    let arguments_tuple = match tuple_arguments {
        TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
        TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()),
    };
    let trait_ref = ty::TraitRef {
        def_id: fn_trait_def_id,
        substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]),
    };
    ty::Binder::bind((trait_ref, sig.skip_binder().output()))
}

pub fn generator_trait_ref_and_outputs(
    tcx: TyCtxt<'tcx>,
    fn_trait_def_id: DefId,
    self_ty: Ty<'tcx>,
    sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
    let trait_ref = ty::TraitRef {
        def_id: fn_trait_def_id,
        substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]),
    };
    ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
}

pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
    assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final()
}

pub enum TupleArgumentsFlag {
    Yes,
    No,
}
<commit_msg>remove obsolete comment<commit_after>use rustc_errors::DiagnosticBuilder;
use rustc_span::Span;
use smallvec::smallvec;
use smallvec::SmallVec;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};

use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::util::*;

///////////////////////////////////////////////////////////////////////////
// `TraitAliasExpander` iterator
///////////////////////////////////////////////////////////////////////////

/// "Trait alias expansion" is the process of expanding a sequence of trait
/// references into another sequence by transitively following all trait
/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias
/// `trait Foo = Bar + Sync;`, and another trait alias
/// `trait Bar = Read + Write`, then the bounds would expand to
/// `Read + Write + Sync + Send`.
/// Expansion is done via a DFS (depth-first search), and the `visited` field
/// is used to avoid cycles.
pub struct TraitAliasExpander<'tcx> {
    tcx: TyCtxt<'tcx>,
    stack: Vec<TraitAliasExpansionInfo<'tcx>>,
}

/// Stores information about the expansion of a trait via a path of zero or more trait aliases.
#[derive(Debug, Clone)]
pub struct TraitAliasExpansionInfo<'tcx> {
    pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>,
}

impl<'tcx> TraitAliasExpansionInfo<'tcx> {
    fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
        Self { path: smallvec![(trait_ref, span)] }
    }

    /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
    /// trait aliases.
    pub fn label_with_exp_info(
        &self,
        diag: &mut DiagnosticBuilder<'_>,
        top_label: &str,
        use_desc: &str,
    ) {
        diag.span_label(self.top().1, top_label);
        if self.path.len() > 1 {
            for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
                diag.span_label(*sp, format!("referenced here ({})", use_desc));
            }
        }
        diag.span_label(
            self.bottom().1,
            format!("trait alias used in trait object type ({})", use_desc),
        );
    }

    pub fn trait_ref(&self) -> &ty::PolyTraitRef<'tcx> {
        &self.top().0
    }

    pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
        self.path.last().unwrap()
    }

    pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) {
        self.path.first().unwrap()
    }

    fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self {
        let mut path = self.path.clone();
        path.push((trait_ref, span));

        Self { path }
    }
}

pub fn expand_trait_aliases<'tcx>(
    tcx: TyCtxt<'tcx>,
    trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>,
) -> TraitAliasExpander<'tcx> {
    let items: Vec<_> =
        trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect();
    TraitAliasExpander { tcx, stack: items }
}

impl<'tcx> TraitAliasExpander<'tcx> {
    /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item`
    /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`.
    /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a
    /// trait alias.
    /// The return value indicates whether `item` should be yielded to the user.
    fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
        let tcx = self.tcx;
        let trait_ref = item.trait_ref();
        let pred = trait_ref.without_const().to_predicate();

        debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);

        // Don't recurse if this bound is not a trait alias.
        let is_alias = tcx.is_trait_alias(trait_ref.def_id());
        if !is_alias {
            return true;
        }

        // Don't recurse if this trait alias is already on the stack for the DFS search.
        let anon_pred = anonymize_predicate(tcx, &pred);
        if item.path.iter().rev().skip(1).any(|(tr, _)| {
            anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred
        }) {
            return false;
        }

        // Get components of trait alias.
        let predicates = tcx.super_predicates_of(trait_ref.def_id());

        let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
            pred.subst_supertrait(tcx, &trait_ref)
                .to_opt_poly_trait_ref()
                .map(|trait_ref| item.clone_and_push(trait_ref, *span))
        });
        debug!("expand_trait_aliases: items={:?}", items.clone());

        self.stack.extend(items);

        false
    }
}

impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
    type Item = TraitAliasExpansionInfo<'tcx>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.stack.len(), None)
    }

    fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> {
        while let Some(item) = self.stack.pop() {
            if self.expand(&item) {
                return Some(item);
            }
        }
        None
    }
}

///////////////////////////////////////////////////////////////////////////
// Iterator over def-IDs of supertraits
///////////////////////////////////////////////////////////////////////////

pub struct SupertraitDefIds<'tcx> {
    tcx: TyCtxt<'tcx>,
    stack: Vec<DefId>,
    visited: FxHashSet<DefId>,
}

pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> {
    SupertraitDefIds {
        tcx,
        stack: vec![trait_def_id],
        visited: Some(trait_def_id).into_iter().collect(),
    }
}

impl Iterator for SupertraitDefIds<'tcx> {
    type Item = DefId;

    fn next(&mut self) -> Option<DefId> {
        let def_id = self.stack.pop()?;
        let predicates = self.tcx.super_predicates_of(def_id);
        let visited = &mut self.visited;
        self.stack.extend(
            predicates
                .predicates
                .iter()
                .filter_map(|(pred, _)| pred.to_opt_poly_trait_ref())
                .map(|trait_ref| trait_ref.def_id())
                .filter(|&super_def_id| visited.insert(super_def_id)),
        );
        Some(def_id)
    }
}

///////////////////////////////////////////////////////////////////////////
// Other
///////////////////////////////////////////////////////////////////////////

/// Instantiate all bound parameters of the impl with the given substs,
/// returning the resulting trait ref and all obligations that arise.
/// The obligations are closed under normalization.
pub fn impl_trait_ref_and_oblig<'a, 'tcx>(
    selcx: &mut SelectionContext<'a, 'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    impl_def_id: DefId,
    impl_substs: SubstsRef<'tcx>,
) -> (ty::TraitRef<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
    let impl_trait_ref = selcx.tcx().impl_trait_ref(impl_def_id).unwrap();
    let impl_trait_ref = impl_trait_ref.subst(selcx.tcx(), impl_substs);
    let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
        super::normalize(selcx, param_env, ObligationCause::dummy(), &impl_trait_ref);

    let predicates = selcx.tcx().predicates_of(impl_def_id);
    let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
    let Normalized { value: predicates, obligations: normalization_obligations2 } =
        super::normalize(selcx, param_env, ObligationCause::dummy(), &predicates);
    let impl_obligations =
        predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates);

    let impl_obligations = impl_obligations
        .chain(normalization_obligations1.into_iter())
        .chain(normalization_obligations2.into_iter());

    (impl_trait_ref, impl_obligations)
}

pub fn predicates_for_generics<'tcx>(
    cause: ObligationCause<'tcx>,
    recursion_depth: usize,
    param_env: ty::ParamEnv<'tcx>,
    generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
    debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds);

    generic_bounds.predicates.into_iter().map(move |predicate| Obligation {
        cause: cause.clone(),
        recursion_depth,
        param_env,
        predicate,
    })
}

pub fn predicate_for_trait_ref<'tcx>(
    cause: ObligationCause<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    trait_ref: ty::TraitRef<'tcx>,
    recursion_depth: usize,
) -> PredicateObligation<'tcx> {
    Obligation {
        cause,
        param_env,
        recursion_depth,
        predicate: trait_ref.without_const().to_predicate(),
    }
}

pub fn predicate_for_trait_def(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    cause: ObligationCause<'tcx>,
    trait_def_id: DefId,
    recursion_depth: usize,
    self_ty: Ty<'tcx>,
    params: &[GenericArg<'tcx>],
) -> PredicateObligation<'tcx> {
    let trait_ref =
        ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) };
    predicate_for_trait_ref(cause, param_env, trait_ref, recursion_depth)
}

/// Casts a trait reference into a reference to one of its super
/// traits; returns `None` if `target_trait_def_id` is not a
/// supertrait.
pub fn upcast_choices(
    tcx: TyCtxt<'tcx>,
    source_trait_ref: ty::PolyTraitRef<'tcx>,
    target_trait_def_id: DefId,
) -> Vec<ty::PolyTraitRef<'tcx>> {
    if source_trait_ref.def_id() == target_trait_def_id {
        return vec![source_trait_ref]; // Shortcut the most common case.
    }

    supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
}

/// Given a trait `trait_ref`, returns the number of vtable entries
/// that come from `trait_ref`, excluding its supertraits. Used in
/// computing the vtable base for an upcast trait of a trait object.
pub fn count_own_vtable_entries(tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
    let mut entries = 0;
    // Count number of methods and add them to the total offset.
    // Skip over associated types and constants.
    for trait_item in tcx.associated_items(trait_ref.def_id()).in_definition_order() {
        if trait_item.kind == ty::AssocKind::Fn {
            entries += 1;
        }
    }
    entries
}

/// Given an upcast trait object described by `object`, returns the
/// index of the method `method_def_id` (which should be part of
/// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<N>(
    tcx: TyCtxt<'tcx>,
    object: &super::VtableObjectData<'tcx, N>,
    method_def_id: DefId,
) -> usize {
    // Count number of methods preceding the one we are selecting and
    // add them to the total offset.
    // Skip over associated types and constants.
    let mut entries = object.vtable_base;
    for trait_item in tcx.associated_items(object.upcast_trait_ref.def_id()).in_definition_order() {
        if trait_item.def_id == method_def_id {
            // The item with the ID we were given really ought to be a method.
            assert_eq!(trait_item.kind, ty::AssocKind::Fn);
            return entries;
        }
        if trait_item.kind == ty::AssocKind::Fn {
            entries += 1;
        }
    }

    bug!("get_vtable_index_of_object_method: {:?} was not found", method_def_id);
}

pub fn closure_trait_ref_and_return_type(
    tcx: TyCtxt<'tcx>,
    fn_trait_def_id: DefId,
    self_ty: Ty<'tcx>,
    sig: ty::PolyFnSig<'tcx>,
    tuple_arguments: TupleArgumentsFlag,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)> {
    let arguments_tuple = match tuple_arguments {
        TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
        TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()),
    };
    let trait_ref = ty::TraitRef {
        def_id: fn_trait_def_id,
        substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]),
    };
    ty::Binder::bind((trait_ref, sig.skip_binder().output()))
}

pub fn generator_trait_ref_and_outputs(
    tcx: TyCtxt<'tcx>,
    fn_trait_def_id: DefId,
    self_ty: Ty<'tcx>,
    sig: ty::PolyGenSig<'tcx>,
) -> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> {
    let trait_ref = ty::TraitRef {
        def_id: fn_trait_def_id,
        substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]),
    };
    ty::Binder::bind((trait_ref, sig.skip_binder().yield_ty, sig.skip_binder().return_ty))
}

pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
    assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final()
}

pub enum TupleArgumentsFlag {
    Yes,
    No,
}
<|endoftext|>


<commit_before>use crate::{
    compiler::{
        hir::{self, Expression, HirDb, Lambda},
        hir_to_lir::HirToLir,
    },
    database::Database,
    input::Input,
    vm::{dump_panicked_vm, tracer::TraceEntry, value::Value, Status, Vm},
};
use log;
use std::fs;

pub fn fuzz(db: &Database, input: Input) {
    let lir = db.lir(input.clone()).unwrap();

    let mut vm = Vm::new(lir.chunks.clone());
    vm.run(1000);
    match vm.status() {
        Status::Running => {
            log::warn!("VM didn't finish running, so we're not fuzzing it.");
            return;
        }
        Status::Done(value) => log::debug!("VM is done: {}", value),
        Status::Panicked(value) => {
            dump_panicked_vm(&db, input, &vm, value);
            return;
        }
    }

    log::info!(
        "Now, the fuzzing begins. So far, we have {} closures to fuzz.",
        vm.fuzzable_closures.len()
    );
    while let Some((closure_id, closure_address)) = vm.fuzzable_closures.pop() {
        let closure = vm.heap.export_without_dropping(closure_address);
        let num_args = if let Value::Closure { body, .. } = closure {
            vm.chunks[body].num_args
        } else {
            panic!("The VM registered a fuzzable closure that's not a closure.");
        };
        log::info!(
            "Fuzzing closure {} (id {}) with {} arguments.",
            closure,
            closure_id,
            num_args
        );

        let arguments = generate_fuzzing_arguments(num_args);
        vm.run_closure(closure_address, arguments);

        vm.run(1000);
        match vm.status() {
            Status::Running => {
                log::warn!("The fuzzer is giving up because the VM didn't finish running.");
                return;
            }
            Status::Done(value) => log::debug!("VM is done: {}", value),
            Status::Panicked(value) => {
                let did_need_in_closure_cause_panic =
                    did_need_in_closure_cause_panic(db, &closure_id, vm.tracer.log.last().unwrap());
                if did_need_in_closure_cause_panic {
                    log::debug!("The closure crashed for some input, but it's the fuzzer's fault.");
                } else {
                    log::error!("The fuzzer discovered an input that crashes the closure:");
                    dump_panicked_vm(&db, input.clone(), &vm, value);

                    let trace = vm.tracer.correlate_and_dump();
                    // PathBuff::new(input.to_path().unwrap())
                    let mut trace_file = input.to_path().unwrap();
                    trace_file.set_extension("candy.trace");
                    fs::write(trace_file.clone(), trace).unwrap();
                    log::info!(
                        "Trace has been written to `{}`.",
                        trace_file.as_path().display()
                    );
                }

                return;
            }
        }
    }
}

fn generate_fuzzing_arguments(num: usize) -> Vec<Value> {
    let mut args = vec![];
    for _ in 0..num {
        args.push(Value::Int(0));
    }
    args
}

fn did_need_in_closure_cause_panic(
    db: &Database,
    closure_id: &hir::Id,
    trace_entry: &TraceEntry,
) -> bool {
    if let TraceEntry::NeedsStarted { id, .. } = trace_entry {
        let mut id = id.parent().unwrap();
        loop {
            if &id == closure_id {
                return true;
            }

            match db
                .find_expression(id.clone())
                .expect("Parent of a `needs` call is a parameter.")
            {
                Expression::Lambda(Lambda { fuzzable, .. }) => {
                    if fuzzable {
                        return false; // The needs is in a different fuzzable lambda.
                    }
                }
                _ => panic!("Only lambdas can be the parent of a `needs` call."),
            };

            match id.parent() {
                Some(parent_id) => id = parent_id,
                None => return false,
            }
        }
    }
    return false;
}
<commit_msg>Implement more fuzzing<commit_after>use crate::{
    compiler::{
        hir::{self, Expression, HirDb, Lambda},
        hir_to_lir::HirToLir,
    },
    database::Database,
    input::Input,
    vm::{dump_panicked_vm, tracer::TraceEntry, value::Value, Status, Vm},
};
use log;
use std::fs;

pub fn fuzz(db: &Database, input: Input) {
    let lir = db.lir(input.clone()).unwrap();

    let mut vm = Vm::new(lir.chunks.clone());
    vm.run(1000);
    match vm.status() {
        Status::Running => {
            log::warn!("VM didn't finish running, so we're not fuzzing it.");
            return;
        }
        Status::Done(value) => log::debug!("VM is done: {}", value),
        Status::Panicked(value) => {
            dump_panicked_vm(&db, input, &vm, value);
            return;
        }
    }

    log::info!(
        "Now, the fuzzing begins. So far, we have {} closures to fuzz.",
        vm.fuzzable_closures.len()
    );

    fuzz_vm(db, input, &vm, 0);
}

fn fuzz_vm(db: &Database, input: Input, vm: &Vm, num_fuzzable_closures_to_skip: usize) {
    'test_different_closures: for (closure_id, closure_address) in vm
        .fuzzable_closures
        .iter()
        .skip(num_fuzzable_closures_to_skip)
    {
        let closure = vm.heap.export_without_dropping(*closure_address);
        let num_args = if let Value::Closure { body, .. } = closure {
            vm.chunks[body].num_args
        } else {
            panic!("The VM registered a fuzzable closure that's not a closure.");
        };
        log::info!("Fuzzing {}.", closure_id,);

        let fuzz_count = num_args * 20;

        for _ in 0..fuzz_count {
            // Snapshot a VM so we can run the fuzzing in the copy without modifying
            // the original VM.
            let mut vm = vm.clone();
            let arguments = generate_fuzzing_arguments(num_args);

            match test_closure_with_args(db, closure_id, &mut vm, *closure_address, arguments) {
                TestResult::StillRunning => {
                    log::warn!("The fuzzer is giving up because the VM didn't finish running.")
                }
                TestResult::NoPanic => {}
                TestResult::WrongInputs => {} // This is the fuzzer's fault.
                TestResult::InternalPanic(message) => {
                    log::error!("The fuzzer discovered an input that crashes the closure:");
                    dump_panicked_vm(&db, input.clone(), &vm, message);

                    let trace = vm.tracer.correlate_and_dump();
                    // PathBuff::new(input.to_path().unwrap())
                    let mut trace_file = input.to_path().unwrap();
                    trace_file.set_extension("candy.trace");
                    fs::write(trace_file.clone(), trace).unwrap();
                    log::info!(
                        "Trace has been written to `{}`.",
                        trace_file.as_path().display()
                    );
                    continue 'test_different_closures;
                }
            }
        }
        log::debug!("Couldn't find any issues with this function.");
    }
}

fn test_closure_with_args(
    db: &Database,
    closure_id: &hir::Id,
    vm: &mut Vm,
    closure_address: usize,
    arguments: Vec<Value>,
) -> TestResult {
    vm.run_closure(closure_address, arguments);

    vm.run(1000);
    match vm.status() {
        Status::Running => TestResult::StillRunning,
        Status::Done(_) => TestResult::NoPanic,
        Status::Panicked(message) => {
            // If a needs directly inside the tested closure was
            // dissatisfied, then the panic is not the fault of the code
            // inside the code, but of the caller.
            let is_our_fault =
                did_need_in_closure_cause_panic(db, &closure_id, vm.tracer.log.last().unwrap());
            if is_our_fault {
                TestResult::WrongInputs
            } else {
                TestResult::InternalPanic(message)
            }
        }
    }
}
enum TestResult {
    StillRunning,
    NoPanic,
    WrongInputs,
    InternalPanic(Value),
}

fn generate_fuzzing_arguments(num: usize) -> Vec<Value> {
    let mut args = vec![];
    for _ in 0..num {
        args.push(Value::Int(0));
    }
    args
}

fn did_need_in_closure_cause_panic(
    db: &Database,
    closure_id: &hir::Id,
    trace_entry: &TraceEntry,
) -> bool {
    if let TraceEntry::NeedsStarted { id, .. } = trace_entry {
        let mut id = id.parent().unwrap();
        loop {
            if &id == closure_id {
                return true;
            }

            match db
                .find_expression(id.clone())
                .expect("Parent of a `needs` call is a parameter.")
            {
                Expression::Lambda(Lambda { fuzzable, .. }) => {
                    if fuzzable {
                        return false; // The needs is in a different fuzzable lambda.
                    }
                }
                _ => panic!("Only lambdas can be the parent of a `needs` call."),
            };

            match id.parent() {
                Some(parent_id) => id = parent_id,
                None => return false,
            }
        }
    }
    return false;
}
<|endoftext|>


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

//! An actor-based remote devtools server implementation. Only tested with
//! nightly Firefox versions at time of writing. Largely based on
//! reverse-engineering of Firefox chrome devtool logs and reading of
//! [code](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/).

#![crate_name = "devtools"]
#![crate_type = "rlib"]

#![feature(box_syntax)]
#![feature(core)]
#![feature(custom_derive)]
#![feature(get_type_id)]
#![feature(plugin)]
#![feature(raw)]
#![feature(reflect_marker)]
#![plugin(serde_macros)]

#![allow(non_snake_case)]

#[macro_use]
extern crate log;

extern crate core;
extern crate devtools_traits;
extern crate rustc_serialize;
extern crate ipc_channel;
extern crate serde;
extern crate msg;
extern crate time;
extern crate util;
extern crate hyper;
extern crate url;

use actor::{Actor, ActorRegistry};
use actors::console::ConsoleActor;
use actors::network_event::{NetworkEventActor, EventActor, ResponseStartMsg};
use actors::framerate::FramerateActor;
use actors::inspector::InspectorActor;
use actors::object::ObjectActor;
use actors::root::RootActor;
use actors::tab::TabActor;
use actors::timeline::TimelineActor;
use actors::worker::WorkerActor;
use protocol::JsonPacketStream;

use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg};
use devtools_traits::{DevtoolsPageInfo, DevtoolScriptControlMsg, LogLevel, NetworkEvent};
use devtools_traits::{ScriptToDevtoolsControlMsg};
use msg::constellation_msg::{PipelineId, WorkerId};
use util::task::spawn_named;

use ipc_channel::ipc::IpcSender;
use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::error::Error;
use std::sync::mpsc::{channel, Receiver, Sender, RecvError};
use std::net::{TcpListener, TcpStream, Shutdown};
use std::sync::{Arc, Mutex};
use time::precise_time_ns;

mod actor;
/// Corresponds to http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/
mod actors {
    pub mod console;
    pub mod framerate;
    pub mod inspector;
    pub mod memory;
    pub mod network_event;
    pub mod object;
    pub mod root;
    pub mod tab;
    pub mod timeline;
    pub mod worker;
}
mod protocol;

#[derive(RustcEncodable)]
struct ConsoleAPICall {
    from: String,
    __type__: String,
    message: ConsoleMsg,
}

#[derive(RustcEncodable)]
struct ConsoleMsg {
    level: String,
    timeStamp: u64,
    arguments: Vec<String>,
    filename: String,
    lineNumber: u32,
    columnNumber: u32,
}

#[derive(RustcEncodable)]
struct NetworkEventMsg {
    from: String,
    __type__: String,
    eventActor: EventActor,
}

#[derive(RustcEncodable)]
struct NetworkEventUpdateMsg {
    from: String,
    __type__: String,
    updateType: String,
    response: ResponseStartMsg,
}

/// Spin up a devtools server that listens for connections on the specified port.
pub fn start_server(port: u16) -> Sender<DevtoolsControlMsg> {
    let (sender, receiver) = channel();
    {
        let sender = sender.clone();
        spawn_named("Devtools".to_owned(), move || {
            run_server(sender, receiver, port)
        });
    }
    sender
}

fn run_server(sender: Sender<DevtoolsControlMsg>,
              receiver: Receiver<DevtoolsControlMsg>,
              port: u16) {
    let listener = TcpListener::bind(&("127.0.0.1", port)).unwrap();

    let mut registry = ActorRegistry::new();

    let root = box RootActor {
        tabs: vec!(),
    };

    registry.register(root);
    registry.find::<RootActor>("root");

    let actors = registry.create_shareable();

    let mut accepted_connections: Vec<TcpStream> = Vec::new();

    let mut actor_pipelines: HashMap<PipelineId, String> = HashMap::new();
    let mut actor_requests: HashMap<String, String> = HashMap::new();

    let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new();

    /// Process the input from a single devtools client until EOF.
    fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) {
        println!("connection established to {}", stream.peer_addr().unwrap());
        {
            let actors = actors.lock().unwrap();
            let msg = actors.find::<RootActor>("root").encodable();
            stream.write_json_packet(&msg);
        }

        'outer: loop {
            match stream.read_json_packet() {
                Ok(Some(json_packet)) => {
                    match actors.lock().unwrap().handle_message(json_packet.as_object().unwrap(),
                                                                &mut stream) {
                        Ok(()) => {},
                        Err(()) => {
                            println!("error: devtools actor stopped responding");
                            let _ = stream.shutdown(Shutdown::Both);
                            break 'outer
                        }
                    }
                }
                Ok(None) => {
                    println!("error: EOF");
                    break 'outer
                }
                Err(err_msg) => {
                    println!("error: {}", err_msg);
                    break 'outer
                }
            }
        }
    }

    fn handle_framerate_tick(actors: Arc<Mutex<ActorRegistry>>, actor_name: String, tick: f64) {
        let actors = actors.lock().unwrap();
        let framerate_actor = actors.find::<FramerateActor>(&actor_name);
        framerate_actor.add_tick(tick);
    }

    // We need separate actor representations for each script global that exists;
    // clients can theoretically connect to multiple globals simultaneously.
    // TODO: move this into the root or tab modules?
    fn handle_new_global(actors: Arc<Mutex<ActorRegistry>>,
                         ids: (PipelineId, Option<WorkerId>),
                         script_sender: IpcSender<DevtoolScriptControlMsg>,
                         devtools_sender: Sender<DevtoolsControlMsg>,
                         actor_pipelines: &mut HashMap<PipelineId, String>,
                         actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
                         page_info: DevtoolsPageInfo) {
        let mut actors = actors.lock().unwrap();

        let (pipeline, worker_id) = ids;

        //TODO: move all this actor creation into a constructor method on TabActor
        let (tab, console, inspector, timeline) = {
            let console = ConsoleActor {
                name: actors.new_name("console"),
                script_chan: script_sender.clone(),
                pipeline: pipeline,
                streams: RefCell::new(Vec::new()),
            };
            let inspector = InspectorActor {
                name: actors.new_name("inspector"),
                walker: RefCell::new(None),
                pageStyle: RefCell::new(None),
                highlighter: RefCell::new(None),
                script_chan: script_sender.clone(),
                pipeline: pipeline,
            };

            let timeline = TimelineActor::new(actors.new_name("timeline"),
                                              pipeline,
                                              script_sender,
                                              devtools_sender);

            let DevtoolsPageInfo { title, url } = page_info;
            let tab = TabActor {
                name: actors.new_name("tab"),
                title: title,
                url: url.serialize(),
                console: console.name(),
                inspector: inspector.name(),
                timeline: timeline.name(),
            };

            let root = actors.find_mut::<RootActor>("root");
            root.tabs.push(tab.name.clone());
            (tab, console, inspector, timeline)
        };

        if let Some(id) = worker_id {
            let worker = WorkerActor {
                name: actors.new_name("worker"),
                console: console.name(),
                id: id,
            };
            actor_workers.insert((pipeline, id), worker.name.clone());
            actors.register(box worker);
        }

        actor_pipelines.insert(pipeline, tab.name.clone());
        actors.register(box tab);
        actors.register(box console);
        actors.register(box inspector);
        actors.register(box timeline);
    }

    fn handle_console_message(actors: Arc<Mutex<ActorRegistry>>,
                              id: PipelineId,
                              worker_id: Option<WorkerId>,
                              console_message: ConsoleMessage,
                              actor_pipelines: &HashMap<PipelineId, String>,
                              actor_workers: &HashMap<(PipelineId, WorkerId), String>) {
        let console_actor_name = match find_console_actor(actors.clone(), id, worker_id, actor_workers,
                                                          actor_pipelines) {
            Some(name) => name,
            None => return,
        };
        let actors = actors.lock().unwrap();
        let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
        let msg = ConsoleAPICall {
            from: console_actor.name.clone(),
            __type__: "consoleAPICall".to_string(),
            message: ConsoleMsg {
                level: match console_message.logLevel {
                    LogLevel::Debug => "debug",
                    LogLevel::Info => "info",
                    LogLevel::Warn => "warn",
                    LogLevel::Error => "error",
                    _ => "log"
                }.to_string(),
                timeStamp: precise_time_ns(),
                arguments: vec!(console_message.message),
                filename: console_message.filename,
                lineNumber: console_message.lineNumber,
                columnNumber: console_message.columnNumber,
            },
        };
        for stream in console_actor.streams.borrow_mut().iter_mut() {
            stream.write_json_packet(&msg);
        }
    }

    fn find_console_actor(actors: Arc<Mutex<ActorRegistry>>,
                          id: PipelineId,
                          worker_id: Option<WorkerId>,
                          actor_workers: &HashMap<(PipelineId, WorkerId), String>,
                          actor_pipelines: &HashMap<PipelineId, String>) -> Option<String> {
        let actors = actors.lock().unwrap();
        if let Some(worker_id) = worker_id {
            let actor_name = match (*actor_workers).get(&(id, worker_id)) {
                Some(name) => name,
                None => return None,
            };
            Some(actors.find::<WorkerActor>(actor_name).console.clone())
        } else {
            let actor_name = match (*actor_pipelines).get(&id) {
                Some(name) => name,
                None => return None,
            };
            Some(actors.find::<TabActor>(actor_name).console.clone())
        }
    }

    fn handle_network_event(actors: Arc<Mutex<ActorRegistry>>,
                            mut connections: Vec<TcpStream>,
                            actor_pipelines: &HashMap<PipelineId, String>,
                            actor_requests: &mut HashMap<String, String>,
                            actor_workers: &HashMap<(PipelineId, WorkerId), String>,
                            pipeline_id: PipelineId,
                            request_id: String,
                            network_event: NetworkEvent) {

        let console_actor_name = match find_console_actor(actors.clone(), pipeline_id, None,
                                                          actor_workers, actor_pipelines) {
            Some(name) => name,
            None => return,
        };
        let netevent_actor_name = find_network_event_actor(actors.clone(), actor_requests, request_id.clone());
        let mut actors = actors.lock().unwrap();
        let actor = actors.find_mut::<NetworkEventActor>(&netevent_actor_name);

        match network_event {
            NetworkEvent::HttpRequest(url, method, headers, body) => {
                //Store the request information in the actor
                actor.add_request(url, method, headers, body);

                //Send a networkEvent message to the client
                let msg = NetworkEventMsg {
                    from: console_actor_name,
                    __type__: "networkEvent".to_string(),
                    eventActor: actor.event_actor(),
                };
                for stream in connections.iter_mut() {
                    stream.write_json_packet(&msg);
                }
            }
            NetworkEvent::HttpResponse(headers, status, body) => {
                //Store the response information in the actor
                actor.add_response(headers, status, body);

                //Send a networkEventUpdate (responseStart) to the client
                let msg = NetworkEventUpdateMsg {
                    from: netevent_actor_name,
                    __type__: "networkEventUpdate".to_string(),
                    updateType: "responseStart".to_string(),
                    response: actor.response_start()
                };

                for stream in connections.iter_mut() {
                    stream.write_json_packet(&msg);
                }
            }
            //TODO: Send the other types of update messages at appropriate times
            //      requestHeaders, requestCookies, responseHeaders, securityInfo, etc
        }
    }

    // Find the name of NetworkEventActor corresponding to request_id
    // Create a new one if it does not exist, add it to the actor_requests hashmap
    fn find_network_event_actor(actors: Arc<Mutex<ActorRegistry>>,
                                actor_requests: &mut HashMap<String, String>,
                                request_id: String) -> String {
        let mut actors = actors.lock().unwrap();
        match (*actor_requests).entry(request_id) {
            Occupied(name) => {
                //TODO: Delete from map like Firefox does?
                name.into_mut().clone()
            }
            Vacant(entry) => {
                let actor_name = actors.new_name("netevent");
                let actor = NetworkEventActor::new(actor_name.clone());
                entry.insert(actor_name.clone());
                actors.register(box actor);
                actor_name
            }
        }
    }

    let sender_clone = sender.clone();
    spawn_named("DevtoolsClientAcceptor".to_owned(), move || {
        // accept connections and process them, spawning a new task for each one
        for stream in listener.incoming() {
            // connection succeeded
            sender_clone.send(DevtoolsControlMsg::FromChrome(
                    ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()))).unwrap();
        }
    });

    loop {
        match receiver.recv() {
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream))) => {
                let actors = actors.clone();
                accepted_connections.push(stream.try_clone().unwrap());
                spawn_named("DevtoolsClientHandler".to_owned(), move || {
                    handle_client(actors, stream.try_clone().unwrap())
                })
            }
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::FramerateTick(
                        actor_name, tick))) =>
                handle_framerate_tick(actors.clone(), actor_name, tick),
            Ok(DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
                        ids, script_sender, pageinfo))) =>
                handle_new_global(actors.clone(), ids, script_sender, sender.clone(), &mut actor_pipelines,
                                  &mut actor_workers, pageinfo),
            Ok(DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::SendConsoleMessage(
                        id,
                        console_message,
                        worker_id))) =>
                handle_console_message(actors.clone(), id, worker_id, console_message,
                                       &actor_pipelines, &actor_workers),
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEventMessage(
                        request_id, network_event))) => {
                // copy the accepted_connections vector
                let mut connections = Vec::<TcpStream>::new();
                for stream in accepted_connections.iter() {
                    connections.push(stream.try_clone().unwrap());
                }
                //TODO: Get pipeline_id from NetworkEventMessage after fixing the send in http_loader
                // For now, the id of the first pipeline is passed
                handle_network_event(actors.clone(), connections, &actor_pipelines, &mut actor_requests,
                                     &actor_workers, PipelineId(0), request_id, network_event);
            },
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg)) |
            Err(RecvError) => break
        }
    }
    for connection in accepted_connections.iter_mut() {
        let _ = connection.shutdown(Shutdown::Both);
    }
}
<commit_msg>Remove unused import.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! An actor-based remote devtools server implementation. Only tested with
//! nightly Firefox versions at time of writing. Largely based on
//! reverse-engineering of Firefox chrome devtool logs and reading of
//! [code](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/).

#![crate_name = "devtools"]
#![crate_type = "rlib"]

#![feature(box_syntax)]
#![feature(core)]
#![feature(custom_derive)]
#![feature(get_type_id)]
#![feature(plugin)]
#![feature(raw)]
#![feature(reflect_marker)]
#![plugin(serde_macros)]

#![allow(non_snake_case)]

#[macro_use]
extern crate log;

extern crate core;
extern crate devtools_traits;
extern crate rustc_serialize;
extern crate ipc_channel;
extern crate serde;
extern crate msg;
extern crate time;
extern crate util;
extern crate hyper;
extern crate url;

use actor::{Actor, ActorRegistry};
use actors::console::ConsoleActor;
use actors::network_event::{NetworkEventActor, EventActor, ResponseStartMsg};
use actors::framerate::FramerateActor;
use actors::inspector::InspectorActor;
use actors::root::RootActor;
use actors::tab::TabActor;
use actors::timeline::TimelineActor;
use actors::worker::WorkerActor;
use protocol::JsonPacketStream;

use devtools_traits::{ChromeToDevtoolsControlMsg, ConsoleMessage, DevtoolsControlMsg};
use devtools_traits::{DevtoolsPageInfo, DevtoolScriptControlMsg, LogLevel, NetworkEvent};
use devtools_traits::{ScriptToDevtoolsControlMsg};
use msg::constellation_msg::{PipelineId, WorkerId};
use util::task::spawn_named;

use ipc_channel::ipc::IpcSender;
use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::error::Error;
use std::sync::mpsc::{channel, Receiver, Sender, RecvError};
use std::net::{TcpListener, TcpStream, Shutdown};
use std::sync::{Arc, Mutex};
use time::precise_time_ns;

mod actor;
/// Corresponds to http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/
mod actors {
    pub mod console;
    pub mod framerate;
    pub mod inspector;
    pub mod memory;
    pub mod network_event;
    pub mod object;
    pub mod root;
    pub mod tab;
    pub mod timeline;
    pub mod worker;
}
mod protocol;

#[derive(RustcEncodable)]
struct ConsoleAPICall {
    from: String,
    __type__: String,
    message: ConsoleMsg,
}

#[derive(RustcEncodable)]
struct ConsoleMsg {
    level: String,
    timeStamp: u64,
    arguments: Vec<String>,
    filename: String,
    lineNumber: u32,
    columnNumber: u32,
}

#[derive(RustcEncodable)]
struct NetworkEventMsg {
    from: String,
    __type__: String,
    eventActor: EventActor,
}

#[derive(RustcEncodable)]
struct NetworkEventUpdateMsg {
    from: String,
    __type__: String,
    updateType: String,
    response: ResponseStartMsg,
}

/// Spin up a devtools server that listens for connections on the specified port.
pub fn start_server(port: u16) -> Sender<DevtoolsControlMsg> {
    let (sender, receiver) = channel();
    {
        let sender = sender.clone();
        spawn_named("Devtools".to_owned(), move || {
            run_server(sender, receiver, port)
        });
    }
    sender
}

fn run_server(sender: Sender<DevtoolsControlMsg>,
              receiver: Receiver<DevtoolsControlMsg>,
              port: u16) {
    let listener = TcpListener::bind(&("127.0.0.1", port)).unwrap();

    let mut registry = ActorRegistry::new();

    let root = box RootActor {
        tabs: vec!(),
    };

    registry.register(root);
    registry.find::<RootActor>("root");

    let actors = registry.create_shareable();

    let mut accepted_connections: Vec<TcpStream> = Vec::new();

    let mut actor_pipelines: HashMap<PipelineId, String> = HashMap::new();
    let mut actor_requests: HashMap<String, String> = HashMap::new();

    let mut actor_workers: HashMap<(PipelineId, WorkerId), String> = HashMap::new();

    /// Process the input from a single devtools client until EOF.
    fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream) {
        println!("connection established to {}", stream.peer_addr().unwrap());
        {
            let actors = actors.lock().unwrap();
            let msg = actors.find::<RootActor>("root").encodable();
            stream.write_json_packet(&msg);
        }

        'outer: loop {
            match stream.read_json_packet() {
                Ok(Some(json_packet)) => {
                    match actors.lock().unwrap().handle_message(json_packet.as_object().unwrap(),
                                                                &mut stream) {
                        Ok(()) => {},
                        Err(()) => {
                            println!("error: devtools actor stopped responding");
                            let _ = stream.shutdown(Shutdown::Both);
                            break 'outer
                        }
                    }
                }
                Ok(None) => {
                    println!("error: EOF");
                    break 'outer
                }
                Err(err_msg) => {
                    println!("error: {}", err_msg);
                    break 'outer
                }
            }
        }
    }

    fn handle_framerate_tick(actors: Arc<Mutex<ActorRegistry>>, actor_name: String, tick: f64) {
        let actors = actors.lock().unwrap();
        let framerate_actor = actors.find::<FramerateActor>(&actor_name);
        framerate_actor.add_tick(tick);
    }

    // We need separate actor representations for each script global that exists;
    // clients can theoretically connect to multiple globals simultaneously.
    // TODO: move this into the root or tab modules?
    fn handle_new_global(actors: Arc<Mutex<ActorRegistry>>,
                         ids: (PipelineId, Option<WorkerId>),
                         script_sender: IpcSender<DevtoolScriptControlMsg>,
                         devtools_sender: Sender<DevtoolsControlMsg>,
                         actor_pipelines: &mut HashMap<PipelineId, String>,
                         actor_workers: &mut HashMap<(PipelineId, WorkerId), String>,
                         page_info: DevtoolsPageInfo) {
        let mut actors = actors.lock().unwrap();

        let (pipeline, worker_id) = ids;

        //TODO: move all this actor creation into a constructor method on TabActor
        let (tab, console, inspector, timeline) = {
            let console = ConsoleActor {
                name: actors.new_name("console"),
                script_chan: script_sender.clone(),
                pipeline: pipeline,
                streams: RefCell::new(Vec::new()),
            };
            let inspector = InspectorActor {
                name: actors.new_name("inspector"),
                walker: RefCell::new(None),
                pageStyle: RefCell::new(None),
                highlighter: RefCell::new(None),
                script_chan: script_sender.clone(),
                pipeline: pipeline,
            };

            let timeline = TimelineActor::new(actors.new_name("timeline"),
                                              pipeline,
                                              script_sender,
                                              devtools_sender);

            let DevtoolsPageInfo { title, url } = page_info;
            let tab = TabActor {
                name: actors.new_name("tab"),
                title: title,
                url: url.serialize(),
                console: console.name(),
                inspector: inspector.name(),
                timeline: timeline.name(),
            };

            let root = actors.find_mut::<RootActor>("root");
            root.tabs.push(tab.name.clone());
            (tab, console, inspector, timeline)
        };

        if let Some(id) = worker_id {
            let worker = WorkerActor {
                name: actors.new_name("worker"),
                console: console.name(),
                id: id,
            };
            actor_workers.insert((pipeline, id), worker.name.clone());
            actors.register(box worker);
        }

        actor_pipelines.insert(pipeline, tab.name.clone());
        actors.register(box tab);
        actors.register(box console);
        actors.register(box inspector);
        actors.register(box timeline);
    }

    fn handle_console_message(actors: Arc<Mutex<ActorRegistry>>,
                              id: PipelineId,
                              worker_id: Option<WorkerId>,
                              console_message: ConsoleMessage,
                              actor_pipelines: &HashMap<PipelineId, String>,
                              actor_workers: &HashMap<(PipelineId, WorkerId), String>) {
        let console_actor_name = match find_console_actor(actors.clone(), id, worker_id, actor_workers,
                                                          actor_pipelines) {
            Some(name) => name,
            None => return,
        };
        let actors = actors.lock().unwrap();
        let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
        let msg = ConsoleAPICall {
            from: console_actor.name.clone(),
            __type__: "consoleAPICall".to_string(),
            message: ConsoleMsg {
                level: match console_message.logLevel {
                    LogLevel::Debug => "debug",
                    LogLevel::Info => "info",
                    LogLevel::Warn => "warn",
                    LogLevel::Error => "error",
                    _ => "log"
                }.to_string(),
                timeStamp: precise_time_ns(),
                arguments: vec!(console_message.message),
                filename: console_message.filename,
                lineNumber: console_message.lineNumber,
                columnNumber: console_message.columnNumber,
            },
        };
        for stream in console_actor.streams.borrow_mut().iter_mut() {
            stream.write_json_packet(&msg);
        }
    }

    fn find_console_actor(actors: Arc<Mutex<ActorRegistry>>,
                          id: PipelineId,
                          worker_id: Option<WorkerId>,
                          actor_workers: &HashMap<(PipelineId, WorkerId), String>,
                          actor_pipelines: &HashMap<PipelineId, String>) -> Option<String> {
        let actors = actors.lock().unwrap();
        if let Some(worker_id) = worker_id {
            let actor_name = match (*actor_workers).get(&(id, worker_id)) {
                Some(name) => name,
                None => return None,
            };
            Some(actors.find::<WorkerActor>(actor_name).console.clone())
        } else {
            let actor_name = match (*actor_pipelines).get(&id) {
                Some(name) => name,
                None => return None,
            };
            Some(actors.find::<TabActor>(actor_name).console.clone())
        }
    }

    fn handle_network_event(actors: Arc<Mutex<ActorRegistry>>,
                            mut connections: Vec<TcpStream>,
                            actor_pipelines: &HashMap<PipelineId, String>,
                            actor_requests: &mut HashMap<String, String>,
                            actor_workers: &HashMap<(PipelineId, WorkerId), String>,
                            pipeline_id: PipelineId,
                            request_id: String,
                            network_event: NetworkEvent) {

        let console_actor_name = match find_console_actor(actors.clone(), pipeline_id, None,
                                                          actor_workers, actor_pipelines) {
            Some(name) => name,
            None => return,
        };
        let netevent_actor_name = find_network_event_actor(actors.clone(), actor_requests, request_id.clone());
        let mut actors = actors.lock().unwrap();
        let actor = actors.find_mut::<NetworkEventActor>(&netevent_actor_name);

        match network_event {
            NetworkEvent::HttpRequest(url, method, headers, body) => {
                //Store the request information in the actor
                actor.add_request(url, method, headers, body);

                //Send a networkEvent message to the client
                let msg = NetworkEventMsg {
                    from: console_actor_name,
                    __type__: "networkEvent".to_string(),
                    eventActor: actor.event_actor(),
                };
                for stream in connections.iter_mut() {
                    stream.write_json_packet(&msg);
                }
            }
            NetworkEvent::HttpResponse(headers, status, body) => {
                //Store the response information in the actor
                actor.add_response(headers, status, body);

                //Send a networkEventUpdate (responseStart) to the client
                let msg = NetworkEventUpdateMsg {
                    from: netevent_actor_name,
                    __type__: "networkEventUpdate".to_string(),
                    updateType: "responseStart".to_string(),
                    response: actor.response_start()
                };

                for stream in connections.iter_mut() {
                    stream.write_json_packet(&msg);
                }
            }
            //TODO: Send the other types of update messages at appropriate times
            //      requestHeaders, requestCookies, responseHeaders, securityInfo, etc
        }
    }

    // Find the name of NetworkEventActor corresponding to request_id
    // Create a new one if it does not exist, add it to the actor_requests hashmap
    fn find_network_event_actor(actors: Arc<Mutex<ActorRegistry>>,
                                actor_requests: &mut HashMap<String, String>,
                                request_id: String) -> String {
        let mut actors = actors.lock().unwrap();
        match (*actor_requests).entry(request_id) {
            Occupied(name) => {
                //TODO: Delete from map like Firefox does?
                name.into_mut().clone()
            }
            Vacant(entry) => {
                let actor_name = actors.new_name("netevent");
                let actor = NetworkEventActor::new(actor_name.clone());
                entry.insert(actor_name.clone());
                actors.register(box actor);
                actor_name
            }
        }
    }

    let sender_clone = sender.clone();
    spawn_named("DevtoolsClientAcceptor".to_owned(), move || {
        // accept connections and process them, spawning a new task for each one
        for stream in listener.incoming() {
            // connection succeeded
            sender_clone.send(DevtoolsControlMsg::FromChrome(
                    ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()))).unwrap();
        }
    });

    loop {
        match receiver.recv() {
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream))) => {
                let actors = actors.clone();
                accepted_connections.push(stream.try_clone().unwrap());
                spawn_named("DevtoolsClientHandler".to_owned(), move || {
                    handle_client(actors, stream.try_clone().unwrap())
                })
            }
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::FramerateTick(
                        actor_name, tick))) =>
                handle_framerate_tick(actors.clone(), actor_name, tick),
            Ok(DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
                        ids, script_sender, pageinfo))) =>
                handle_new_global(actors.clone(), ids, script_sender, sender.clone(), &mut actor_pipelines,
                                  &mut actor_workers, pageinfo),
            Ok(DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::SendConsoleMessage(
                        id,
                        console_message,
                        worker_id))) =>
                handle_console_message(actors.clone(), id, worker_id, console_message,
                                       &actor_pipelines, &actor_workers),
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEventMessage(
                        request_id, network_event))) => {
                // copy the accepted_connections vector
                let mut connections = Vec::<TcpStream>::new();
                for stream in accepted_connections.iter() {
                    connections.push(stream.try_clone().unwrap());
                }
                //TODO: Get pipeline_id from NetworkEventMessage after fixing the send in http_loader
                // For now, the id of the first pipeline is passed
                handle_network_event(actors.clone(), connections, &actor_pipelines, &mut actor_requests,
                                     &actor_workers, PipelineId(0), request_id, network_event);
            },
            Ok(DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg)) |
            Err(RecvError) => break
        }
    }
    for connection in accepted_connections.iter_mut() {
        let _ = connection.shutdown(Shutdown::Both);
    }
}
<|endoftext|>


<commit_before>/// Official capitalized logging (and logging filtering) level names
///
/// In order of `as_usize()`.
pub static LOG_LEVEL_NAMES: [&'static str; 7] = ["OFF", "CRITICAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];

/// Official capitalized logging (and logging filtering) short level names
///
/// In order of `as_usize()`.
pub static LOG_LEVEL_SHORT_NAMES: [&'static str; 7] = ["OFF", "CRIT", "ERRO", "WARN", "INFO", "DEBG", "TRCE"];

/// Log record level
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Level {
    /// Critical
    Critical,
    /// Error
    Error,
    /// Warning
    Warning,
    /// Info
    Info,
    /// Debug
    Debug,
    /// Trace
    Trace
}

/// Logging filtering level
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum FilterLevel {
    /// Log nothing
    Off,
    /// Log critical level only
    Critical,
    /// Log only error level and above
    Error,
    /// Log only warning level and above
    Warning,
    /// Log only info level and above
    Info,
    /// Log only debug level and above
    Debug,
    /// Log everything
    Trace,
}

impl Level {
    /// Convert to `str` from `LOG_LEVEL_SHORT_NAMES`
    pub fn as_short_str(&self) -> &'static str {
        LOG_LEVEL_SHORT_NAMES[self.as_usize()]
    }

    /// Convert to `str` from `LOG_LEVEL_NAMES`
    pub fn as_str(&self) -> &'static str {
        LOG_LEVEL_NAMES[self.as_usize()]
    }

    /// Cast `Level` to ordering integer
    ///
    /// `Critical` is the smallest and `Trace` the biggest value
    pub fn as_usize(&self) -> usize {
        match *self {
            Level::Critical => 1,
            Level::Error => 2,
            Level::Warning => 3,
            Level::Info => 4,
            Level::Debug => 5,
            Level::Trace => 6,
        }
    }

    /// Get a `Level` from an `usize`
    ///
    /// This complements `as_usize`
    pub fn from_usize(u: usize) -> Option<Level> {
        match u {
            1 => Some(Level::Critical),
            2 => Some(Level::Error),
            3 => Some(Level::Warning),
            4 => Some(Level::Info),
            5 => Some(Level::Debug),
            6 => Some(Level::Trace),
            _ => None
        }
    }
}

impl FilterLevel {
    /// Convert to `usize` value
    ///
    /// `Off` is 0, and `Trace` 6
    pub fn as_usize(&self) -> usize {
        match *self {
            FilterLevel::Off => 0,
            FilterLevel::Critical => 1,
            FilterLevel::Error => 2,
            FilterLevel::Warning => 3,
            FilterLevel::Info => 4,
            FilterLevel::Debug => 5,
            FilterLevel::Trace => 6,
        }
    }

    /// Get a `FilterLevel` from an `usize`
    ///
    /// This complements `as_usize`
    pub fn from_usize(u: usize) -> Option<FilterLevel> {
        match u {
            0 => Some(FilterLevel::Off),
            1 => Some(FilterLevel::Critical),
            2 => Some(FilterLevel::Error),
            3 => Some(FilterLevel::Warning),
            4 => Some(FilterLevel::Info),
            5 => Some(FilterLevel::Debug),
            6 => Some(FilterLevel::Trace),
            _ => None
        }
    }

    /// Maximum logging level (log everything)
    pub fn max() -> Self {
        FilterLevel::Trace
    }

    /// Minimum logging level (log nothing)
    pub fn min() -> Self {
        FilterLevel::Off
    }
}

impl FromStr for FilterLevel {
    type Err = ();
    fn from_str(level: &str) -> std::result::Result<FilterLevel, ()> {
        LOG_LEVEL_NAMES.iter()
            .position(|&name| name.eq_ignore_ascii_case(level))
            .map(|p| FilterLevel::from_usize(p).unwrap()).ok_or(())
    }
}

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

impl Level {
    /// Returns true if `self` is at least `level` logging level
    pub fn is_at_least(&self, level : Self) -> bool {
        self.as_usize() <= level.as_usize()
    }
}

#[test]
fn level_at_least() {
    assert!(Level::Debug.is_at_least(Level::Debug));
    assert!(Level::Debug.is_at_least(Level::Trace));
    assert!(!Level::Debug.is_at_least(Level::Info));
}

#[test]
fn filterlevel_sanity() {
    assert!(Level::Critical.as_usize() > FilterLevel::Off.as_usize());
    assert!(Level::Critical.as_usize() <= FilterLevel::Critical.as_usize());
    assert!(Level::Trace.as_usize() <= FilterLevel::Trace.as_usize());
}
<commit_msg>Add some `#[inline]` to help benchmark<commit_after>/// Official capitalized logging (and logging filtering) level names
///
/// In order of `as_usize()`.
pub static LOG_LEVEL_NAMES: [&'static str; 7] = ["OFF", "CRITICAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];

/// Official capitalized logging (and logging filtering) short level names
///
/// In order of `as_usize()`.
pub static LOG_LEVEL_SHORT_NAMES: [&'static str; 7] = ["OFF", "CRIT", "ERRO", "WARN", "INFO", "DEBG", "TRCE"];

/// Log record level
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Level {
    /// Critical
    Critical,
    /// Error
    Error,
    /// Warning
    Warning,
    /// Info
    Info,
    /// Debug
    Debug,
    /// Trace
    Trace
}

/// Logging filtering level
#[repr(usize)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum FilterLevel {
    /// Log nothing
    Off,
    /// Log critical level only
    Critical,
    /// Log only error level and above
    Error,
    /// Log only warning level and above
    Warning,
    /// Log only info level and above
    Info,
    /// Log only debug level and above
    Debug,
    /// Log everything
    Trace,
}

impl Level {
    /// Convert to `str` from `LOG_LEVEL_SHORT_NAMES`
    pub fn as_short_str(&self) -> &'static str {
        LOG_LEVEL_SHORT_NAMES[self.as_usize()]
    }

    /// Convert to `str` from `LOG_LEVEL_NAMES`
    pub fn as_str(&self) -> &'static str {
        LOG_LEVEL_NAMES[self.as_usize()]
    }

    /// Cast `Level` to ordering integer
    ///
    /// `Critical` is the smallest and `Trace` the biggest value
    #[inline]
    pub fn as_usize(&self) -> usize {
        match *self {
            Level::Critical => 1,
            Level::Error => 2,
            Level::Warning => 3,
            Level::Info => 4,
            Level::Debug => 5,
            Level::Trace => 6,
        }
    }

    /// Get a `Level` from an `usize`
    ///
    /// This complements `as_usize`
    #[inline]
    pub fn from_usize(u: usize) -> Option<Level> {
        match u {
            1 => Some(Level::Critical),
            2 => Some(Level::Error),
            3 => Some(Level::Warning),
            4 => Some(Level::Info),
            5 => Some(Level::Debug),
            6 => Some(Level::Trace),
            _ => None
        }
    }
}

impl FilterLevel {
    /// Convert to `usize` value
    ///
    /// `Off` is 0, and `Trace` 6
    #[inline]
    pub fn as_usize(&self) -> usize {
        match *self {
            FilterLevel::Off => 0,
            FilterLevel::Critical => 1,
            FilterLevel::Error => 2,
            FilterLevel::Warning => 3,
            FilterLevel::Info => 4,
            FilterLevel::Debug => 5,
            FilterLevel::Trace => 6,
        }
    }

    /// Get a `FilterLevel` from an `usize`
    ///
    /// This complements `as_usize`
    #[inline]
    pub fn from_usize(u: usize) -> Option<FilterLevel> {
        match u {
            0 => Some(FilterLevel::Off),
            1 => Some(FilterLevel::Critical),
            2 => Some(FilterLevel::Error),
            3 => Some(FilterLevel::Warning),
            4 => Some(FilterLevel::Info),
            5 => Some(FilterLevel::Debug),
            6 => Some(FilterLevel::Trace),
            _ => None
        }
    }

    /// Maximum logging level (log everything)
    #[inline]
    pub fn max() -> Self {
        FilterLevel::Trace
    }

    /// Minimum logging level (log nothing)
    #[inline]
    pub fn min() -> Self {
        FilterLevel::Off
    }
}

impl FromStr for FilterLevel {
    type Err = ();
    fn from_str(level: &str) -> std::result::Result<FilterLevel, ()> {
        LOG_LEVEL_NAMES.iter()
            .position(|&name| name.eq_ignore_ascii_case(level))
            .map(|p| FilterLevel::from_usize(p).unwrap()).ok_or(())
    }
}

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

impl Level {
    /// Returns true if `self` is at least `level` logging level
    #[inline]
    pub fn is_at_least(&self, level : Self) -> bool {
        self.as_usize() <= level.as_usize()
    }
}

#[test]
fn level_at_least() {
    assert!(Level::Debug.is_at_least(Level::Debug));
    assert!(Level::Debug.is_at_least(Level::Trace));
    assert!(!Level::Debug.is_at_least(Level::Info));
}

#[test]
fn filterlevel_sanity() {
    assert!(Level::Critical.as_usize() > FilterLevel::Off.as_usize());
    assert!(Level::Critical.as_usize() <= FilterLevel::Critical.as_usize());
    assert!(Level::Trace.as_usize() <= FilterLevel::Trace.as_usize());
}
<|endoftext|>


<commit_before>
use graphics::ImageSize;
use graphics::vecmath::Scalar;

use event::{
    Status,
    Success,
    Running,
};

use Sprite;

/// Actions supported by Sprite
#[deriving(Clone)]
pub enum Action {
    /// duration, x, y
    ///
    /// Move sprite to specified position
    MoveTo(f64, Scalar, Scalar),
    /// duration, x, y
    ///
    /// Move sprite to specified position, relatively
    MoveBy(f64, Scalar, Scalar),
    /// duration, deg
    ///
    /// Rotate sprite to specified degree
    RotateTo(f64, Scalar),
    /// duration, deg
    ///
    /// Rotate sprite to specified degree, relatively
    RotateBy(f64, Scalar),
    /// duration, sx, sy
    ///
    /// Scale sprite to specified scale
    ScaleTo(f64, Scalar, Scalar),
    /// duration, sx, sy
    ///
    /// Scale sprite to specified scale, relatively
    ScaleBy(f64, Scalar, Scalar),
    /// Flip sprite in x direction
    FlipX(bool),
    /// Flip sprite in y direction
    FlipY(bool),
    /// Set the sprite's visibility to true
    Show,
    /// Set the sprite's visibility to false
    Hide,
    /// Toggle the sprite's visibility
    ToggleVisibility,
    /// duration, times
    Blink(f64, uint),
    /// duration
    ///
    /// Fade in the sprite, set its opacity from 0 to 1 in `dt` seconds
    FadeIn(f64),
    /// duration
    ///
    /// Fade out the sprite, set its opacity from 1 to 0 in `dt` seconds
    FadeOut(f64),
    /// duration, opacity
    ///
    /// Set the sprite's opacity to specified value in `dt` seconds
    FadeTo(f64, f64),
    /// Tweening the action with Quadratic Easing
    EaseIn(Box<Action>),
    /// Tweening the action with Quadratic Easing
    EaseOut(Box<Action>),
    /// Tweening the action with Quadratic Easing
    EaseInOut(Box<Action>),
    /// A empty action
    EmptyAction,
}

impl Action {
    /// Generate a new state from Action with specified Sprite
    pub fn to_state<I: ImageSize>(&self, sprite: &Sprite<I>) -> ActionState {
        match *self {
            MoveTo(dur, dx, dy) => {
                let (bx, by) = sprite.position();
                MoveState(0.0, bx, by, dx - bx, dy - by, dur)
            },
            MoveBy(dur, cx, cy) => {
                let (bx, by) = sprite.position();
                MoveState(0.0, bx, by, cx, cy, dur)
            },
            RotateTo(dur, d) => {
                let b = sprite.rotation();
                RotateState(0.0, b, d - b, dur)
            },
            RotateBy(dur, c) => {
                let b = sprite.rotation();
                RotateState(0.0, b, c, dur)
            },
            ScaleTo(dur, dx, dy) => {
                let (bx, by) = sprite.scale();
                ScaleState(0.0, bx, by, dx - bx, dy - by, dur)
            },
            ScaleBy(dur, cx, cy) => {
                let (bx, by) = sprite.scale();
                ScaleState(0.0, bx, by, cx, cy, dur)
            },
            FlipX(flip_x) => {
                let flip_y = sprite.flip_y();
                FlipState(flip_x, flip_y)
            },
            FlipY(flip_y) => {
                let flip_x = sprite.flip_x();
                FlipState(flip_x, flip_y)
            },
            Show => {
                VisibilityState(true)
            },
            Hide => {
                VisibilityState(false)
            },
            ToggleVisibility => {
                let visible = sprite.visible();
                VisibilityState(!visible)
            },
            Blink(dur, times) => {
                BlinkState(0.0, dur, 0, 2 * times)
            },
            FadeIn(dur) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, 1.0 - b, dur)
            },
            FadeOut(dur) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, 0.0 - b, dur)
            },
            FadeTo(dur, d) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, d - b, dur)
            },
            _ => {
                EmptyState
            },
        }
    }
}

/// The state of action
#[deriving(Clone)]
pub enum ActionState {
    /// time, begin_x, begin_y, change_x, change_y, duration
    MoveState(f64, Scalar, Scalar, Scalar, Scalar, f64),
    /// time, begin, change, duration
    RotateState(f64, Scalar, Scalar, f64),
    /// time, begin_x, begin_y, change_x, change_y, duration
    ScaleState(f64, Scalar, Scalar, Scalar, Scalar, f64),
    /// flip_x, flip_y
    FlipState(bool, bool),
    /// visible
    VisibilityState(bool),
    /// past_time, duration, blinked_times, total_times
    BlinkState(f64, f64, uint, uint),
    /// time, begin, change, duration
    FadeState(f64, f64, f64, f64),
    /// in, out, state
    EaseState(bool, bool, Box<ActionState>),
    /// An empty state
    EmptyState,
}

impl ActionState {
    /// Update the state and change the sprite's properties
    pub fn update<I: ImageSize>(&self, sprite: &mut Sprite<I>, dt: f64) -> (ActionState, Status, f64) {
        match *self {
            MoveState(t, bx, by, cx, cy, d) => {
                if t + dt >= d {
                    sprite.set_position(bx + cx, by + cy);
                    (EmptyState, Success, t + dt - d)
                } else {
                    let factor = (t + dt) / d;
                    sprite.set_position(bx + cx * factor, by + cy * factor);
                    (MoveState(t + dt, bx, by, cx, cy, d),
                     Running, 0.0)
                }
            },
            RotateState(t, b, c, d) => {
                if t + dt >= d {
                    sprite.set_rotation(b + c);
                    (EmptyState, Success, t + dt - d)
                } else {
                    let factor = (t + dt) / d;
                    sprite.set_rotation(b + c * factor);
                    (RotateState(t + dt, b, c, d),
                     Running, 0.0)
                }
            },
            ScaleState(t, bx, by, cx, cy, d) => {
                if t + dt >= d {
                    sprite.set_scale(bx + cx, by + cy);
                    (EmptyState, Success, t + dt - d)
                } else {
                    let factor = (t + dt) / d;
                    sprite.set_scale(bx + cx * factor, by + cy * factor);
                    (ScaleState(t + dt, bx, by, cx, cy, d),
                     Running, 0.0)
                }
            },
            FlipState(flip_x, flip_y) => {
                sprite.set_flip_x(flip_x);
                sprite.set_flip_y(flip_y);
                (EmptyState, Success, dt)
            },
            VisibilityState(visible) => {
                sprite.set_visible(visible);
                (EmptyState, Success, dt)
            },
            BlinkState(past, dur, cur, total) => {
                let period = dur / total as f64;
                if past + dt >= (cur + 1) as f64 * period {
                    let visible = sprite.visible();
                    sprite.set_visible(!visible);
                    if past + dt >= dur {
                        (EmptyState, Success, past + dt - dur)
                    } else {
                        (BlinkState(past + dt, dur, cur + 1, total),
                         Running, 0.0)
                    }
                } else {
                    (BlinkState(past + dt, dur, cur, total),
                     Running, 0.0)
                }
            },
            FadeState(t, b, c, d) => {
                if t + dt >= d {
                    sprite.set_opacity((b + c) as f32);
                    (EmptyState, Success, t + dt - d)
                } else {
                    let factor = (t + dt) / d;
                    sprite.set_opacity((b + c * factor) as f32);
                    (FadeState(t + dt, b, c, d),
                     Running, 0.0)
                }
            },
            _ => { (EmptyState, Success, dt) },
        }
    }
}

<commit_msg>Added `Ease(Rate)In`, `Ease(Rate)Out`, `Ease(Rate)InOut` actions<commit_after>
use std::num::pow;

use graphics::ImageSize;
use graphics::vecmath::Scalar;

use event::{
    Status,
    Success,
    Running,
};

use Sprite;

/// Actions supported by Sprite
#[deriving(Clone)]
pub enum Action {
    /// duration, x, y
    ///
    /// Move sprite to specified position
    MoveTo(f64, Scalar, Scalar),
    /// duration, x, y
    ///
    /// Move sprite to specified position, relatively
    MoveBy(f64, Scalar, Scalar),
    /// duration, deg
    ///
    /// Rotate sprite to specified degree
    RotateTo(f64, Scalar),
    /// duration, deg
    ///
    /// Rotate sprite to specified degree, relatively
    RotateBy(f64, Scalar),
    /// duration, sx, sy
    ///
    /// Scale sprite to specified scale
    ScaleTo(f64, Scalar, Scalar),
    /// duration, sx, sy
    ///
    /// Scale sprite to specified scale, relatively
    ScaleBy(f64, Scalar, Scalar),
    /// Flip sprite in x direction
    FlipX(bool),
    /// Flip sprite in y direction
    FlipY(bool),
    /// Set the sprite's visibility to true
    Show,
    /// Set the sprite's visibility to false
    Hide,
    /// Toggle the sprite's visibility
    ToggleVisibility,
    /// duration, times
    Blink(f64, uint),
    /// duration
    ///
    /// Fade in the sprite, set its opacity from 0 to 1 in `dt` seconds
    FadeIn(f64),
    /// duration
    ///
    /// Fade out the sprite, set its opacity from 1 to 0 in `dt` seconds
    FadeOut(f64),
    /// duration, opacity
    ///
    /// Set the sprite's opacity to specified value in `dt` seconds
    FadeTo(f64, f64),
    /// action
    ///
    /// Tweening the action with Quadratic Easing
    EaseIn(Box<Action>),
    /// action
    ///
    /// Tweening the action with Quadratic Easing
    EaseOut(Box<Action>),
    /// action
    ///
    /// Tweening the action with Quadratic Easing
    EaseInOut(Box<Action>),
    /// rate, action
    ///
    /// Tweening the action with specified rate
    EaseRateIn(uint, Box<Action>),
    /// rate, action
    ///
    /// Tweening the action with specified rate
    EaseRateOut(uint, Box<Action>),
    /// rate, action
    ///
    /// Tweening the action with specified rate
    EaseRateInOut(uint, Box<Action>),
    /// A empty action
    EmptyAction,
}

impl Action {
    /// Generate a new state from Action with specified Sprite
    pub fn to_state<I: ImageSize>(&self, sprite: &Sprite<I>) -> ActionState {
        match *self {
            MoveTo(dur, dx, dy) => {
                let (bx, by) = sprite.position();
                MoveState(0.0, bx, by, dx - bx, dy - by, dur)
            },
            MoveBy(dur, cx, cy) => {
                let (bx, by) = sprite.position();
                MoveState(0.0, bx, by, cx, cy, dur)
            },
            RotateTo(dur, d) => {
                let b = sprite.rotation();
                RotateState(0.0, b, d - b, dur)
            },
            RotateBy(dur, c) => {
                let b = sprite.rotation();
                RotateState(0.0, b, c, dur)
            },
            ScaleTo(dur, dx, dy) => {
                let (bx, by) = sprite.scale();
                ScaleState(0.0, bx, by, dx - bx, dy - by, dur)
            },
            ScaleBy(dur, cx, cy) => {
                let (bx, by) = sprite.scale();
                ScaleState(0.0, bx, by, cx, cy, dur)
            },
            FlipX(flip_x) => {
                let flip_y = sprite.flip_y();
                FlipState(flip_x, flip_y)
            },
            FlipY(flip_y) => {
                let flip_x = sprite.flip_x();
                FlipState(flip_x, flip_y)
            },
            Show => {
                VisibilityState(true)
            },
            Hide => {
                VisibilityState(false)
            },
            ToggleVisibility => {
                let visible = sprite.visible();
                VisibilityState(!visible)
            },
            Blink(dur, times) => {
                BlinkState(0.0, dur, 0, 2 * times)
            },
            FadeIn(dur) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, 1.0 - b, dur)
            },
            FadeOut(dur) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, 0.0 - b, dur)
            },
            FadeTo(dur, d) => {
                let b = sprite.opacity() as f64;
                FadeState(0.0, b, d - b, dur)
            },
            EaseIn(ref action) => {
                EaseRateState(true, false, 2, box action.to_state(sprite))
            },
            EaseOut(ref action) => {
                EaseRateState(false, true, 2, box action.to_state(sprite))
            },
            EaseInOut(ref action) => {
                EaseRateState(true, true, 2, box action.to_state(sprite))
            },
            EaseRateIn(rate, ref action) => {
                EaseRateState(true, false, rate, box action.to_state(sprite))
            },
            EaseRateOut(rate, ref action) => {
                EaseRateState(false, true, rate, box action.to_state(sprite))
            },
            EaseRateInOut(rate, ref action) => {
                EaseRateState(true, true, rate, box action.to_state(sprite))
            },
            _ => {
                EmptyState
            },
        }
    }
}

/// The state of action
#[deriving(Clone)]
pub enum ActionState {
    /// time, begin_x, begin_y, change_x, change_y, duration
    MoveState(f64, Scalar, Scalar, Scalar, Scalar, f64),
    /// time, begin, change, duration
    RotateState(f64, Scalar, Scalar, f64),
    /// time, begin_x, begin_y, change_x, change_y, duration
    ScaleState(f64, Scalar, Scalar, Scalar, Scalar, f64),
    /// flip_x, flip_y
    FlipState(bool, bool),
    /// visible
    VisibilityState(bool),
    /// past_time, duration, blinked_times, total_times
    BlinkState(f64, f64, uint, uint),
    /// time, begin, change, duration
    FadeState(f64, f64, f64, f64),
    /// in, out, rate, state
    EaseRateState(bool, bool, uint, Box<ActionState>),
    /// An empty state
    EmptyState,
}

impl ActionState {
    /// Update the state and change the sprite's properties
    pub fn update<I: ImageSize>(&self, sprite: &mut Sprite<I>, dt: f64) -> (ActionState, Status, f64) {
        match *self {
            MoveState(t, bx, by, cx, cy, d) => {
                let factor = (t + dt) / d;
                update_position(sprite, factor, t + dt, bx, by, cx, cy, d)
            },
            RotateState(t, b, c, d) => {
                let factor = (t + dt) / d;
                update_rotation(sprite, factor, t + dt, b, c, d)
            },
            ScaleState(t, bx, by, cx, cy, d) => {
                let factor = (t + dt) / d;
                update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
            },
            FlipState(flip_x, flip_y) => {
                sprite.set_flip_x(flip_x);
                sprite.set_flip_y(flip_y);
                (EmptyState, Success, dt)
            },
            VisibilityState(visible) => {
                sprite.set_visible(visible);
                (EmptyState, Success, dt)
            },
            BlinkState(past, dur, cur, total) => {
                let period = dur / total as f64;
                if past + dt >= (cur + 1) as f64 * period {
                    let visible = sprite.visible();
                    sprite.set_visible(!visible);
                    if past + dt >= dur {
                        (EmptyState, Success, past + dt - dur)
                    } else {
                        (BlinkState(past + dt, dur, cur + 1, total),
                         Running, 0.0)
                    }
                } else {
                    (BlinkState(past + dt, dur, cur, total),
                     Running, 0.0)
                }
            },
            FadeState(t, b, c, d) => {
                let factor = (t + dt) / d;
                update_opacity(sprite, factor, t + dt, b, c, d)
            },
            EaseRateState(ease_in, ease_out, rate, ref state) => {
                let mut support_ease = true;
                let (state, status, remain) = match *state {
                    box MoveState(t, bx, by, cx, cy, d) => {
                        let factor = calc_factor_for_ease_rate(ease_in, ease_out, rate, t, d);
                        update_position(sprite, factor, t + dt, bx, by, cx, cy, d)
                    },
                    box RotateState(t, b, c, d) => {
                        let factor = calc_factor_for_ease_rate(ease_in, ease_out, rate, t, d);
                        update_rotation(sprite, factor, t + dt, b, c, d)
                    },
                    box ScaleState(t, bx, by, cx, cy, d) => {
                        let factor = calc_factor_for_ease_rate(ease_in, ease_out, rate, t, d);
                        update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
                    },
                    box FadeState(t, b, c, d) => {
                        let factor = calc_factor_for_ease_rate(ease_in, ease_out, rate, t, d);
                        update_opacity(sprite, factor, t + dt, b, c, d)
                    },
                    _ => {
                        support_ease = false;
                        state.update(sprite, dt)
                    }
                };

                if !support_ease {
                    return (state, status, remain);
                }

                match state {
                    EmptyState => {
                        (EmptyState, status, remain)
                    },
                    _ => {
                        (EaseRateState(ease_in, ease_out, rate, box state),
                         status, remain)
                    },
                }
            },
            _ => { (EmptyState, Success, dt) },
        }
    }
}

fn update_position<I: ImageSize>(sprite: &mut Sprite<I>, factor: f64, t: f64, bx: f64, by: f64, cx: f64, cy: f64, d: f64) -> (ActionState, Status, f64) {
    if t >= d {
        sprite.set_position(bx + cx, by + cy);
        (EmptyState, Success, t - d)
    } else {
        sprite.set_position(bx + cx * factor, by + cy * factor);
        (MoveState(t, bx, by, cx, cy, d),
         Running, 0.0)
    }
}

fn update_rotation<I: ImageSize>(sprite: &mut Sprite<I>, factor: f64, t: f64, b: f64, c: f64, d: f64) -> (ActionState, Status, f64) {
    if t >= d {
        sprite.set_rotation(b + c);
        (EmptyState, Success, t - d)
    } else {
        sprite.set_rotation(b + c * factor);
        (RotateState(t, b, c, d),
         Running, 0.0)
    }
}

fn update_scale<I: ImageSize>(sprite: &mut Sprite<I>, factor: f64, t: f64, bx: f64, by: f64, cx: f64, cy: f64, d: f64) -> (ActionState, Status, f64) {
    if t >= d {
        sprite.set_scale(bx + cx, by + cy);
        (EmptyState, Success, t - d)
    } else {
        sprite.set_scale(bx + cx * factor, by + cy * factor);
        (ScaleState(t, bx, by, cx, cy, d),
         Running, 0.0)
    }
}

fn update_opacity<I: ImageSize>(sprite: &mut Sprite<I>, factor: f64, t: f64, b: f64, c: f64, d: f64) -> (ActionState, Status, f64) {
    if t >= d {
        sprite.set_opacity((b + c) as f32);
        (EmptyState, Success, t - d)
    } else {
        sprite.set_opacity((b + c * factor) as f32);
        (FadeState(t, b, c, d),
         Running, 0.0)
    }
}

fn calc_factor_for_ease_rate(ease_in: bool, ease_out: bool, rate: uint, t: f64, d: f64) -> f64 {
    if ease_in && !ease_out{
        // ease in
        pow(t / d, rate)
    } else if !ease_in && ease_out {
        // ease out
        1.0 - pow(1.0 - t / d, rate)
    } else {
        // ease in out
        if t / d <= 0.5 {
            calc_factor_for_ease_rate(true, false, rate, t, d / 2.0) / 2.0
        } else {
            0.5 + calc_factor_for_ease_rate(false, true, rate, t - d / 2.0, d / 2.0) / 2.0
        }
    }
}

<|endoftext|>