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

use std::convert::TryFrom;

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

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

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

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

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

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

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

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

        // Raw pointers are not allowed in type level constants, as raw pointers cannot be treated
        // like references. If we looked behind the raw pointer, we may be breaking the meaning of
        // the raw pointer. Equality on raw pointers is performed on the pointer and not on the pointee,
        // and we cannot guarantee any kind of pointer stability in the type system.
        // Technically we could allow function pointers, but they are not guaranteed to be the
        // same as the function pointers at runtime.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

        // Trait objects are not allowed in type level constants, as we have no concept for
        // resolving their backing type, even if we can do that at const eval time. We may want to consider
        // adding a `ValTree::DownCast(Ty<'tcx>, Box<ValTree>)` in the future, but I don't even know the
        // questions such a concept would open up, so an RFC would probably be good for this.
        ty::Dynamic(..) => None,

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

        ty::Adt(def, _) => {
            if def.variants.is_empty() {
                // Uninhabited
                return None;
            }

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

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

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

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

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

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

    mir::DestructuredConst { variant, fields }
}

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

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

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
<commit_msg>Explain pointer and dyn Trait handling in const_to_valtree<commit_after>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

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

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

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

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

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

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

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

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

        // Raw pointers are not allowed in type level constants, as raw pointers compare equal if
        // their addresses are equal. Since we cannot guarantee any kind of pointer stability in
        // the type system.
        // Technically we could allow function pointers, but they are not guaranteed to be the
        // same as the function pointers at runtime.
        ty::FnPtr(_) | ty::RawPtr(_) => None,
        ty::Ref(..) => unimplemented!("need to use deref_const"),

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

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

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

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

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

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

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

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

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

    mir::DestructuredConst { variant, fields }
}

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

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

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


<commit_before>extern crate rmp_serialize;
extern crate rustc_serialize;

use rustc_serialize::Encodable;

use rmp_serialize::encode::Encoder;

#[test]
fn pass_null() {
    let mut buf = [0x00];

    let val = ();
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xc0], buf);
}

#[test]
fn fail_null() {
    let mut buf = [];

    let val = ();

    match val.encode(&mut Encoder::new(&mut &mut buf[..])) {
        Err(..) => (),
        other => panic!("unexpected result: {:?}", other)
    }
}

#[test]
fn pass_bool() {
    let mut buf = Vec::new();
    {
        let mut encoder = Encoder::new(&mut buf);
        true.encode(&mut encoder).unwrap();
        false.encode(&mut encoder).unwrap();
    }

    assert_eq!(vec![0xc3, 0xc2], buf);
}

#[test]
fn pass_usize() {
    let mut buf = [0x00, 0x00];

    let val = 255usize;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcc, 0xff], buf);
}

#[test]
fn pass_u8() {
    let mut buf = [0x00, 0x00];

    let val = 255u8;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcc, 0xff], buf);
}

#[test]
fn pass_u16() {
    let mut buf = [0x00, 0x00, 0x00];

    let val = 65535u16;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcd, 0xff, 0xff], buf);
}

#[test]
fn pass_u32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 4294967295u32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xce, 0xff, 0xff, 0xff, 0xff], buf);
}

#[test]
fn pass_u64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 18446744073709551615u64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], buf);
}

#[test]
fn pass_isize() {
    let mut buf = [0x00, 0x00];

    let val = -128isize;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd0, 0x80], buf);
}

#[test]
fn pass_i8() {
    let mut buf = [0x00, 0x00];

    let val = -128i8;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd0, 0x80], buf);
}

#[test]
fn pass_i16() {
    let mut buf = [0x00, 0x00, 0x00];

    let val = -32768i16;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd1, 0x80, 0x00], buf);
}

#[test]
fn pass_i32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = -2147483648i32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd2, 0x80, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_i64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = -9223372036854775808i64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_f32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 3.4028234e38_f32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xca, 0x7f, 0x7f, 0xff, 0xff], buf);
}

#[test]
fn pass_f64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 42f64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcb, 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_char() {
    let mut buf = [0x00, 0x00];

    let val = '!';
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xa1, 0x21], buf);
}

#[test]
fn pass_string() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = "le message";
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
}

#[test]
fn pass_struct() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    #[derive(RustcEncodable)]
    struct Decoded { id: u32, value: u32 }

    let val = Decoded { id: 42, value: 100500 };
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94], buf);
}

#[test]
fn pass_tuple() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = (42u32, 100500u32);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94], buf);
}

#[test]
fn pass_option_some() {
    let mut buf = [0x00];

    let val = Some(100u32);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x64], buf);
}

#[test]
fn pass_option_none() {
    let mut buf = [0x00];

    let val: Option<u32> = None;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xc0], buf);
}

#[test]
fn pass_seq() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = vec!["le", "shit"];
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0xa2, 0x6c, 0x65, 0xa4, 0x73, 0x68, 0x69, 0x74], buf);
}

#[test]
fn pass_map() {
    use std::collections::BTreeMap;

    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let mut val = BTreeMap::new();
    val.insert(0u8, "le");
    val.insert(1u8, "shit");
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [
        0x82, // 2 (size)
        0x00, // 0
        0xa2, 0x6c, 0x65, // "le"
        0x01, // 1
        0xa4, 0x73, 0x68, 0x69, 0x74, // "shit"
    ];
    assert_eq!(out, buf);
}

#[test]
fn pass_enum() {
    // We encode enum types as [id, [args...]].

    #[allow(unused)]
    #[derive(Debug, PartialEq, RustcEncodable)]
    enum Custom {
        First,
        Second,
    }

    let mut buf = [0x00; 3];

    let val = Custom::Second;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [0x92, 0x01, 0x90];
    assert_eq!(out, buf);
}

#[test]
fn pass_enum_variant_with_arg() {
    #[allow(unused)]
    #[derive(Debug, PartialEq, RustcEncodable)]
    enum Custom {
        First,
        Second(u32),
    }

    let mut buf = [0x00; 4];

    let val = Custom::Second(42);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [0x92, 0x01, 0x91, 0x2a];
    assert_eq!(out, buf);
}

#[test]
fn pass_encodong_struct_into_vec() {
    let val = (42u8, "the Answer");

    let mut buf: Vec<u8> = Vec::new();

    val.encode(&mut Encoder::new(&mut buf)).unwrap();

    assert_eq!(vec![0x92, 0x2a, 0xaa, 0x74, 0x68, 0x65, 0x20, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72], buf);
}

#[test]
fn encode_struct_with_string_using_vec() {
    #[derive(Debug, PartialEq, RustcEncodable)]
    struct Custom {
        data: String,
    }

    let mut buf = Vec::new();

    let val = Custom { data: "le message".to_string() };
    val.encode(&mut Encoder::new(&mut buf)).ok().unwrap();

    let out = vec![0x91, 0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65];
    assert_eq!(out, buf);
}
<commit_msg>fix: spelling in test name (#94)<commit_after>extern crate rmp_serialize;
extern crate rustc_serialize;

use rustc_serialize::Encodable;

use rmp_serialize::encode::Encoder;

#[test]
fn pass_null() {
    let mut buf = [0x00];

    let val = ();
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xc0], buf);
}

#[test]
fn fail_null() {
    let mut buf = [];

    let val = ();

    match val.encode(&mut Encoder::new(&mut &mut buf[..])) {
        Err(..) => (),
        other => panic!("unexpected result: {:?}", other)
    }
}

#[test]
fn pass_bool() {
    let mut buf = Vec::new();
    {
        let mut encoder = Encoder::new(&mut buf);
        true.encode(&mut encoder).unwrap();
        false.encode(&mut encoder).unwrap();
    }

    assert_eq!(vec![0xc3, 0xc2], buf);
}

#[test]
fn pass_usize() {
    let mut buf = [0x00, 0x00];

    let val = 255usize;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcc, 0xff], buf);
}

#[test]
fn pass_u8() {
    let mut buf = [0x00, 0x00];

    let val = 255u8;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcc, 0xff], buf);
}

#[test]
fn pass_u16() {
    let mut buf = [0x00, 0x00, 0x00];

    let val = 65535u16;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcd, 0xff, 0xff], buf);
}

#[test]
fn pass_u32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 4294967295u32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xce, 0xff, 0xff, 0xff, 0xff], buf);
}

#[test]
fn pass_u64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 18446744073709551615u64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], buf);
}

#[test]
fn pass_isize() {
    let mut buf = [0x00, 0x00];

    let val = -128isize;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd0, 0x80], buf);
}

#[test]
fn pass_i8() {
    let mut buf = [0x00, 0x00];

    let val = -128i8;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd0, 0x80], buf);
}

#[test]
fn pass_i16() {
    let mut buf = [0x00, 0x00, 0x00];

    let val = -32768i16;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd1, 0x80, 0x00], buf);
}

#[test]
fn pass_i32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = -2147483648i32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd2, 0x80, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_i64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = -9223372036854775808i64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_f32() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 3.4028234e38_f32;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xca, 0x7f, 0x7f, 0xff, 0xff], buf);
}

#[test]
fn pass_f64() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = 42f64;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xcb, 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf);
}

#[test]
fn pass_char() {
    let mut buf = [0x00, 0x00];

    let val = '!';
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xa1, 0x21], buf);
}

#[test]
fn pass_string() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = "le message";
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf);
}

#[test]
fn pass_struct() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    #[derive(RustcEncodable)]
    struct Decoded { id: u32, value: u32 }

    let val = Decoded { id: 42, value: 100500 };
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94], buf);
}

#[test]
fn pass_tuple() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = (42u32, 100500u32);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94], buf);
}

#[test]
fn pass_option_some() {
    let mut buf = [0x00];

    let val = Some(100u32);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x64], buf);
}

#[test]
fn pass_option_none() {
    let mut buf = [0x00];

    let val: Option<u32> = None;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0xc0], buf);
}

#[test]
fn pass_seq() {
    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let val = vec!["le", "shit"];
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    assert_eq!([0x92, 0xa2, 0x6c, 0x65, 0xa4, 0x73, 0x68, 0x69, 0x74], buf);
}

#[test]
fn pass_map() {
    use std::collections::BTreeMap;

    let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    let mut val = BTreeMap::new();
    val.insert(0u8, "le");
    val.insert(1u8, "shit");
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [
        0x82, // 2 (size)
        0x00, // 0
        0xa2, 0x6c, 0x65, // "le"
        0x01, // 1
        0xa4, 0x73, 0x68, 0x69, 0x74, // "shit"
    ];
    assert_eq!(out, buf);
}

#[test]
fn pass_enum() {
    // We encode enum types as [id, [args...]].

    #[allow(unused)]
    #[derive(Debug, PartialEq, RustcEncodable)]
    enum Custom {
        First,
        Second,
    }

    let mut buf = [0x00; 3];

    let val = Custom::Second;
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [0x92, 0x01, 0x90];
    assert_eq!(out, buf);
}

#[test]
fn pass_enum_variant_with_arg() {
    #[allow(unused)]
    #[derive(Debug, PartialEq, RustcEncodable)]
    enum Custom {
        First,
        Second(u32),
    }

    let mut buf = [0x00; 4];

    let val = Custom::Second(42);
    val.encode(&mut Encoder::new(&mut &mut buf[..])).ok().unwrap();

    let out = [0x92, 0x01, 0x91, 0x2a];
    assert_eq!(out, buf);
}

#[test]
fn pass_encoding_struct_into_vec() {
    let val = (42u8, "the Answer");

    let mut buf: Vec<u8> = Vec::new();

    val.encode(&mut Encoder::new(&mut buf)).unwrap();

    assert_eq!(vec![0x92, 0x2a, 0xaa, 0x74, 0x68, 0x65, 0x20, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72], buf);
}

#[test]
fn encode_struct_with_string_using_vec() {
    #[derive(Debug, PartialEq, RustcEncodable)]
    struct Custom {
        data: String,
    }

    let mut buf = Vec::new();

    let val = Custom { data: "le message".to_string() };
    val.encode(&mut Encoder::new(&mut buf)).ok().unwrap();

    let out = vec![0x91, 0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65];
    assert_eq!(out, buf);
}
<|endoftext|>


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

use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root};
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::utils::WindowProxyHandler;
use dom::bindings::utils::get_array_index_from_id;
use dom::document::Document;
use dom::element::Element;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
use js::glue::{GetProxyPrivate, SetProxyExtra};
use js::jsapi::{Handle, JS_ForwardSetPropertyTo, ObjectOpResult, RootedObject, RootedValue};
use js::jsapi::{HandleId, HandleObject, MutableHandle, MutableHandleValue};
use js::jsapi::{JSAutoCompartment, JSAutoRequest, JS_GetClass};
use js::jsapi::{JSContext, JSErrNum, JSObject, JSPropertyDescriptor};
use js::jsapi::{JS_AlreadyHasOwnPropertyById, JS_ForwardGetPropertyTo};
use js::jsapi::{JS_DefinePropertyById6, JS_GetPropertyDescriptorById};
use js::jsval::{ObjectValue, UndefinedValue, PrivateValue};
use std::ptr;

#[dom_struct]
pub struct BrowsingContext {
    reflector: Reflector,
    history: Vec<SessionHistoryEntry>,
    active_index: usize,
    frame_element: Option<JS<Element>>,
}

impl BrowsingContext {
    pub fn new_inherited(document: &Document, frame_element: Option<&Element>) -> BrowsingContext {
        BrowsingContext {
            reflector: Reflector::new(),
            history: vec![SessionHistoryEntry::new(document)],
            active_index: 0,
            frame_element: frame_element.map(JS::from_ref),
        }
    }

    #[allow(unsafe_code)]
    pub fn new(document: &Document, frame_element: Option<&Element>) -> Root<BrowsingContext> {
        unsafe {
            let window = document.window();

            let WindowProxyHandler(handler) = window.windowproxy_handler();
            assert!(!handler.is_null());

            let cx = window.get_cx();
            let _ar = JSAutoRequest::new(cx);
            let parent = window.reflector().get_jsobject();
            assert!(!parent.get().is_null());
            assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0);
            let _ac = JSAutoCompartment::new(cx, parent.get());
            let window_proxy = RootedObject::new(cx,
                NewWindowProxy(cx, parent, handler));
            assert!(!window_proxy.ptr.is_null());

            let object = box BrowsingContext::new_inherited(document, frame_element);

            let raw = Box::into_raw(object);
            SetProxyExtra(window_proxy.ptr, 0, PrivateValue(raw as *const _));

            (*raw).init_reflector(window_proxy.ptr);

            Root::from_ref(&*raw)
        }
    }

    pub fn active_document(&self) -> &Document {
        &*self.history[self.active_index].document
    }

    pub fn active_window(&self) -> &Window {
        self.active_document().window()
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.as_ref().map(|element| &**element)
    }

    pub fn window_proxy(&self) -> *mut JSObject {
        let window_proxy = self.reflector.get_jsobject();
        assert!(!window_proxy.get().is_null());
        window_proxy.get()
    }
}

// This isn't a DOM struct, just a convenience struct
// without a reflector, so we don't mark this as #[dom_struct]
#[must_root]
#[privatize]
#[derive(JSTraceable, HeapSizeOf)]
pub struct SessionHistoryEntry {
    document: JS<Document>,
    children: Vec<JS<BrowsingContext>>,
}

impl SessionHistoryEntry {
    fn new(document: &Document) -> SessionHistoryEntry {
        SessionHistoryEntry {
            document: JS::from_ref(document),
            children: vec![],
        }
    }
}

#[allow(unsafe_code)]
unsafe fn GetSubframeWindow(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId)
                            -> Option<Root<Window>> {
    let index = get_array_index_from_id(cx, id);
    if let Some(index) = index {
        let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
        let win = root_from_handleobject::<Window>(target.handle()).unwrap();
        let mut found = false;
        return win.IndexedGetter(index, &mut found);
    }

    None
}

#[allow(unsafe_code)]
unsafe extern "C" fn getOwnPropertyDescriptor(cx: *mut JSContext,
                                              proxy: HandleObject,
                                              id: HandleId,
                                              desc: MutableHandle<JSPropertyDescriptor>)
                                              -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        let mut val = RootedValue::new(cx, UndefinedValue());
        window.to_jsval(cx, val.handle_mut());
        (*desc.ptr).value = val.ptr;
        fill_property_descriptor(&mut *desc.ptr, *proxy.ptr, true);
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    // XXX This should be JS_GetOwnPropertyDescriptorById
    if !JS_GetPropertyDescriptorById(cx, target.handle(), id, desc) {
        return false;
    }

    if (*desc.ptr).obj != target.ptr {
        // Not an own property
        (*desc.ptr).obj = ptr::null_mut();
    } else {
        (*desc.ptr).obj = *proxy.ptr;
    }

    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn defineProperty(cx: *mut JSContext,
                                    proxy: HandleObject,
                                    id: HandleId,
                                    desc: Handle<JSPropertyDescriptor>,
                                    res: *mut ObjectOpResult)
                                    -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Spec says to Reject whether this is a supported index or not,
        // since we have no indexed setter or indexed creator.  That means
        // throwing in strict mode (FIXME: Bug 828137), doing nothing in
        // non-strict mode.
        (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    JS_DefinePropertyById6(cx, target.handle(), id, desc, res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn hasOwn(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId,
                            bp: *mut bool)
                            -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if window.is_some() {
        *bp = true;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    let mut found = false;
    if !JS_AlreadyHasOwnPropertyById(cx, target.handle(), id, &mut found) {
        return false;
    }

    *bp = found;
    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn get(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleObject,
                         id: HandleId,
                         vp: MutableHandleValue)
                         -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        window.to_jsval(cx, vp);
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, vp)
}

#[allow(unsafe_code)]
unsafe extern "C" fn set(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleObject,
                         id: HandleId,
                         vp: MutableHandleValue,
                         res: *mut ObjectOpResult)
                         -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Reject (which means throw if and only if strict) the set.
        (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    let receiver = RootedValue::new(cx, ObjectValue(&**receiver.ptr));
    JS_ForwardSetPropertyTo(cx,
                            target.handle(),
                            id,
                            vp.to_handle(),
                            receiver.handle(),
                            res)
}

static PROXY_HANDLER: ProxyTraps = ProxyTraps {
    enter: None,
    getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
    defineProperty: Some(defineProperty),
    ownPropertyKeys: None,
    delete_: None,
    enumerate: None,
    preventExtensions: None,
    isExtensible: None,
    has: None,
    get: Some(get),
    set: Some(set),
    call: None,
    construct: None,
    getPropertyDescriptor: Some(get_property_descriptor),
    hasOwn: Some(hasOwn),
    getOwnEnumerablePropertyKeys: None,
    nativeCall: None,
    hasInstance: None,
    objectClassIs: None,
    className: None,
    fun_toString: None,
    boxedValue_unbox: None,
    defaultValue: None,
    trace: None,
    finalize: None,
    objectMoved: None,
    isCallable: None,
    isConstructor: None,
};

#[allow(unsafe_code)]
pub fn new_window_proxy_handler() -> WindowProxyHandler {
    unsafe {
        WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
    }
}
<commit_msg>Use JS_GetOwnPropertyDescriptorById in BrowserContext (fixes #6984).<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root};
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::utils::WindowProxyHandler;
use dom::bindings::utils::get_array_index_from_id;
use dom::document::Document;
use dom::element::Element;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
use js::glue::{GetProxyPrivate, SetProxyExtra};
use js::jsapi::{Handle, JS_ForwardSetPropertyTo, ObjectOpResult, RootedObject, RootedValue};
use js::jsapi::{HandleId, HandleObject, MutableHandle, MutableHandleValue};
use js::jsapi::{JSAutoCompartment, JSAutoRequest, JS_GetClass};
use js::jsapi::{JSContext, JSErrNum, JSObject, JSPropertyDescriptor};
use js::jsapi::{JS_AlreadyHasOwnPropertyById, JS_ForwardGetPropertyTo};
use js::jsapi::{JS_DefinePropertyById6, JS_GetOwnPropertyDescriptorById};
use js::jsval::{ObjectValue, UndefinedValue, PrivateValue};

#[dom_struct]
pub struct BrowsingContext {
    reflector: Reflector,
    history: Vec<SessionHistoryEntry>,
    active_index: usize,
    frame_element: Option<JS<Element>>,
}

impl BrowsingContext {
    pub fn new_inherited(document: &Document, frame_element: Option<&Element>) -> BrowsingContext {
        BrowsingContext {
            reflector: Reflector::new(),
            history: vec![SessionHistoryEntry::new(document)],
            active_index: 0,
            frame_element: frame_element.map(JS::from_ref),
        }
    }

    #[allow(unsafe_code)]
    pub fn new(document: &Document, frame_element: Option<&Element>) -> Root<BrowsingContext> {
        unsafe {
            let window = document.window();

            let WindowProxyHandler(handler) = window.windowproxy_handler();
            assert!(!handler.is_null());

            let cx = window.get_cx();
            let _ar = JSAutoRequest::new(cx);
            let parent = window.reflector().get_jsobject();
            assert!(!parent.get().is_null());
            assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0);
            let _ac = JSAutoCompartment::new(cx, parent.get());
            let window_proxy = RootedObject::new(cx,
                NewWindowProxy(cx, parent, handler));
            assert!(!window_proxy.ptr.is_null());

            let object = box BrowsingContext::new_inherited(document, frame_element);

            let raw = Box::into_raw(object);
            SetProxyExtra(window_proxy.ptr, 0, PrivateValue(raw as *const _));

            (*raw).init_reflector(window_proxy.ptr);

            Root::from_ref(&*raw)
        }
    }

    pub fn active_document(&self) -> &Document {
        &*self.history[self.active_index].document
    }

    pub fn active_window(&self) -> &Window {
        self.active_document().window()
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.as_ref().map(|element| &**element)
    }

    pub fn window_proxy(&self) -> *mut JSObject {
        let window_proxy = self.reflector.get_jsobject();
        assert!(!window_proxy.get().is_null());
        window_proxy.get()
    }
}

// This isn't a DOM struct, just a convenience struct
// without a reflector, so we don't mark this as #[dom_struct]
#[must_root]
#[privatize]
#[derive(JSTraceable, HeapSizeOf)]
pub struct SessionHistoryEntry {
    document: JS<Document>,
    children: Vec<JS<BrowsingContext>>,
}

impl SessionHistoryEntry {
    fn new(document: &Document) -> SessionHistoryEntry {
        SessionHistoryEntry {
            document: JS::from_ref(document),
            children: vec![],
        }
    }
}

#[allow(unsafe_code)]
unsafe fn GetSubframeWindow(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId)
                            -> Option<Root<Window>> {
    let index = get_array_index_from_id(cx, id);
    if let Some(index) = index {
        let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
        let win = root_from_handleobject::<Window>(target.handle()).unwrap();
        let mut found = false;
        return win.IndexedGetter(index, &mut found);
    }

    None
}

#[allow(unsafe_code)]
unsafe extern "C" fn getOwnPropertyDescriptor(cx: *mut JSContext,
                                              proxy: HandleObject,
                                              id: HandleId,
                                              desc: MutableHandle<JSPropertyDescriptor>)
                                              -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        let mut val = RootedValue::new(cx, UndefinedValue());
        window.to_jsval(cx, val.handle_mut());
        (*desc.ptr).value = val.ptr;
        fill_property_descriptor(&mut *desc.ptr, *proxy.ptr, true);
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    if !JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, desc) {
        return false;
    }

    assert!(desc.get().obj.is_null() || desc.get().obj == target.ptr);
    if desc.get().obj == target.ptr {
        desc.get().obj = *proxy.ptr;
    }

    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn defineProperty(cx: *mut JSContext,
                                    proxy: HandleObject,
                                    id: HandleId,
                                    desc: Handle<JSPropertyDescriptor>,
                                    res: *mut ObjectOpResult)
                                    -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Spec says to Reject whether this is a supported index or not,
        // since we have no indexed setter or indexed creator.  That means
        // throwing in strict mode (FIXME: Bug 828137), doing nothing in
        // non-strict mode.
        (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    JS_DefinePropertyById6(cx, target.handle(), id, desc, res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn hasOwn(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId,
                            bp: *mut bool)
                            -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if window.is_some() {
        *bp = true;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    let mut found = false;
    if !JS_AlreadyHasOwnPropertyById(cx, target.handle(), id, &mut found) {
        return false;
    }

    *bp = found;
    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn get(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleObject,
                         id: HandleId,
                         vp: MutableHandleValue)
                         -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        window.to_jsval(cx, vp);
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, vp)
}

#[allow(unsafe_code)]
unsafe extern "C" fn set(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleObject,
                         id: HandleId,
                         vp: MutableHandleValue,
                         res: *mut ObjectOpResult)
                         -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Reject (which means throw if and only if strict) the set.
        (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
        return true;
    }

    let target = RootedObject::new(cx, GetProxyPrivate(*proxy.ptr).to_object());
    let receiver = RootedValue::new(cx, ObjectValue(&**receiver.ptr));
    JS_ForwardSetPropertyTo(cx,
                            target.handle(),
                            id,
                            vp.to_handle(),
                            receiver.handle(),
                            res)
}

static PROXY_HANDLER: ProxyTraps = ProxyTraps {
    enter: None,
    getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
    defineProperty: Some(defineProperty),
    ownPropertyKeys: None,
    delete_: None,
    enumerate: None,
    preventExtensions: None,
    isExtensible: None,
    has: None,
    get: Some(get),
    set: Some(set),
    call: None,
    construct: None,
    getPropertyDescriptor: Some(get_property_descriptor),
    hasOwn: Some(hasOwn),
    getOwnEnumerablePropertyKeys: None,
    nativeCall: None,
    hasInstance: None,
    objectClassIs: None,
    className: None,
    fun_toString: None,
    boxedValue_unbox: None,
    defaultValue: None,
    trace: None,
    finalize: None,
    objectMoved: None,
    isCallable: None,
    isConstructor: None,
};

#[allow(unsafe_code)]
pub fn new_window_proxy_handler() -> WindowProxyHandler {
    unsafe {
        WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
    }
}
<|endoftext|>


<commit_before>#![feature(question_mark)]
#[macro_use]
extern crate rustfbp;
extern crate capnp;
extern crate toml;
extern crate libc;
extern crate copperline;

use std::thread;
use std::env::{current_dir, home_dir};
use std::io::stdout;
use copperline::Copperline;

pub struct Prompt {
    user_prompt: String,
    cwd: String,
}

impl Prompt {
    pub fn new() -> Prompt {
        let mut object = Prompt {
            user_prompt: "lain > ".to_owned(),
            cwd: "~/".to_owned(),
        };
        object.update_cwd();
        object
    }

    pub fn get_user_prompt(&self) -> String {
        self.user_prompt.to_owned()
    }

    pub fn get_cwd(&self) -> String {
        self.cwd.to_owned()
    }

    pub fn update_cwd(&mut self){
        let buff = current_dir().ok().expect("No current directory");

        if buff.starts_with(home_dir().expect("No Home directory").as_path()){
        let mut home = "~/".to_owned();
            home.push_str(buff.as_path()
                .to_str().expect("Failed to become a str"));
            self.cwd = home;
        } else {
            self.cwd = buff.as_path()
                .to_str().expect("Failed to turn path into str").to_owned();
        }

    }

    pub fn print(&self) {
        print!("{}", self.get_user_prompt());
        stdout().flush().ok().expect("Could not flush stdout");
    }

}

component! {
  shells_lain_prompt, contracts(generic_text)
  inputs(),
  inputs_array(),
  outputs(output: generic_text),
  outputs_array(),
  option(),
  acc(),
  fn run(&mut self) -> Result<()> {
    let mut prompt = Prompt::new();
    let mut input_buffer = Copperline::new();
    println!("Copyright - Noware Ltd. Hong Kong");
    println!("License - Mozilla Public License v2");
    println!("Welcome to the Fractalide Shell.");
    loop {
        let line = input_buffer.read_line_utf8(&prompt.get_user_prompt()).ok();
        if line.is_none(){
            continue;
        }
        let command = line.expect("Could not get line");
        input_buffer.add_history(command.clone());
        if command.is_empty() {
            prompt.print();
            continue;
        } else if command.starts_with("exit") {
            println!("bye");
            break;
        }
        let mut out_ip_output = IP::new();
        {
          let mut variable = out_ip_output.init_root::<generic_text::Builder>();
          variable.set_text(command.as_str());
        }
        self.ports.send("output", out_ip_output)?;
        prompt.print();
    }
    Ok(())
  }
}
<commit_msg>Problem: users might not know how to quit lain<commit_after>#![feature(question_mark)]
#[macro_use]
extern crate rustfbp;
extern crate capnp;
extern crate toml;
extern crate libc;
extern crate copperline;

use std::thread;
use std::env::{current_dir, home_dir};
use std::io::stdout;
use copperline::Copperline;

pub struct Prompt {
    user_prompt: String,
    cwd: String,
}

impl Prompt {
    pub fn new() -> Prompt {
        let mut object = Prompt {
            user_prompt: "lain > ".to_owned(),
            cwd: "~/".to_owned(),
        };
        object.update_cwd();
        object
    }

    pub fn get_user_prompt(&self) -> String {
        self.user_prompt.to_owned()
    }

    pub fn get_cwd(&self) -> String {
        self.cwd.to_owned()
    }

    pub fn update_cwd(&mut self){
        let buff = current_dir().ok().expect("No current directory");

        if buff.starts_with(home_dir().expect("No Home directory").as_path()){
        let mut home = "~/".to_owned();
            home.push_str(buff.as_path()
                .to_str().expect("Failed to become a str"));
            self.cwd = home;
        } else {
            self.cwd = buff.as_path()
                .to_str().expect("Failed to turn path into str").to_owned();
        }

    }

    pub fn print(&self) {
        print!("{}", self.get_user_prompt());
        stdout().flush().ok().expect("Could not flush stdout");
    }

}

component! {
  shells_lain_prompt, contracts(generic_text)
  inputs(),
  inputs_array(),
  outputs(output: generic_text),
  outputs_array(),
  option(),
  acc(),
  fn run(&mut self) -> Result<()> {
    let mut prompt = Prompt::new();
    let mut input_buffer = Copperline::new();
    println!("Copyright - Noware Ltd. Hong Kong");
    println!("License - Mozilla Public License v2");
    println!("Welcome to the Fractalide Shell.");
    println!("Type 'exit' then <ctl>-c to quit.");
    loop {
        let line = input_buffer.read_line_utf8(&prompt.get_user_prompt()).ok();
        if line.is_none(){
            continue;
        }
        let command = line.expect("Could not get line");
        input_buffer.add_history(command.clone());
        if command.is_empty() {
            prompt.print();
            continue;
        } else if command.starts_with("exit") {
            println!("bye");
            break;
        }
        let mut out_ip_output = IP::new();
        {
          let mut variable = out_ip_output.init_root::<generic_text::Builder>();
          variable.set_text(command.as_str());
        }
        self.ports.send("output", out_ip_output)?;
        prompt.print();
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>use seax_svm::svm::cell::SVMCell;
use seax_svm::slist::List;

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self)                        -> Result<SVMCell, &'static str>;

    /// Pretty-print this node
    fn prettyprint(&self)                   -> String {
        prettyprint_level(&self, 0usize)
    }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize)     -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
#[deriving(Clone, PartialEq)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    List(ListNode),
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

/// AST node for the root of a program's AST
#[deriving(Clone, PartialEq)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[deriving(Clone, PartialEq)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

/// AST node for a list literal
#[deriving(Clone, PartialEq)]
pub struct ListNode { pub elements: Vec<ExprNode> }

/// AST node for an identifier
#[deriving(Clone, PartialEq)]
pub struct NameNode { pub name: String }

/// AST node for an integer constant
#[deriving(Clone, PartialEq)]
pub struct IntNode { pub value: isize }

/// AST node for an unsigned integer constant
#[deriving(Clone, PartialEq)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[deriving(Clone, PartialEq)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[deriving(Clone, PartialEq)]
pub struct BoolNode { pub value: bool }

/// AST node for a character constant
#[deriving(Clone, PartialEq)]
pub struct CharNode { pub value: char }

<commit_msg>Use correct name for 'derive'<commit_after>use seax_svm::svm::cell::SVMCell;
use seax_svm::slist::List;

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self)                        -> Result<SVMCell, &'static str>;

    /// Pretty-print this node
    fn prettyprint(&self)                   -> String {
        prettyprint_level(&self, 0usize)
    }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize)     -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
#[derive(Clone, PartialEq)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    List(ListNode),
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

/// AST node for the root of a program's AST
#[derive(Clone, PartialEq)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[derive(Clone, PartialEq)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

/// AST node for a list literal
#[derive(Clone, PartialEq)]
pub struct ListNode { pub elements: Vec<ExprNode> }

/// AST node for an identifier
#[derive(Clone, PartialEq)]
pub struct NameNode { pub name: String }

/// AST node for an integer constant
#[derive(Clone, PartialEq)]
pub struct IntNode { pub value: isize }

/// AST node for an unsigned integer constant
#[derive(Clone, PartialEq)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[derive(Clone, PartialEq)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[derive(Clone, PartialEq)]
pub struct BoolNode { pub value: bool }

/// AST node for a character constant
#[derive(Clone, PartialEq)]
pub struct CharNode { pub value: char }

<|endoftext|>


<commit_before>use clap::{IntoApp, Parser, Subcommand};
use clap_complete;
use cmd_queue::{constants, error::CmdqClientError, CommandRequest, CommandResponse};
use reqwest;

#[derive(Parser, Debug)]
#[clap(name = "cmdq")]
#[clap(author = "Jonathan Fok kan <jonathan@fokkan.ca>")]
#[clap(version = "1.0")]
#[clap(about = "A program to queue commands", long_about = None)]
struct Cli {
    #[clap(help = "command to queue")]
    pub input: Vec<String>,

    #[clap(subcommand)]
    pub subcommands: Option<Subcommands>,
}

#[derive(Subcommand, Debug)]
enum Subcommands {
    /// Download with yt-dlp
    Ytdlp {
        url: String,
        #[clap(long, short, help = "Optional prefix to filename downloaded")]
        prefix: Option<String>,
    },
    /// Shutdown server daemon
    Shutdown {
        #[clap(long, short, help = "Force shutdown of cmdq server")]
        force: bool,
    },
    GenerateCompletion {
        #[clap(arg_enum)]
        shell: clap_complete::Shell,
    },
}

fn main() -> Result<(), CmdqClientError> {
    let cli = Cli::parse();
    //println!("{:?}", cli);
    let cwd = std::env::current_dir().expect("current dir");

    if !cli.input.is_empty() && cli.subcommands.is_some() {
        println!("Sorry, but I don't know what to do both INPUT and subcommand were encountered. Going to sleep instead.");
        Ok(())
    } else if let Some(subcommand) = cli.subcommands {
        match subcommand {
            Subcommands::Ytdlp { url, prefix } => {
                let args = if let Some(prefix) = prefix {
                    vec![
                        "-o".to_string(),
                        format!("{} %(title)s [%(id)s].%(ext)s", prefix),
                        url,
                    ]
                } else {
                    vec![url]
                };
                command_request(&cwd.to_string_lossy(), "yt-dlp", args)
            }
            Subcommands::Shutdown { force } => shutdown_server(),
            Subcommands::GenerateCompletion { shell } => {
                print_completions(shell, &mut Cli::command_for_update());
                Ok(())
            }
        }
    } else if !cli.input.is_empty() {
        command_request(
            &cwd.to_string_lossy(),
            &cli.input[0],
            cli.input.clone().into_iter().skip(1).collect(),
        )
    } else {
        println!("no command queued");
        Ok(())
    }
}

fn print_completions<G: clap_complete::Generator>(gen: G, cmd: &mut clap::Command) {
    clap_complete::generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
}

fn command_request(cwd: &str, program: &str, args: Vec<String>) -> Result<(), CmdqClientError> {
    start_server_if_needed().expect("failed to start server");

    let client = reqwest::blocking::Client::new();
    let response = client
        .post(server_host("commands/"))
        .json(&CommandRequest {
            path: cwd.to_string(),
            program: program.to_string(),
            args: args,
        })
        .send()
        .map_err(|e| CmdqClientError::HttpClientError(e))?;
    println!("{:?}", response);
    let cmd_response = response
        .json::<CommandResponse>()
        .map_err(|e| CmdqClientError::ResponseDeserializationError(e))?;
    Ok(())
}

fn start_server_if_needed() -> std::io::Result<()> {
    let resp = reqwest::blocking::get(server_host("health"));
    if resp.is_err() {
        std::process::Command::new("cmdq_server").spawn()?;
        // TODO better handling of waiting for server to startup
        // Continue to poll health endpoint with max attempts and backoff
        std::thread::sleep(std::time::Duration::from_secs(2));
    }
    Ok(())
}

fn shutdown_server() -> Result<(), CmdqClientError> {
    use nix::sys::signal::{self, Signal};
    use nix::unistd::Pid;
    use std::fs;
    let pid_str = fs::read_to_string(constants::SERVER_DAEMON_PIDFILE).map_err(|e| {
        CmdqClientError::ReadServerPidFile(constants::SERVER_DAEMON_PIDFILE.to_string(), e)
    })?;
    let pid = pid_str
        .parse::<i32>()
        .map_err(|e| CmdqClientError::ParseServerPid(e))?;
    signal::kill(Pid::from_raw(pid), Signal::SIGINT)
        .map_err(|e| CmdqClientError::KillServer(pid, e))?;
    Ok(())
}

fn server_host(path: &str) -> String {
    format!("http://localhost:{}/{}", constants::DEFAULT_PORT, path)
}
<commit_msg>cmd-queue: Added force shutdown<commit_after>use clap::{IntoApp, Parser, Subcommand};
use clap_complete;
use cmd_queue::{constants, error::CmdqClientError, CommandRequest, CommandResponse};
use reqwest;

#[derive(Parser, Debug)]
#[clap(name = "cmdq")]
#[clap(author = "Jonathan Fok kan <jonathan@fokkan.ca>")]
#[clap(version = "1.0")]
#[clap(about = "A program to queue commands", long_about = None)]
struct Cli {
    #[clap(help = "command to queue")]
    pub input: Vec<String>,

    #[clap(subcommand)]
    pub subcommands: Option<Subcommands>,
}

#[derive(Subcommand, Debug)]
enum Subcommands {
    /// Download with yt-dlp
    Ytdlp {
        url: String,
        #[clap(long, short, help = "Optional prefix to filename downloaded")]
        prefix: Option<String>,
    },
    /// Shutdown server daemon
    Shutdown {
        #[clap(long, short, help = "Force shutdown of cmdq server")]
        force: bool,
    },
    GenerateCompletion {
        #[clap(arg_enum)]
        shell: clap_complete::Shell,
    },
}

fn main() -> Result<(), CmdqClientError> {
    let cli = Cli::parse();
    println!("{:?}", cli);
    let cwd = std::env::current_dir().expect("current dir");

    if !cli.input.is_empty() && cli.subcommands.is_some() {
        println!("Sorry, but I don't know what to do both INPUT and subcommand were encountered. Going to sleep instead.");
        Ok(())
    } else if let Some(subcommand) = cli.subcommands {
        match subcommand {
            Subcommands::Ytdlp { url, prefix } => {
                let args = if let Some(prefix) = prefix {
                    vec![
                        "-o".to_string(),
                        format!("{} %(title)s [%(id)s].%(ext)s", prefix),
                        url,
                    ]
                } else {
                    vec![url]
                };
                command_request(&cwd.to_string_lossy(), "yt-dlp", args)
            }
            Subcommands::Shutdown { force } => shutdown_server(force),
            Subcommands::GenerateCompletion { shell } => {
                print_completions(shell, &mut Cli::command_for_update());
                Ok(())
            }
        }
    } else if !cli.input.is_empty() {
        command_request(
            &cwd.to_string_lossy(),
            &cli.input[0],
            cli.input.clone().into_iter().skip(1).collect(),
        )
    } else {
        println!("no command queued");
        Ok(())
    }
}

fn print_completions<G: clap_complete::Generator>(gen: G, cmd: &mut clap::Command) {
    clap_complete::generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
}

fn command_request(cwd: &str, program: &str, args: Vec<String>) -> Result<(), CmdqClientError> {
    start_server_if_needed().expect("failed to start server");

    let client = reqwest::blocking::Client::new();
    let response = client
        .post(server_host("commands/"))
        .json(&CommandRequest {
            path: cwd.to_string(),
            program: program.to_string(),
            args: args,
        })
        .send()
        .map_err(|e| CmdqClientError::HttpClientError(e))?;
    println!("{:?}", response);
    let cmd_response = response
        .json::<CommandResponse>()
        .map_err(|e| CmdqClientError::ResponseDeserializationError(e))?;
    Ok(())
}

fn start_server_if_needed() -> std::io::Result<()> {
    let resp = reqwest::blocking::get(server_host("health"));
    if resp.is_err() {
        std::process::Command::new("cmdq_server").spawn()?;
        // TODO better handling of waiting for server to startup
        // Continue to poll health endpoint with max attempts and backoff
        std::thread::sleep(std::time::Duration::from_secs(2));
    }
    Ok(())
}

fn shutdown_server(force: bool) -> Result<(), CmdqClientError> {
    use nix::sys::signal::{self, Signal};
    use nix::unistd::Pid;
    use std::fs;
    let pid_str = fs::read_to_string(constants::SERVER_DAEMON_PIDFILE).map_err(|e| {
        CmdqClientError::ReadServerPidFile(constants::SERVER_DAEMON_PIDFILE.to_string(), e)
    })?;
    let pid = pid_str
        .parse::<i32>()
        .map_err(|e| CmdqClientError::ParseServerPid(e))?;
    let signal = if force {
        Signal::SIGTERM
    } else {
        Signal::SIGINT
    };
    signal::kill(Pid::from_raw(pid), signal).map_err(|e| CmdqClientError::KillServer(pid, e))?;
    Ok(())
}

fn server_host(path: &str) -> String {
    format!("http://localhost:{}/{}", constants::DEFAULT_PORT, path)
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This crate implements a *configuration service* for Exonum blockchain framework.
//!
//! Upon being plugged in, the service allows to modify Exonum blockchain configuration
//! using [proposals](struct.Propose.html) and [voting for proposal](struct.Vote.html),
//! both of which are implemented as transactions signed by blockchain validators.
//!
//! The service also provides HTTP API for public queries (get actual/following
//! configuration, etc.) and private queries, intended for use only by validator nodes' maintainers
//! (post configuration propose, post vote for a configuration propose).
//!
//! # Blockchain configuration
//!
//! Blockchain configuration corresponds to [`StoredConfiguration`][sc]
//! in the Exonum core library. The logic of the configuration service extensively uses
//! hashes of configuration, which are calculated as follows:
//!
//! 1. Parse a `StoredConfiguration` from JSON string if necessary.
//! 2. Convert a `StoredConfiguration` into bytes as per its `StorageValue` implementation.
//! 3. Use `exonum::crypto::hash()` on the obtained bytes.
//!
//! [sc]: https://docs.rs/exonum/0.3.0/exonum/blockchain/config/struct.StoredConfiguration.html
//!
//! # Examples
//!
//! ```rust,no_run
//! extern crate exonum;
//! extern crate exonum_configuration;
//!
//! use exonum::helpers::fabric::NodeBuilder;
//!
//! use exonum_configuration::ConfigurationServiceFactory;
//!
//! fn main() {
//!     exonum::helpers::init_logger().unwrap();
//!     NodeBuilder::new()
//!         .with_service(Box::new(ConfigurationServiceFactory))
//!         .run();
//! }
//! ```

#[macro_use]
extern crate exonum;
#[cfg(test)]
#[macro_use]
extern crate exonum_testkit;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate iron;
extern crate router;
extern crate bodyparser;
extern crate params;
#[macro_use]
extern crate lazy_static;

use router::Router;
use iron::Handler;
use exonum::api::Api;
use exonum::blockchain::{Service, Transaction, ApiContext};
use exonum::helpers::fabric::{ServiceFactory, Context};
use exonum::crypto::Hash;
use exonum::messages::RawTransaction;
use exonum::storage::Snapshot;
use exonum::encoding::{Error as EncodingError};

pub mod api;
pub mod schema;
pub mod transactions;

#[cfg(test)]
mod tests;

#[doc(no_inline)]
pub use schema::{ConfigurationSchema, ProposeData};
#[doc(no_inline)]
pub use transactions::{Propose, Vote};

/// Service identifier for the configuration service.
pub const CONFIGURATION_SERVICE_ID: u16 = 1;

/// Configuration service.
#[derive(Default)]
pub struct ConfigurationService {}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_name(&self) -> &'static str {
        "configuration"
    }

    fn service_id(&self) -> u16 {
        CONFIGURATION_SERVICE_ID
    }

    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash> {
        let schema = ConfigurationSchema::new(snapshot);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, EncodingError> {
        transactions::tx_from_raw(raw)
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PublicConfigApi { blockchain: ctx.blockchain().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PrivateConfigApi {
            channel: ctx.node_channel().clone(),
            config: (*ctx.public_key(), ctx.secret_key().clone()),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A configuration service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct ConfigurationServiceFactory;

impl ServiceFactory for ConfigurationServiceFactory {
    fn make_service(&mut self, _: &Context) -> Box<Service> {
        Box::new(ConfigurationService::new())
    }
}
<commit_msg>Hide `api` module<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This crate implements a *configuration service* for Exonum blockchain framework.
//!
//! Upon being plugged in, the service allows to modify Exonum blockchain configuration
//! using [proposals](struct.Propose.html) and [voting for proposal](struct.Vote.html),
//! both of which are implemented as transactions signed by blockchain validators.
//!
//! The service also provides HTTP API for public queries (get actual/following
//! configuration, etc.) and private queries, intended for use only by validator nodes' maintainers
//! (post configuration propose, post vote for a configuration propose).
//!
//! # Blockchain configuration
//!
//! Blockchain configuration corresponds to [`StoredConfiguration`][sc]
//! in the Exonum core library. The logic of the configuration service extensively uses
//! hashes of configuration, which are calculated as follows:
//!
//! 1. Parse a `StoredConfiguration` from JSON string if necessary.
//! 2. Convert a `StoredConfiguration` into bytes as per its `StorageValue` implementation.
//! 3. Use `exonum::crypto::hash()` on the obtained bytes.
//!
//! [sc]: https://docs.rs/exonum/0.3.0/exonum/blockchain/config/struct.StoredConfiguration.html
//!
//! # Examples
//!
//! ```rust,no_run
//! extern crate exonum;
//! extern crate exonum_configuration;
//!
//! use exonum::helpers::fabric::NodeBuilder;
//!
//! use exonum_configuration::ConfigurationServiceFactory;
//!
//! fn main() {
//!     exonum::helpers::init_logger().unwrap();
//!     NodeBuilder::new()
//!         .with_service(Box::new(ConfigurationServiceFactory))
//!         .run();
//! }
//! ```

#[macro_use]
extern crate exonum;
#[cfg(test)]
#[macro_use]
extern crate exonum_testkit;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
extern crate iron;
extern crate router;
extern crate bodyparser;
extern crate params;
#[macro_use]
extern crate lazy_static;

use router::Router;
use iron::Handler;
use exonum::api::Api;
use exonum::blockchain::{Service, Transaction, ApiContext};
use exonum::helpers::fabric::{ServiceFactory, Context};
use exonum::crypto::Hash;
use exonum::messages::RawTransaction;
use exonum::storage::Snapshot;
use exonum::encoding::{Error as EncodingError};

pub mod schema;
pub mod transactions;

mod api;
#[cfg(test)]
mod tests;

#[doc(no_inline)]
pub use schema::{ConfigurationSchema, ProposeData};
#[doc(no_inline)]
pub use transactions::{Propose, Vote};

/// Service identifier for the configuration service.
pub const CONFIGURATION_SERVICE_ID: u16 = 1;

/// Configuration service.
#[derive(Default)]
pub struct ConfigurationService {}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_name(&self) -> &'static str {
        "configuration"
    }

    fn service_id(&self) -> u16 {
        CONFIGURATION_SERVICE_ID
    }

    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash> {
        let schema = ConfigurationSchema::new(snapshot);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, EncodingError> {
        transactions::tx_from_raw(raw)
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PublicConfigApi { blockchain: ctx.blockchain().clone() };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PrivateConfigApi {
            channel: ctx.node_channel().clone(),
            config: (*ctx.public_key(), ctx.secret_key().clone()),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A configuration service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct ConfigurationServiceFactory;

impl ServiceFactory for ConfigurationServiceFactory {
    fn make_service(&mut self, _: &Context) -> Box<Service> {
        Box::new(ConfigurationService::new())
    }
}
<|endoftext|>


<commit_before>
use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::cmp;

#[derive(Debug, Eq)]
pub struct BaseN/*<T>*/{
    pub base: usize, // TODO: Why isn't base u8?  Want to limit to 255 right?
    pub vec: Vec</*T*/u8>
    // TODO: Make proper accessor functions for struct fields
}

impl BaseN {

    pub fn new(base: usize) -> BaseN/*<T>*/ {
        BaseN { base: base, vec: Vec::new() }
    }
    pub fn with_capacity(base: usize, capacity: usize) -> BaseN {
        BaseN { base: base, vec: Vec::with_capacity(capacity) }
    }
    pub fn with_existing(base: usize, vec: Vec<u8>) -> BaseN {
        BaseN { base: base, vec: vec }
    }
    pub fn from_usize(base: usize, val: usize) -> Result<BaseN, &'static str> {

        let mut new_vec:Vec<u8> = Vec::new();
        let mut val10: usize = val;
        loop {
            let _val = val10 / base;
            match _val {
                0 => {
                    new_vec.push(val10 as u8);
                    break;
                }
                _ => {
                    let remainder = val10 % base;
                    println!("{}", remainder);
                    new_vec.push(remainder as u8);
                    val10 = _val;
                }
            }
        }
        let new_basen: BaseN = BaseN { base: base, vec: new_vec };

        Ok(new_basen)
    }

    pub fn to_base(self, new_base: usize) -> Result<BaseN, &'static str> {

        //TODO: If base is same, just return existing??
        if self.base == new_base {
            return Ok(self);
        }

        // TODO: Create copy, convert mutable, or both?
        //let new_basen = BaseN::new(new_base);

        println!("converting base {} to base {}", self.base, new_base);
        let mut val10: usize = self.to_usize().unwrap();
        println!("{}", val10);

        BaseN::from_usize(new_base, val10)

    }
    pub fn to_usize(&self) -> Result<usize, &'static str> {
        let mut _val10: usize = 0;
        for (i, x) in self.vec.iter().enumerate() {
            _val10 = _val10 + (*x as usize) * self.base.pow(i as u32);
        }
        Ok(_val10)
    }

}

//impl Copy for BaseN { }

impl PartialEq for BaseN {
    fn eq(&self, other: &BaseN) -> bool {
        (self.vec == other.vec)
            && (self.base == other.base)
    }
}

impl Clone for BaseN {
    fn clone(&self) -> Self {
        BaseN { base: self.base, vec: self.vec.clone() }
    }

    fn clone_from(&mut self, source: &Self) {
        self.vec.clone_from(&source.vec);
        self.base = source.base;
    }
}

impl Add for BaseN {
    type Output = Result<BaseN, &'static str>;

    // Option<> because might overflow, bases incorrect, etc.
    // I suppose you could add a base16 to base10 with an internal convert
    fn add(self, _rhs: BaseN) -> Result<BaseN, &'static str> {

        let add_basen = match _rhs.base==self.base {
            true => _rhs,//.clone(),
            false => _rhs.to_base(self.base).unwrap()
                // TODO: Throw error?  Don't actually know
                // what base user wanted to end with
        };

        // TODO: Use zip() w/ carry array instead?
        let max = cmp::max(self.vec.len(), add_basen.vec.len());
        let mut new_vec: Vec<u8> = Vec::with_capacity(max);

        let mut carry: u8 = 0;

        for i in 0..max {
            match (self.vec.get(i), add_basen.vec.get(i)) {
                (Some(x), Some(y)) => {
                    let sum: u8 = *x + *y + carry;
                    carry = sum / (add_basen.base as u8);
                    new_vec.push(sum % (add_basen.base as u8));
                },
                (Some(x), None) => {
                    // no carry, but need to resize
                    let sum: u8 = *x + carry;
                    carry = sum / (add_basen.base as u8);
                    new_vec.push(sum % (add_basen.base as u8));
                },
                (_, _) => break // no ops needed

            }
        }

        let new_basen: BaseN = BaseN { base: add_basen.base, vec: new_vec };

        Ok(new_basen)
    }
}

<commit_msg>Comment out debug prints<commit_after>
use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::cmp;

#[derive(Debug, Eq)]
pub struct BaseN/*<T>*/{
    pub base: usize, // TODO: Why isn't base u8?  Want to limit to 255 right?
    pub vec: Vec</*T*/u8>
    // TODO: Make proper accessor functions for struct fields
}

impl BaseN {

    pub fn new(base: usize) -> BaseN/*<T>*/ {
        BaseN { base: base, vec: Vec::new() }
    }
    pub fn with_capacity(base: usize, capacity: usize) -> BaseN {
        BaseN { base: base, vec: Vec::with_capacity(capacity) }
    }
    pub fn with_existing(base: usize, vec: Vec<u8>) -> BaseN {
        BaseN { base: base, vec: vec }
    }
    pub fn from_usize(base: usize, val: usize) -> Result<BaseN, &'static str> {

        let mut new_vec:Vec<u8> = Vec::new();
        let mut val10: usize = val;
        loop {
            let _val = val10 / base;
            match _val {
                0 => {
                    new_vec.push(val10 as u8);
                    break;
                }
                _ => {
                    let remainder = val10 % base;
                    //println!("{}", remainder);
                    new_vec.push(remainder as u8);
                    val10 = _val;
                }
            }
        }
        let new_basen: BaseN = BaseN { base: base, vec: new_vec };

        Ok(new_basen)
    }

    pub fn to_base(self, new_base: usize) -> Result<BaseN, &'static str> {

        //TODO: If base is same, just return existing??
        if self.base == new_base {
            return Ok(self);
        }

        // TODO: Create copy, convert mutable, or both?
        //let new_basen = BaseN::new(new_base);

        //println!("converting base {} to base {}", self.base, new_base);
        let mut val10: usize = self.to_usize().unwrap();
        //println!("{}", val10);

        BaseN::from_usize(new_base, val10)

    }
    pub fn to_usize(&self) -> Result<usize, &'static str> {
        let mut _val10: usize = 0;
        for (i, x) in self.vec.iter().enumerate() {
            _val10 = _val10 + (*x as usize) * self.base.pow(i as u32);
        }
        Ok(_val10)
    }

}

//impl Copy for BaseN { }

impl PartialEq for BaseN {
    fn eq(&self, other: &BaseN) -> bool {
        (self.vec == other.vec)
            && (self.base == other.base)
    }
}

impl Clone for BaseN {
    fn clone(&self) -> Self {
        BaseN { base: self.base, vec: self.vec.clone() }
    }

    fn clone_from(&mut self, source: &Self) {
        self.vec.clone_from(&source.vec);
        self.base = source.base;
    }
}

impl Add for BaseN {
    type Output = Result<BaseN, &'static str>;

    // Option<> because might overflow, bases incorrect, etc.
    // I suppose you could add a base16 to base10 with an internal convert
    fn add(self, _rhs: BaseN) -> Result<BaseN, &'static str> {

        let add_basen = match _rhs.base==self.base {
            true => _rhs,//.clone(),
            false => _rhs.to_base(self.base).unwrap()
                // TODO: Throw error?  Don't actually know
                // what base user wanted to end with
        };

        // TODO: Use zip() w/ carry array instead?
        let max = cmp::max(self.vec.len(), add_basen.vec.len());
        let mut new_vec: Vec<u8> = Vec::with_capacity(max);

        let mut carry: u8 = 0;

        for i in 0..max {
            match (self.vec.get(i), add_basen.vec.get(i)) {
                (Some(x), Some(y)) => {
                    let sum: u8 = *x + *y + carry;
                    carry = sum / (add_basen.base as u8);
                    new_vec.push(sum % (add_basen.base as u8));
                },
                (Some(x), None) => {
                    // no carry, but need to resize
                    let sum: u8 = *x + carry;
                    carry = sum / (add_basen.base as u8);
                    new_vec.push(sum % (add_basen.base as u8));
                },
                (_, _) => break // no ops needed

            }
        }

        let new_basen: BaseN = BaseN { base: add_basen.base, vec: new_vec };

        Ok(new_basen)
    }
}

<|endoftext|>


<commit_before>extern crate lz4;

use std::os;
use std::old_io::fs;
use std::old_io::{File, IoError, IoErrorKind, Reader, Writer, USER_RWX};
use std::old_io::process::{ProcessOutput, ProcessExit};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{Hasher, SipHasher};

use super::utils::hash_write_stream;
use super::utils::DEFAULT_BUF_SIZE;

const HEADER: &'static [u8] = b"OBCF\x00\x01";
const FOOTER: &'static [u8] = b"END\x00";

struct FileHash {
	hash: String,
	size: u64,
	modified: u64,
}

#[derive(Clone)]
pub struct Cache {
	file_hash: Arc<Mutex<HashMap<Path, Arc<Mutex<Option<FileHash>>>>>>,
	cache_dir: Path
}

impl Cache {
	pub fn new() -> Self {
		let cache_dir = match os::getenv("OCTOBUILD_CACHE") {
			Some(value) => Path::new(value),
			None => os::homedir().unwrap().join_many(&[".octobuild", "cache"])
		};
		Cache {
			file_hash: Arc::new(Mutex::new(HashMap::new())),
			cache_dir: cache_dir
		}
	}

	pub fn run_cached<F: Fn()->Result<ProcessOutput, IoError>>(&self, params: &str, inputs: &Vec<Path>, outputs: &Vec<Path>, worker: F) -> Result<ProcessOutput, IoError> {
		let hash = try! (self.generate_hash(params, inputs));
		let path = self.cache_dir.join(&hash[0..2]).join(&hash[2..4]).join(&(hash[4..].to_string() + ".lz4"));
		// Try to read data from cache.
		match read_cache(&path, outputs) {
			Ok(output) => {return Ok(output)}
			Err(_) => {}
		}
		// Run task and save result to cache.
		let output = try !(worker());
		try !(write_cache(&path, outputs, &output));
		Ok(output)
	}

	fn generate_hash(&self, params: &str, inputs: &Vec<Path>) -> Result<String, IoError> {
		let mut sip_hash = SipHasher::new();
		let hash: &mut Hasher = &mut sip_hash;
		// str
		hash.write(params.as_bytes());
		hash.write_u8(0);
		// inputs
		for input in inputs.iter() {
			let file_hash = try! (self.get_file_hash(input));
			hash.write(file_hash.as_bytes());
		}
		Ok(format!("{:016x}", hash.finish()))
	}

	pub fn get_file_hash(&self, path: &Path) -> Result<String, IoError> {
		// Get/create lock for file entry.
		let hash_lock = match self.file_hash.lock() {
			Ok(mut map) => {
				match map.entry(path.clone()) {
					Entry::Occupied(entry) => entry.get().clone(),
					Entry::Vacant(entry) => entry.insert(Arc::new(Mutex::new(None))).clone()
				}
			}
			Err(e) => {
				return Err(IoError {
					kind: IoErrorKind::OtherIoError,
					desc: "Mutex error",
					detail: Some(e.to_string())
				});
			}
		};
		// Get file hash.
		let result = match hash_lock.lock() { // rust: #22722
			Ok(mut hash_entry) => {
				// Validate entry, if exists.
				match *hash_entry {
					Some(ref value) => {
						let stat = try! (fs::stat(path));
						if value.size == stat.size && value.modified == stat.modified {
							return Ok(value.hash.clone());
						}
					}
					None => {}
				}
				// Calculate hash value.
				let stat = try! (fs::stat(path));
				let hash = try! (generate_file_hash(path));
				*hash_entry = Some(FileHash {
					hash: hash.clone(),
					size: stat.size,
					modified: stat.modified,
				});
				Ok(hash)
			}
			Err(e) => Err(IoError {
				kind: IoErrorKind::OtherIoError,
				desc: "Mutex error",
				detail: Some(e.to_string())
			})
		};
		result
	}
}

fn generate_file_hash(path: &Path) -> Result<String, IoError> {
	let mut hash = SipHasher::new();
	let mut file = try! (File::open(path));
	try! (hash_write_stream(&mut hash, &mut file));
	Ok(format!("{:016x}", hash.result()))
}

fn write_cache(path: &Path, paths: &Vec<Path>, output: &ProcessOutput) -> Result<(), IoError> {
	if !output.status.success() {
		return Ok(());
	}
	try! (fs::mkdir_recursive(&path.dir_path(), USER_RWX));
	let mut stream = try! (lz4::Encoder::new(try! (File::create(path)), 1));
	try! (stream.write_all(HEADER));
	try! (stream.write_le_uint(paths.len()));
	let mut buf: [u8; DEFAULT_BUF_SIZE] = [0; DEFAULT_BUF_SIZE];
	for path in paths.iter() {
		let mut file = try! (File::open(path));
		loop {
			match file.read(&mut buf) {
				Ok(size) => {
					try! (stream.write_le_uint(size));
					try! (stream.write_all(&buf.as_slice()[0..size]));
				}
				Err(ref e) if e.kind == IoErrorKind::EndOfFile => break,
				Err(e) => return Err(e)
			}
		}
		try! (stream.write_le_uint(0));
	}
	try! (write_output(&mut stream, output));
	try! (stream.write_all(FOOTER));
	match stream.finish() {
		(_, result) => result
	}
}

fn read_cache(path: &Path, paths: &Vec<Path>) -> Result<ProcessOutput, IoError> {
	let mut stream = try! (lz4::Decoder::new (try! (File::open(path))));
	if try! (stream.read_exact(HEADER.len())) != HEADER {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Invalid cache file header",
			detail: Some(path.display().to_string())
		})
	}
	if try! (stream.read_le_uint()) != paths.len() {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Unexpected count of packed cached files",
			detail: Some(path.display().to_string())
		})
	}
	for path in paths.iter() {
		let mut file = try! (File::create(path));
		loop {
			let size = try! (stream.read_le_uint());
			if size == 0 {break;}
			let block = try! (stream.read_exact(size));
			try! (file.write_all(block.as_slice()));
		}
	}
	let output = try! (read_output(&mut stream));
	if try! (stream.read_exact(FOOTER.len())) != FOOTER {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Invalid cache file footer",
			detail: Some(path.display().to_string())
		})
	}
	Ok(output)
}

fn write_blob(stream: &mut Writer, blob: &[u8]) -> Result<(), IoError> {
	try! (stream.write_le_uint(blob.len()));
	try! (stream.write_all(blob));
	Ok(())
}

fn read_blob(stream: &mut Reader) -> Result<Vec<u8>, IoError> {
	let size = try! (stream.read_le_uint());
	stream.read_exact(size)
}

fn write_output(stream: &mut Writer, output: &ProcessOutput) -> Result<(), IoError> {
	try! (write_blob(stream, output.output.as_slice()));
	try! (write_blob(stream, output.error.as_slice()));
	Ok(())
}

fn read_output(stream: &mut Reader) -> Result<ProcessOutput, IoError> {
	let output = try! (read_blob(stream));
	let error  = try! (read_blob(stream));
	Ok(ProcessOutput {
		status: ProcessExit::ExitStatus(0),
		output: output,
		error: error,
	})
}
<commit_msg>Change directory cache depth.<commit_after>extern crate lz4;

use std::os;
use std::old_io::fs;
use std::old_io::{File, IoError, IoErrorKind, Reader, Writer, USER_RWX};
use std::old_io::process::{ProcessOutput, ProcessExit};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{Hasher, SipHasher};

use super::utils::hash_write_stream;
use super::utils::DEFAULT_BUF_SIZE;

const HEADER: &'static [u8] = b"OBCF\x00\x01";
const FOOTER: &'static [u8] = b"END\x00";

struct FileHash {
	hash: String,
	size: u64,
	modified: u64,
}

#[derive(Clone)]
pub struct Cache {
	file_hash: Arc<Mutex<HashMap<Path, Arc<Mutex<Option<FileHash>>>>>>,
	cache_dir: Path
}

impl Cache {
	pub fn new() -> Self {
		let cache_dir = match os::getenv("OCTOBUILD_CACHE") {
			Some(value) => Path::new(value),
			None => os::homedir().unwrap().join_many(&[".octobuild", "cache"])
		};
		Cache {
			file_hash: Arc::new(Mutex::new(HashMap::new())),
			cache_dir: cache_dir
		}
	}

	pub fn run_cached<F: Fn()->Result<ProcessOutput, IoError>>(&self, params: &str, inputs: &Vec<Path>, outputs: &Vec<Path>, worker: F) -> Result<ProcessOutput, IoError> {
		let hash = try! (self.generate_hash(params, inputs));
		let path = self.cache_dir.join(&hash[0..2]).join(&(hash[2..].to_string() + ".lz4"));
		// Try to read data from cache.
		match read_cache(&path, outputs) {
			Ok(output) => {return Ok(output)}
			Err(_) => {}
		}
		// Run task and save result to cache.
		let output = try !(worker());
		try !(write_cache(&path, outputs, &output));
		Ok(output)
	}

	fn generate_hash(&self, params: &str, inputs: &Vec<Path>) -> Result<String, IoError> {
		let mut sip_hash = SipHasher::new();
		let hash: &mut Hasher = &mut sip_hash;
		// str
		hash.write(params.as_bytes());
		hash.write_u8(0);
		// inputs
		for input in inputs.iter() {
			let file_hash = try! (self.get_file_hash(input));
			hash.write(file_hash.as_bytes());
		}
		Ok(format!("{:016x}", hash.finish()))
	}

	pub fn get_file_hash(&self, path: &Path) -> Result<String, IoError> {
		// Get/create lock for file entry.
		let hash_lock = match self.file_hash.lock() {
			Ok(mut map) => {
				match map.entry(path.clone()) {
					Entry::Occupied(entry) => entry.get().clone(),
					Entry::Vacant(entry) => entry.insert(Arc::new(Mutex::new(None))).clone()
				}
			}
			Err(e) => {
				return Err(IoError {
					kind: IoErrorKind::OtherIoError,
					desc: "Mutex error",
					detail: Some(e.to_string())
				});
			}
		};
		// Get file hash.
		let result = match hash_lock.lock() { // rust: #22722
			Ok(mut hash_entry) => {
				// Validate entry, if exists.
				match *hash_entry {
					Some(ref value) => {
						let stat = try! (fs::stat(path));
						if value.size == stat.size && value.modified == stat.modified {
							return Ok(value.hash.clone());
						}
					}
					None => {}
				}
				// Calculate hash value.
				let stat = try! (fs::stat(path));
				let hash = try! (generate_file_hash(path));
				*hash_entry = Some(FileHash {
					hash: hash.clone(),
					size: stat.size,
					modified: stat.modified,
				});
				Ok(hash)
			}
			Err(e) => Err(IoError {
				kind: IoErrorKind::OtherIoError,
				desc: "Mutex error",
				detail: Some(e.to_string())
			})
		};
		result
	}
}

fn generate_file_hash(path: &Path) -> Result<String, IoError> {
	let mut hash = SipHasher::new();
	let mut file = try! (File::open(path));
	try! (hash_write_stream(&mut hash, &mut file));
	Ok(format!("{:016x}", hash.result()))
}

fn write_cache(path: &Path, paths: &Vec<Path>, output: &ProcessOutput) -> Result<(), IoError> {
	if !output.status.success() {
		return Ok(());
	}
	try! (fs::mkdir_recursive(&path.dir_path(), USER_RWX));
	let mut stream = try! (lz4::Encoder::new(try! (File::create(path)), 1));
	try! (stream.write_all(HEADER));
	try! (stream.write_le_uint(paths.len()));
	let mut buf: [u8; DEFAULT_BUF_SIZE] = [0; DEFAULT_BUF_SIZE];
	for path in paths.iter() {
		let mut file = try! (File::open(path));
		loop {
			match file.read(&mut buf) {
				Ok(size) => {
					try! (stream.write_le_uint(size));
					try! (stream.write_all(&buf.as_slice()[0..size]));
				}
				Err(ref e) if e.kind == IoErrorKind::EndOfFile => break,
				Err(e) => return Err(e)
			}
		}
		try! (stream.write_le_uint(0));
	}
	try! (write_output(&mut stream, output));
	try! (stream.write_all(FOOTER));
	match stream.finish() {
		(_, result) => result
	}
}

fn read_cache(path: &Path, paths: &Vec<Path>) -> Result<ProcessOutput, IoError> {
	let mut stream = try! (lz4::Decoder::new (try! (File::open(path))));
	if try! (stream.read_exact(HEADER.len())) != HEADER {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Invalid cache file header",
			detail: Some(path.display().to_string())
		})
	}
	if try! (stream.read_le_uint()) != paths.len() {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Unexpected count of packed cached files",
			detail: Some(path.display().to_string())
		})
	}
	for path in paths.iter() {
		let mut file = try! (File::create(path));
		loop {
			let size = try! (stream.read_le_uint());
			if size == 0 {break;}
			let block = try! (stream.read_exact(size));
			try! (file.write_all(block.as_slice()));
		}
	}
	let output = try! (read_output(&mut stream));
	if try! (stream.read_exact(FOOTER.len())) != FOOTER {
		return Err(IoError {
			kind: IoErrorKind::InvalidInput,
			desc: "Invalid cache file footer",
			detail: Some(path.display().to_string())
		})
	}
	Ok(output)
}

fn write_blob(stream: &mut Writer, blob: &[u8]) -> Result<(), IoError> {
	try! (stream.write_le_uint(blob.len()));
	try! (stream.write_all(blob));
	Ok(())
}

fn read_blob(stream: &mut Reader) -> Result<Vec<u8>, IoError> {
	let size = try! (stream.read_le_uint());
	stream.read_exact(size)
}

fn write_output(stream: &mut Writer, output: &ProcessOutput) -> Result<(), IoError> {
	try! (write_blob(stream, output.output.as_slice()));
	try! (write_blob(stream, output.error.as_slice()));
	Ok(())
}

fn read_output(stream: &mut Reader) -> Result<ProcessOutput, IoError> {
	let output = try! (read_blob(stream));
	let error  = try! (read_blob(stream));
	Ok(ProcessOutput {
		status: ProcessExit::ExitStatus(0),
		output: output,
		error: error,
	})
}
<|endoftext|>