<commit_before>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.kind().skip_binder() {
                ty::PredicateKind::RegionOutlives(_)
                | ty::PredicateKind::TypeOutlives(_)
                | ty::PredicateKind::WellFormed(_)
                | ty::PredicateKind::Projection(_)
                | ty::PredicateKind::ConstEvaluatable(..)
                | ty::PredicateKind::ConstEquate(..)
                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateKind::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        },

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<commit_msg>Update match branches<commit_after>use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
    Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
    TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
    let def_id = body.source.def_id();
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate.kind().skip_binder() {
                ty::PredicateKind::RegionOutlives(_)
                | ty::PredicateKind::TypeOutlives(_)
                | ty::PredicateKind::WellFormed(_)
                | ty::PredicateKind::Projection(_)
                | ty::PredicateKind::ConstEvaluatable(..)
                | ty::PredicateKind::ConstEquate(..)
                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
                ty::PredicateKind::Trait(pred, _) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.self_ty().kind() {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                },
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
    for arg in ty.walk() {
        let ty = match arg.unpack() {
            GenericArgKind::Type(ty) => ty,

            // No constraints on lifetimes or constants, except potentially
            // constants' types, but `walk` will get to them as well.
            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
        };

        match ty.kind() {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                return Err((span, "mutable references in const fn are unstable".into()));
            },
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                return Err((span, "function pointers in const fn are unstable".into()));
            },
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        },
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        },
                    }
                }
            },
            _ => {},
        }
    }
    Ok(())
}

fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
    match rvalue {
        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
            check_place(tcx, *place, span, body)
        },
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc_middle::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                },
                _ => check_operand(tcx, operand, span, body),
            }
        },
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, body)
        },
        Rvalue::Cast(
            CastKind::Pointer(
                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
            ),
            _,
            _,
        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
                deref_ty.ty
            } else {
                // We cannot allow this for now.
                return Err((span, "unsizing casts are only allowed for references right now".into()));
            };
            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                check_operand(tcx, op, span, body)?;
                // Casting/coercing things to slices is fine.
                Ok(())
            } else {
                // We just can't allow trait objects until we have figured out trait method calls.
                Err((span, "unsizing casts are not allowed in const fn".into()))
            }
        },
        // binops are fine on integers
        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
            check_operand(tcx, lhs, span, body)?;
            check_operand(tcx, rhs, span, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        },
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        },
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, body)?;
            }
            Ok(())
        },
    }
}

fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, *place, span, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        StatementKind::FakeRead(_, place) |
        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),

        StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),

        StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping{
          dst, src, size,
        }) => {
          check_operand(tcx, dst, span, body)?;
          check_operand(tcx, src, span, body)?;
          check_operand(tcx, size, span, body)
        },
        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Coverage(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let [ref proj_base @ .., elem] = *cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        return Err((span, "accessing union fields is unstable".into()));
                    }
                }
            },
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Downcast(..)
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {},
        }
    }

    Ok(())
}

fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdge { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume
        | TerminatorKind::Unreachable => Ok(()),

        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
        TerminatorKind::DropAndReplace { place, value, .. } => {
            check_place(tcx, *place, span, body)?;
            check_operand(tcx, value, span, body)
        },

        TerminatorKind::SwitchInt {
            discr,
            switch_ty: _,
            targets: _,
        } => check_operand(tcx, discr, span, body),

        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        },

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
            fn_span: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
                if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                // HACK: This is to "unstabilize" the `transmute` intrinsic
                // within const fns. `transmute` is allowed in all other const contexts.
                // This won't really scale to more intrinsics or functions. Let's allow const
                // transmutes in const fn before we add more hacks to this.
                if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
                    return Err((
                        span,
                        "can only call `transmute` from const items, not `const fn`".into(),
                    ));
                }

                check_operand(tcx, func, span, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        },

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, body),

        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
    }
}
<|endoftext|>


<commit_before>//! ARM C Language Extensions (ACLE)
//!
//! # Developer notes
//!
//! Below is a list of built-in targets that are representative of the different ARM
//! architectures; the list includes the `target_feature`s they possess.
//!
//! - `armv4t-unknown-linux-gnueabi` - **ARMv4** - `+v4t`
//! - `armv5te-unknown-linux-gnueabi` - **ARMv5TE** - `+v4t +v5te`
//! - `arm-unknown-linux-gnueabi` - **ARMv6** - `+v4t +v5te +v6`
//! - `thumbv6m-none-eabi` - **ARMv6-M** - `+v4t +v5te +v6 +thumb-mode +mclass`
//! - `armv7-unknown-linux-gnueabihf` - **ARMv7-A** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +dsp +thumb2 +aclass`
//! - `armv7r-none-eabi` - **ARMv7-R** - `+v4t +v5te +v6 +v6k +v6t2  +v7 +dsp +thumb2 +rclass`
//! - `thumbv7m-none-eabi` - **ARMv7-M** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +thumb2 +thumb-mode +mclass`
//! - `thumbv7em-none-eabi` - **ARMv7E-M** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +dsp +thumb2 +thumb-mode +mclass`
//! - `aarch64-unknown-linux-gnu` - **ARMv8-A (AArch64)** - `+fp +neon`
//!
//! Section 10.1 of ACLE says:
//!
//! - "In the sequence of Arm architectures { v5, v5TE, v6, v6T2, v7 } each architecture includes
//! its predecessor instruction set."
//!
//! - "In the sequence of Thumb-only architectures { v6-M, v7-M, v7E-M } each architecture includes
//! its predecessor instruction set."
//!
//! From that info and from looking at how LLVM features work (using custom targets) we can identify
//! features that are subsets of others:
//!
//! Legend: `a < b` reads as "`a` is a subset of `b`"; this means that if `b` is enabled then `a` is
//! enabled as well.
//!
//! - `v4t < v5te < v6 < v6k < v6t2 < v7 < v8`
//! - `v6 < v8m < v6t2`
//! - `v7 < v8m.main`
//!
//! # References
//!
//! - [ACLE Q2 2018](https://developer.arm.com/docs/101028/latest)

// Supported arches: 8, 7, 6-M. See Section 10.1 of ACLE (e.g. DMB)
// But this is further refined within the module
#[cfg(any(
    // v8
    target_arch = "aarch64",
    // v7
    target_feature = "v7",
    // v6-M
    target_feature = "mclass"
))]
mod barrier;

#[cfg(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
))]
pub use self::barrier::*;

mod hints;

pub use self::hints::*;

mod registers;

pub use self::registers::*;

// Supported arches: 5TE, 7E-M. See Section 10.1 of ACLE (e.g. QADD)
// But we also exclude the A profile because DSP is deprecated on that profile as of ACLE 2.0 (see
// section 5.4.7)
#[cfg(any(
    // >= v5TE but excludes v7-A
    all(target_feature = "v5te", not(target_feature = "mclass"), not(target_feature = "aclass")),
    // v7E-M
    all(target_feature = "mclass", target_feature = "dsp"),
))]
mod dsp;

#[cfg(any(
    all(
        target_feature = "v5te",
        not(target_feature = "mclass"),
        not(target_feature = "aclass")
    ),
    all(target_feature = "mclass", target_feature = "dsp"),
))]
pub use self::dsp::*;

// Deprecated in ACLE 2.0 for the A profile but fully supported on the M and R profiles, says
// Section 5.4.9 of ACLE. We'll expose these for the A profile even if deprecated
#[cfg(any(
    // v7-A, v7-R
    all(target_feature = "v6", not(target_feature = "mclass")),
    // v7E-M
    all(target_feature = "mclass", target_feature = "dsp")
))]
mod simd32;

#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")),
    all(target_feature = "mclass", target_feature = "dsp")
))]
pub use self::simd32::*;

mod sealed {
    pub trait Dmb {
        unsafe fn __dmb(&self);
    }

    pub trait Dsb {
        unsafe fn __dsb(&self);
    }

    pub trait Isb {
        unsafe fn __isb(&self);
    }

    pub trait Rsr {
        unsafe fn __rsr(&self) -> u32;
    }

    pub trait Rsr64 {
        unsafe fn __rsr64(&self) -> u64;
    }

    pub trait Rsrp {
        unsafe fn __rsrp(&self) -> *const u8;
    }

    pub trait Wsr {
        unsafe fn __wsr(&self, value: u32);
    }

    pub trait Wsr64 {
        unsafe fn __wsr64(&self, value: u64);
    }

    pub trait Wsrp {
        unsafe fn __wsrp(&self, value: *const u8);
    }
}
<commit_msg>acle/dsp: make available on the A profile<commit_after>//! ARM C Language Extensions (ACLE)
//!
//! # Developer notes
//!
//! Below is a list of built-in targets that are representative of the different ARM
//! architectures; the list includes the `target_feature`s they possess.
//!
//! - `armv4t-unknown-linux-gnueabi` - **ARMv4** - `+v4t`
//! - `armv5te-unknown-linux-gnueabi` - **ARMv5TE** - `+v4t +v5te`
//! - `arm-unknown-linux-gnueabi` - **ARMv6** - `+v4t +v5te +v6`
//! - `thumbv6m-none-eabi` - **ARMv6-M** - `+v4t +v5te +v6 +thumb-mode +mclass`
//! - `armv7-unknown-linux-gnueabihf` - **ARMv7-A** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +dsp +thumb2 +aclass`
//! - `armv7r-none-eabi` - **ARMv7-R** - `+v4t +v5te +v6 +v6k +v6t2  +v7 +dsp +thumb2 +rclass`
//! - `thumbv7m-none-eabi` - **ARMv7-M** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +thumb2 +thumb-mode +mclass`
//! - `thumbv7em-none-eabi` - **ARMv7E-M** - `+v4t +v5te +v6 +v6k +v6t2 +v7 +dsp +thumb2 +thumb-mode +mclass`
//! - `aarch64-unknown-linux-gnu` - **ARMv8-A (AArch64)** - `+fp +neon`
//!
//! Section 10.1 of ACLE says:
//!
//! - "In the sequence of Arm architectures { v5, v5TE, v6, v6T2, v7 } each architecture includes
//! its predecessor instruction set."
//!
//! - "In the sequence of Thumb-only architectures { v6-M, v7-M, v7E-M } each architecture includes
//! its predecessor instruction set."
//!
//! From that info and from looking at how LLVM features work (using custom targets) we can identify
//! features that are subsets of others:
//!
//! Legend: `a < b` reads as "`a` is a subset of `b`"; this means that if `b` is enabled then `a` is
//! enabled as well.
//!
//! - `v4t < v5te < v6 < v6k < v6t2 < v7 < v8`
//! - `v6 < v8m < v6t2`
//! - `v7 < v8m.main`
//!
//! # References
//!
//! - [ACLE Q2 2018](https://developer.arm.com/docs/101028/latest)

// Supported arches: 8, 7, 6-M. See Section 10.1 of ACLE (e.g. DMB)
// But this is further refined within the module
#[cfg(any(
    // v8
    target_arch = "aarch64",
    // v7
    target_feature = "v7",
    // v6-M
    target_feature = "mclass"
))]
mod barrier;

#[cfg(any(
    target_arch = "aarch64",
    target_feature = "v7",
    target_feature = "mclass"
))]
pub use self::barrier::*;

mod hints;

pub use self::hints::*;

mod registers;

pub use self::registers::*;

// Supported arches: 5TE, 7E-M. See Section 10.1 of ACLE (e.g. QADD)
// We also include the A profile even though DSP is deprecated on that profile as of ACLE 2.0 (see
// section 5.4.7)
#[cfg(any(
    // >= v5TE but excludes v7-A
    all(target_feature = "v5te", not(target_feature = "mclass")),
    // v7E-M
    all(target_feature = "mclass", target_feature = "dsp"),
))]
mod dsp;

#[cfg(any(
    all(target_feature = "v5te", not(target_feature = "mclass")),
    all(target_feature = "mclass", target_feature = "dsp"),
))]
pub use self::dsp::*;

// Deprecated in ACLE 2.0 for the A profile but fully supported on the M and R profiles, says
// Section 5.4.9 of ACLE. We'll expose these for the A profile even if deprecated
#[cfg(any(
    // v7-A, v7-R
    all(target_feature = "v6", not(target_feature = "mclass")),
    // v7E-M
    all(target_feature = "mclass", target_feature = "dsp")
))]
mod simd32;

#[cfg(any(
    all(target_feature = "v6", not(target_feature = "mclass")),
    all(target_feature = "mclass", target_feature = "dsp")
))]
pub use self::simd32::*;

mod sealed {
    pub trait Dmb {
        unsafe fn __dmb(&self);
    }

    pub trait Dsb {
        unsafe fn __dsb(&self);
    }

    pub trait Isb {
        unsafe fn __isb(&self);
    }

    pub trait Rsr {
        unsafe fn __rsr(&self) -> u32;
    }

    pub trait Rsr64 {
        unsafe fn __rsr64(&self) -> u64;
    }

    pub trait Rsrp {
        unsafe fn __rsrp(&self) -> *const u8;
    }

    pub trait Wsr {
        unsafe fn __wsr(&self, value: u32);
    }

    pub trait Wsr64 {
        unsafe fn __wsr64(&self, value: u64);
    }

    pub trait Wsrp {
        unsafe fn __wsrp(&self, value: *const u8);
    }
}
<|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 https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::*;
use crate::ConstellationCanvasMsg;
use canvas_traits::canvas::*;
use crossbeam_channel::{select, unbounded, Sender};
use euclid::default::Size2D;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::thread;

pub enum AntialiasMode {
    Default,
    None,
}

pub struct CanvasPaintThread<'a> {
    canvases: HashMap<CanvasId, CanvasData<'a>>,
    next_canvas_id: CanvasId,
}

impl<'a> CanvasPaintThread<'a> {
    fn new() -> CanvasPaintThread<'a> {
        CanvasPaintThread {
            canvases: HashMap::new(),
            next_canvas_id: CanvasId(0),
        }
    }

    /// Creates a new `CanvasPaintThread` and returns an `IpcSender` to
    /// communicate with it.
    pub fn start() -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) {
        let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap();
        let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
        let (create_sender, create_receiver) = unbounded();
        thread::Builder::new()
            .name("CanvasThread".to_owned())
            .spawn(move || {
                let mut canvas_paint_thread = CanvasPaintThread::new();
                loop {
                    select! {
                        recv(msg_receiver) -> msg => {
                            match msg {
                                Ok(CanvasMsg::Canvas2d(message, canvas_id)) => {
                                    canvas_paint_thread.process_canvas_2d_message(message, canvas_id);
                                },
                                Ok(CanvasMsg::Close(canvas_id)) => {
                                    canvas_paint_thread.canvases.remove(&canvas_id);
                                },
                                Ok(CanvasMsg::Recreate(size, canvas_id)) => {
                                    canvas_paint_thread.canvas(canvas_id).recreate(size);
                                },
                                Ok(CanvasMsg::FromScript(message, canvas_id)) => match message {
                                    FromScriptMsg::SendPixels(chan) => {
                                        canvas_paint_thread.canvas(canvas_id).send_pixels(chan);
                                    },
                                },
                                Ok(CanvasMsg::FromLayout(message, canvas_id)) => match message {
                                    FromLayoutMsg::SendData(chan) => {
                                        canvas_paint_thread.canvas(canvas_id).send_data(chan);
                                    },
                                },
                                Err(e) => {
                                    warn!("Error on CanvasPaintThread receive ({})", e);
                                },
                            }
                        }
                        recv(create_receiver) -> msg => {
                            match msg {
                                Ok(ConstellationCanvasMsg::Create {
                                    id_sender: creator,
                                    size,
                                    webrender_sender: webrenderer_api_sender,
                                    antialias
                                }) => {
                                    let canvas_id = canvas_paint_thread.create_canvas(
                                        size,
                                        webrenderer_api_sender,
                                        antialias,
                                    );
                                    creator.send(canvas_id).unwrap();
                                },
                                Ok(ConstellationCanvasMsg::Exit) => break,
                                Err(e) => {
                                    warn!("Error on CanvasPaintThread receive ({})", e);
                                },
                            }
                        }
                    }
                }
            })
            .expect("Thread spawning failed");

        (create_sender, ipc_sender)
    }

    pub fn create_canvas(
        &mut self,
        size: Size2D<u64>,
        webrender_api_sender: webrender_api::RenderApiSender,
        antialias: bool,
    ) -> CanvasId {
        let antialias = if antialias {
            AntialiasMode::Default
        } else {
            AntialiasMode::None
        };

        let canvas_id = self.next_canvas_id.clone();
        self.next_canvas_id.0 += 1;

        let canvas_data = CanvasData::new(size, webrender_api_sender, antialias, canvas_id.clone());
        self.canvases.insert(canvas_id.clone(), canvas_data);

        canvas_id
    }

    fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {
        match message {
            Canvas2dMsg::FillText(text, x, y, max_width, style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill_text(text, x, y, max_width);
            },
            Canvas2dMsg::FillRect(rect, style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill_rect(&rect);
            },
            Canvas2dMsg::StrokeRect(rect, style) => {
                self.canvas(canvas_id).set_stroke_style(style);
                self.canvas(canvas_id).stroke_rect(&rect);
            },
            Canvas2dMsg::ClearRect(ref rect) => self.canvas(canvas_id).clear_rect(rect),
            Canvas2dMsg::BeginPath => self.canvas(canvas_id).begin_path(),
            Canvas2dMsg::ClosePath => self.canvas(canvas_id).close_path(),
            Canvas2dMsg::Fill(style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill();
            },
            Canvas2dMsg::Stroke(style) => {
                self.canvas(canvas_id).set_stroke_style(style);
                self.canvas(canvas_id).stroke();
            },
            Canvas2dMsg::Clip => self.canvas(canvas_id).clip(),
            Canvas2dMsg::IsPointInPath(x, y, fill_rule, chan) => self
                .canvas(canvas_id)
                .is_point_in_path(x, y, fill_rule, chan),
            Canvas2dMsg::DrawImage(
                imagedata,
                image_size,
                dest_rect,
                source_rect,
                smoothing_enabled,
            ) => {
                let data = imagedata.map_or_else(
                    || vec![0; image_size.width as usize * image_size.height as usize * 4],
                    |bytes| bytes.into_vec(),
                );
                self.canvas(canvas_id).draw_image(
                    data,
                    image_size,
                    dest_rect,
                    source_rect,
                    smoothing_enabled,
                )
            },
            Canvas2dMsg::DrawImageInOther(
                other_canvas_id,
                image_size,
                dest_rect,
                source_rect,
                smoothing,
            ) => {
                let image_data = self
                    .canvas(canvas_id)
                    .read_pixels(source_rect.to_u64(), image_size.to_u64());
                self.canvas(other_canvas_id).draw_image(
                    image_data.into(),
                    source_rect.size,
                    dest_rect,
                    source_rect,
                    smoothing,
                );
            },
            Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point),
            Canvas2dMsg::LineTo(ref point) => self.canvas(canvas_id).line_to(point),
            Canvas2dMsg::Rect(ref rect) => self.canvas(canvas_id).rect(rect),
            Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => {
                self.canvas(canvas_id).quadratic_curve_to(cp, pt)
            },
            Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => {
                self.canvas(canvas_id).bezier_curve_to(cp1, cp2, pt)
            },
            Canvas2dMsg::Arc(ref center, radius, start, end, ccw) => {
                self.canvas(canvas_id).arc(center, radius, start, end, ccw)
            },
            Canvas2dMsg::ArcTo(ref cp1, ref cp2, radius) => {
                self.canvas(canvas_id).arc_to(cp1, cp2, radius)
            },
            Canvas2dMsg::Ellipse(ref center, radius_x, radius_y, rotation, start, end, ccw) => self
                .canvas(canvas_id)
                .ellipse(center, radius_x, radius_y, rotation, start, end, ccw),
            Canvas2dMsg::RestoreContext => self.canvas(canvas_id).restore_context_state(),
            Canvas2dMsg::SaveContext => self.canvas(canvas_id).save_context_state(),
            Canvas2dMsg::SetLineWidth(width) => self.canvas(canvas_id).set_line_width(width),
            Canvas2dMsg::SetLineCap(cap) => self.canvas(canvas_id).set_line_cap(cap),
            Canvas2dMsg::SetLineJoin(join) => self.canvas(canvas_id).set_line_join(join),
            Canvas2dMsg::SetMiterLimit(limit) => self.canvas(canvas_id).set_miter_limit(limit),
            Canvas2dMsg::SetTransform(ref matrix) => self.canvas(canvas_id).set_transform(matrix),
            Canvas2dMsg::SetGlobalAlpha(alpha) => self.canvas(canvas_id).set_global_alpha(alpha),
            Canvas2dMsg::SetGlobalComposition(op) => {
                self.canvas(canvas_id).set_global_composition(op)
            },
            Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender) => {
                let pixels = self.canvas(canvas_id).read_pixels(dest_rect, canvas_size);
                sender.send(&pixels).unwrap();
            },
            Canvas2dMsg::PutImageData(rect, receiver) => {
                self.canvas(canvas_id)
                    .put_image_data(receiver.recv().unwrap(), rect);
            },
            Canvas2dMsg::SetShadowOffsetX(value) => {
                self.canvas(canvas_id).set_shadow_offset_x(value)
            },
            Canvas2dMsg::SetShadowOffsetY(value) => {
                self.canvas(canvas_id).set_shadow_offset_y(value)
            },
            Canvas2dMsg::SetShadowBlur(value) => self.canvas(canvas_id).set_shadow_blur(value),
            Canvas2dMsg::SetShadowColor(color) => self.canvas(canvas_id).set_shadow_color(color),
        }
    }

    fn canvas(&mut self, canvas_id: CanvasId) -> &mut CanvasData<'a> {
        self.canvases.get_mut(&canvas_id).expect("Bogus canvas id")
    }
}
<commit_msg>Don't spam the log if the constellation can't be reached from the canvas thread.<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 https://mozilla.org/MPL/2.0/. */

use crate::canvas_data::*;
use crate::ConstellationCanvasMsg;
use canvas_traits::canvas::*;
use crossbeam_channel::{select, unbounded, Sender};
use euclid::default::Size2D;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::thread;

pub enum AntialiasMode {
    Default,
    None,
}

pub struct CanvasPaintThread<'a> {
    canvases: HashMap<CanvasId, CanvasData<'a>>,
    next_canvas_id: CanvasId,
}

impl<'a> CanvasPaintThread<'a> {
    fn new() -> CanvasPaintThread<'a> {
        CanvasPaintThread {
            canvases: HashMap::new(),
            next_canvas_id: CanvasId(0),
        }
    }

    /// Creates a new `CanvasPaintThread` and returns an `IpcSender` to
    /// communicate with it.
    pub fn start() -> (Sender<ConstellationCanvasMsg>, IpcSender<CanvasMsg>) {
        let (ipc_sender, ipc_receiver) = ipc::channel::<CanvasMsg>().unwrap();
        let msg_receiver = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(ipc_receiver);
        let (create_sender, create_receiver) = unbounded();
        thread::Builder::new()
            .name("CanvasThread".to_owned())
            .spawn(move || {
                let mut canvas_paint_thread = CanvasPaintThread::new();
                loop {
                    select! {
                        recv(msg_receiver) -> msg => {
                            match msg {
                                Ok(CanvasMsg::Canvas2d(message, canvas_id)) => {
                                    canvas_paint_thread.process_canvas_2d_message(message, canvas_id);
                                },
                                Ok(CanvasMsg::Close(canvas_id)) => {
                                    canvas_paint_thread.canvases.remove(&canvas_id);
                                },
                                Ok(CanvasMsg::Recreate(size, canvas_id)) => {
                                    canvas_paint_thread.canvas(canvas_id).recreate(size);
                                },
                                Ok(CanvasMsg::FromScript(message, canvas_id)) => match message {
                                    FromScriptMsg::SendPixels(chan) => {
                                        canvas_paint_thread.canvas(canvas_id).send_pixels(chan);
                                    },
                                },
                                Ok(CanvasMsg::FromLayout(message, canvas_id)) => match message {
                                    FromLayoutMsg::SendData(chan) => {
                                        canvas_paint_thread.canvas(canvas_id).send_data(chan);
                                    },
                                },
                                Err(e) => {
                                    warn!("Error on CanvasPaintThread receive ({})", e);
                                },
                            }
                        }
                        recv(create_receiver) -> msg => {
                            match msg {
                                Ok(ConstellationCanvasMsg::Create {
                                    id_sender: creator,
                                    size,
                                    webrender_sender: webrenderer_api_sender,
                                    antialias
                                }) => {
                                    let canvas_id = canvas_paint_thread.create_canvas(
                                        size,
                                        webrenderer_api_sender,
                                        antialias,
                                    );
                                    creator.send(canvas_id).unwrap();
                                },
                                Ok(ConstellationCanvasMsg::Exit) => break,
                                Err(e) => {
                                    warn!("Error on CanvasPaintThread receive ({})", e);
                                    break;
                                },
                            }
                        }
                    }
                }
            })
            .expect("Thread spawning failed");

        (create_sender, ipc_sender)
    }

    pub fn create_canvas(
        &mut self,
        size: Size2D<u64>,
        webrender_api_sender: webrender_api::RenderApiSender,
        antialias: bool,
    ) -> CanvasId {
        let antialias = if antialias {
            AntialiasMode::Default
        } else {
            AntialiasMode::None
        };

        let canvas_id = self.next_canvas_id.clone();
        self.next_canvas_id.0 += 1;

        let canvas_data = CanvasData::new(size, webrender_api_sender, antialias, canvas_id.clone());
        self.canvases.insert(canvas_id.clone(), canvas_data);

        canvas_id
    }

    fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {
        match message {
            Canvas2dMsg::FillText(text, x, y, max_width, style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill_text(text, x, y, max_width);
            },
            Canvas2dMsg::FillRect(rect, style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill_rect(&rect);
            },
            Canvas2dMsg::StrokeRect(rect, style) => {
                self.canvas(canvas_id).set_stroke_style(style);
                self.canvas(canvas_id).stroke_rect(&rect);
            },
            Canvas2dMsg::ClearRect(ref rect) => self.canvas(canvas_id).clear_rect(rect),
            Canvas2dMsg::BeginPath => self.canvas(canvas_id).begin_path(),
            Canvas2dMsg::ClosePath => self.canvas(canvas_id).close_path(),
            Canvas2dMsg::Fill(style) => {
                self.canvas(canvas_id).set_fill_style(style);
                self.canvas(canvas_id).fill();
            },
            Canvas2dMsg::Stroke(style) => {
                self.canvas(canvas_id).set_stroke_style(style);
                self.canvas(canvas_id).stroke();
            },
            Canvas2dMsg::Clip => self.canvas(canvas_id).clip(),
            Canvas2dMsg::IsPointInPath(x, y, fill_rule, chan) => self
                .canvas(canvas_id)
                .is_point_in_path(x, y, fill_rule, chan),
            Canvas2dMsg::DrawImage(
                imagedata,
                image_size,
                dest_rect,
                source_rect,
                smoothing_enabled,
            ) => {
                let data = imagedata.map_or_else(
                    || vec![0; image_size.width as usize * image_size.height as usize * 4],
                    |bytes| bytes.into_vec(),
                );
                self.canvas(canvas_id).draw_image(
                    data,
                    image_size,
                    dest_rect,
                    source_rect,
                    smoothing_enabled,
                )
            },
            Canvas2dMsg::DrawImageInOther(
                other_canvas_id,
                image_size,
                dest_rect,
                source_rect,
                smoothing,
            ) => {
                let image_data = self
                    .canvas(canvas_id)
                    .read_pixels(source_rect.to_u64(), image_size.to_u64());
                self.canvas(other_canvas_id).draw_image(
                    image_data.into(),
                    source_rect.size,
                    dest_rect,
                    source_rect,
                    smoothing,
                );
            },
            Canvas2dMsg::MoveTo(ref point) => self.canvas(canvas_id).move_to(point),
            Canvas2dMsg::LineTo(ref point) => self.canvas(canvas_id).line_to(point),
            Canvas2dMsg::Rect(ref rect) => self.canvas(canvas_id).rect(rect),
            Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => {
                self.canvas(canvas_id).quadratic_curve_to(cp, pt)
            },
            Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => {
                self.canvas(canvas_id).bezier_curve_to(cp1, cp2, pt)
            },
            Canvas2dMsg::Arc(ref center, radius, start, end, ccw) => {
                self.canvas(canvas_id).arc(center, radius, start, end, ccw)
            },
            Canvas2dMsg::ArcTo(ref cp1, ref cp2, radius) => {
                self.canvas(canvas_id).arc_to(cp1, cp2, radius)
            },
            Canvas2dMsg::Ellipse(ref center, radius_x, radius_y, rotation, start, end, ccw) => self
                .canvas(canvas_id)
                .ellipse(center, radius_x, radius_y, rotation, start, end, ccw),
            Canvas2dMsg::RestoreContext => self.canvas(canvas_id).restore_context_state(),
            Canvas2dMsg::SaveContext => self.canvas(canvas_id).save_context_state(),
            Canvas2dMsg::SetLineWidth(width) => self.canvas(canvas_id).set_line_width(width),
            Canvas2dMsg::SetLineCap(cap) => self.canvas(canvas_id).set_line_cap(cap),
            Canvas2dMsg::SetLineJoin(join) => self.canvas(canvas_id).set_line_join(join),
            Canvas2dMsg::SetMiterLimit(limit) => self.canvas(canvas_id).set_miter_limit(limit),
            Canvas2dMsg::SetTransform(ref matrix) => self.canvas(canvas_id).set_transform(matrix),
            Canvas2dMsg::SetGlobalAlpha(alpha) => self.canvas(canvas_id).set_global_alpha(alpha),
            Canvas2dMsg::SetGlobalComposition(op) => {
                self.canvas(canvas_id).set_global_composition(op)
            },
            Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender) => {
                let pixels = self.canvas(canvas_id).read_pixels(dest_rect, canvas_size);
                sender.send(&pixels).unwrap();
            },
            Canvas2dMsg::PutImageData(rect, receiver) => {
                self.canvas(canvas_id)
                    .put_image_data(receiver.recv().unwrap(), rect);
            },
            Canvas2dMsg::SetShadowOffsetX(value) => {
                self.canvas(canvas_id).set_shadow_offset_x(value)
            },
            Canvas2dMsg::SetShadowOffsetY(value) => {
                self.canvas(canvas_id).set_shadow_offset_y(value)
            },
            Canvas2dMsg::SetShadowBlur(value) => self.canvas(canvas_id).set_shadow_blur(value),
            Canvas2dMsg::SetShadowColor(color) => self.canvas(canvas_id).set_shadow_color(color),
        }
    }

    fn canvas(&mut self, canvas_id: CanvasId) -> &mut CanvasData<'a> {
        self.canvases.get_mut(&canvas_id).expect("Bogus canvas id")
    }
}
<|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::cell::DOMRefCell;
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root, RootedReference};
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: DOMRefCell<Vec<SessionHistoryEntry>>,
    active_index: usize,
    frame_element: Option<JS<Element>>,
}

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

    #[allow(unsafe_code)]
    pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> {
        unsafe {
            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(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 init(&self, document: &Document) {
        assert!(self.history.borrow().is_empty());
        assert_eq!(self.active_index, 0);
        self.history.borrow_mut().push(SessionHistoryEntry::new(document));
    }

    pub fn active_document(&self) -> Root<Document> {
        Root::from_ref(&*self.history.borrow()[self.active_index].document)
    }

    pub fn active_window(&self) -> Root<Window> {
        Root::from_ref(self.active_document().window())
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.r()
    }

    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))
    }
}
<commit_msg>Reorder js imports in browsingcontext<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::cell::DOMRefCell;
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root, RootedReference};
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, GetProxyPrivate, NewWindowProxy};
use js::glue::{ProxyTraps, SetProxyExtra};
use js::jsapi::{Handle, HandleId, HandleObject, JSAutoCompartment, JSAutoRequest, JSContext};
use js::jsapi::{JSErrNum, JSObject, JSPropertyDescriptor, JS_AlreadyHasOwnPropertyById};
use js::jsapi::{JS_DefinePropertyById6, JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo};
use js::jsapi::{JS_GetClass, JS_GetOwnPropertyDescriptorById, MutableHandle, MutableHandleValue};
use js::jsapi::{ObjectOpResult, RootedObject, RootedValue};
use js::jsval::{ObjectValue, PrivateValue, UndefinedValue};

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

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

    #[allow(unsafe_code)]
    pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> {
        unsafe {
            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(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 init(&self, document: &Document) {
        assert!(self.history.borrow().is_empty());
        assert_eq!(self.active_index, 0);
        self.history.borrow_mut().push(SessionHistoryEntry::new(document));
    }

    pub fn active_document(&self) -> Root<Document> {
        Root::from_ref(&*self.history.borrow()[self.active_index].document)
    }

    pub fn active_window(&self) -> Root<Window> {
        Root::from_ref(self.active_document().window())
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.r()
    }

    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>/* 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::activation::Activatable;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlanchorelement::follow_hyperlink;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use std::default::Default;
use std::f32;
use style::attr::AttrValue;

#[derive(PartialEq)]
#[derive(Debug)]
pub enum Area {
    Circle { left: f32, top: f32, radius: f32 },
    Rectangle { top_left: (f32, f32), bottom_right: (f32, f32) },
    Polygon { points: Vec<f32> },
}

pub enum Shape {
    Circle,
    Rectangle,
    Polygon,
}

// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-list-of-floating-point-numbers
// https://html.spec.whatwg.org/multipage/#image-map-processing-model
impl Area {
    pub fn parse(coord: &str, target: Shape) -> Option<Area> {
        let points_count =  match target {
            Shape::Circle => 3,
            Shape::Rectangle => 4,
            Shape::Polygon => 0,
        };

        let size = coord.len();
        let num = coord.as_bytes();
        let mut index = 0;

        // Step 4: Walk till char is not a delimiter
        while index < size {
            let val = num[index];
            match val {
                b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r'  => {},
                _ => break,
            }

            index += 1;
        }

        //This vector will hold all parsed coordinates
        let mut number_list = Vec::new();
        let mut array = Vec::new();
        let ar_ref = &mut array;

        // Step 5: walk till end of string
        while index < size {
            // Step 5.1: walk till we hit a valid char i.e., 0 to 9, dot or dash, e, E
            while index < size {
                let val = num[index];
                match val {
                    b'0'...b'9' | b'.' | b'-' | b'E' | b'e' => break,
                    _ => {},
                }

                index += 1;
            }

            // Step 5.2: collect valid symbols till we hit another delimiter
            while index < size {
                let val = num[index];

                match val {
                    b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r' => break,
                    _ => (*ar_ref).push(val),
                }

                index += 1;
            }

            // The input does not consist any valid charecters
            if (*ar_ref).is_empty() {
                break;
            }

            // Convert String to float
            match String::from_utf8((*ar_ref).clone()).unwrap().parse::<f32>() {
                Ok(v) => number_list.push(v),
                Err(_) => number_list.push(0.0),
            };

            (*ar_ref).clear();

            // For rectangle and circle, stop parsing once we have three
            // and four coordinates respectively
            if points_count > 0 && number_list.len() == points_count {
                break;
            }
        }

        let final_size = number_list.len();

        match target {
            Shape::Circle => {
                if final_size == 3 {
                    if number_list[2] <= 0.0 {
                        None
                    } else {
                        Some(Area::Circle {
                                 left: number_list[0],
                                 top: number_list[1],
                                 radius: number_list[2]
                             })
                    }
                } else {
                    None
                }
            },

            Shape::Rectangle => {
                if final_size == 4 {
                    if number_list[0] > number_list[2] {
                        number_list.swap(0, 2);
                    }

                    if number_list[1] > number_list[3] {
                        number_list.swap(1, 3);
                    }

                    Some(Area::Rectangle {
                             top_left: (number_list[0], number_list[1]),
                             bottom_right: (number_list[2], number_list[3])
                         })
                } else {
                    None
                }
            },

            Shape::Polygon => {
                if final_size >= 6 {
                    if final_size % 2 != 0 {
                        // Drop last element if there are odd number of coordinates
                        number_list.remove(final_size - 1);
                    }
                    Some(Area::Polygon { points: number_list })
                } else {
                    None
                }
            },
        }
    }

    pub fn hit_test(&self, p: Point2D<f32>) -> bool {
        match *self {
            Area::Circle { left, top, radius } => {
                (p.x - left) * (p.x - left) +
                (p.y - top) * (p.y - top) -
                radius * radius <= 0.0
            },

            Area::Rectangle { top_left, bottom_right } => {
                p.x <= bottom_right.0 && p.x >= top_left.0 &&
                p.y <= bottom_right.1 && p.y >= top_left.1
            },

            //TODO polygon hit_test
            _ => false,
        }
    }

    pub fn absolute_coords(&self, p: Point2D<f32>) -> Area {
        match *self {
            Area::Rectangle { top_left, bottom_right } => {
                Area::Rectangle {
                    top_left: (top_left.0 + p.x, top_left.1 + p.y),
                    bottom_right: (bottom_right.0 + p.x, bottom_right.1 + p.y)
                }
            },
            Area::Circle { left, top, radius } => {
                Area::Circle {
                    left: (left + p.x),
                    top: (top + p.y),
                    radius: radius
                }
            },
            Area::Polygon { ref points } => {
//                let new_points = Vec::new();
                let iter = points.iter().enumerate().map(|(index, point)| {
                    match index % 2 {
                        0 => point + p.x as f32,
                        _ => point + p.y as f32,
                    }
                });
                Area::Polygon { points: iter.collect::<Vec<_>>() }
            },
        }
    }
}

#[dom_struct]
pub struct HTMLAreaElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableJS<DOMTokenList>,
}

impl HTMLAreaElement {
    fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLAreaElement> {
        Node::reflect_node(box HTMLAreaElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLAreaElementBinding::Wrap)
    }

    pub fn get_shape_from_coords(&self) -> Option<Area> {
       let elem = self.upcast::<Element>();
       let shape = elem.get_string_attribute(&"shape".into());
       let shp: Shape = match shape.to_lowercase().as_ref() {
           "circle" => Shape::Circle,
           "circ" => Shape::Circle,
           "rectangle" => Shape::Rectangle,
           "rect" => Shape::Rectangle,
           "polygon" => Shape::Rectangle,
           "poly" => Shape::Polygon,
           _ => return None,
        };
        if elem.has_attribute(&"coords".into()) {
            let attribute = elem.get_string_attribute(&"coords".into());
            Area::parse(&attribute, shp)
        } else {
            None
        }
    }
}

impl VirtualMethods for HTMLAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLAreaElementMethods for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#dom-area-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(self.upcast(), &local_name!("rel"))
        })
    }
}

impl Activatable for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        self.as_element().has_attribute(&local_name!("href"))
    }

    fn pre_click_activation(&self) {
    }

    fn canceled_activation(&self) {
    }

    fn implicit_submission(&self, _ctrl_key: bool, _shift_key: bool,
                           _alt_key: bool, _meta_key: bool) {
    }

    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        // Step 1
        let doc = document_from_node(self);
        if !doc.is_fully_active() {
            return;
        }
        // Step 2
        // TODO : We should be choosing a browsing context and navigating to it.
        // Step 3
        let referrer_policy = match self.RelList().Contains("noreferrer".into()) {
            true => Some(ReferrerPolicy::NoReferrer),
            false => None,
        };
        follow_hyperlink(self.upcast::<Element>(), None, referrer_policy);
    }
}
<commit_msg>Auto merge of #17620 - omakk:master, r=nox<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::activation::Activatable;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlanchorelement::follow_hyperlink;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use std::default::Default;
use std::f32;
use std::str;
use style::attr::AttrValue;

#[derive(PartialEq)]
#[derive(Debug)]
pub enum Area {
    Circle { left: f32, top: f32, radius: f32 },
    Rectangle { top_left: (f32, f32), bottom_right: (f32, f32) },
    Polygon { points: Vec<f32> },
}

pub enum Shape {
    Circle,
    Rectangle,
    Polygon,
}

// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-list-of-floating-point-numbers
// https://html.spec.whatwg.org/multipage/#image-map-processing-model
impl Area {
    pub fn parse(coord: &str, target: Shape) -> Option<Area> {
        let points_count =  match target {
            Shape::Circle => 3,
            Shape::Rectangle => 4,
            Shape::Polygon => 0,
        };

        let size = coord.len();
        let num = coord.as_bytes();
        let mut index = 0;

        // Step 4: Walk till char is not a delimiter
        while index < size {
            let val = num[index];
            match val {
                b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r'  => {},
                _ => break,
            }

            index += 1;
        }

        //This vector will hold all parsed coordinates
        let mut number_list = Vec::new();
        let mut array = Vec::new();

        // Step 5: walk till end of string
        while index < size {
            // Step 5.1: walk till we hit a valid char i.e., 0 to 9, dot or dash, e, E
            while index < size {
                let val = num[index];
                match val {
                    b'0'...b'9' | b'.' | b'-' | b'E' | b'e' => break,
                    _ => {},
                }

                index += 1;
            }

            // Step 5.2: collect valid symbols till we hit another delimiter
            while index < size {
                let val = num[index];

                match val {
                    b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r' => break,
                    _ => array.push(val),
                }

                index += 1;
            }

            // The input does not consist any valid charecters
            if array.is_empty() {
                break;
            }

            // Convert String to float
            match str::from_utf8(&array).ok().and_then(|s| s.parse::<f32>().ok()) {
                Some(v) => number_list.push(v),
                None => number_list.push(0.0),
            };

            array.clear();

            // For rectangle and circle, stop parsing once we have three
            // and four coordinates respectively
            if points_count > 0 && number_list.len() == points_count {
                break;
            }
        }

        let final_size = number_list.len();

        match target {
            Shape::Circle => {
                if final_size == 3 {
                    if number_list[2] <= 0.0 {
                        None
                    } else {
                        Some(Area::Circle {
                                 left: number_list[0],
                                 top: number_list[1],
                                 radius: number_list[2]
                             })
                    }
                } else {
                    None
                }
            },

            Shape::Rectangle => {
                if final_size == 4 {
                    if number_list[0] > number_list[2] {
                        number_list.swap(0, 2);
                    }

                    if number_list[1] > number_list[3] {
                        number_list.swap(1, 3);
                    }

                    Some(Area::Rectangle {
                             top_left: (number_list[0], number_list[1]),
                             bottom_right: (number_list[2], number_list[3])
                         })
                } else {
                    None
                }
            },

            Shape::Polygon => {
                if final_size >= 6 {
                    if final_size % 2 != 0 {
                        // Drop last element if there are odd number of coordinates
                        number_list.remove(final_size - 1);
                    }
                    Some(Area::Polygon { points: number_list })
                } else {
                    None
                }
            },
        }
    }

    pub fn hit_test(&self, p: Point2D<f32>) -> bool {
        match *self {
            Area::Circle { left, top, radius } => {
                (p.x - left) * (p.x - left) +
                (p.y - top) * (p.y - top) -
                radius * radius <= 0.0
            },

            Area::Rectangle { top_left, bottom_right } => {
                p.x <= bottom_right.0 && p.x >= top_left.0 &&
                p.y <= bottom_right.1 && p.y >= top_left.1
            },

            //TODO polygon hit_test
            _ => false,
        }
    }

    pub fn absolute_coords(&self, p: Point2D<f32>) -> Area {
        match *self {
            Area::Rectangle { top_left, bottom_right } => {
                Area::Rectangle {
                    top_left: (top_left.0 + p.x, top_left.1 + p.y),
                    bottom_right: (bottom_right.0 + p.x, bottom_right.1 + p.y)
                }
            },
            Area::Circle { left, top, radius } => {
                Area::Circle {
                    left: (left + p.x),
                    top: (top + p.y),
                    radius: radius
                }
            },
            Area::Polygon { ref points } => {
//                let new_points = Vec::new();
                let iter = points.iter().enumerate().map(|(index, point)| {
                    match index % 2 {
                        0 => point + p.x as f32,
                        _ => point + p.y as f32,
                    }
                });
                Area::Polygon { points: iter.collect::<Vec<_>>() }
            },
        }
    }
}

#[dom_struct]
pub struct HTMLAreaElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableJS<DOMTokenList>,
}

impl HTMLAreaElement {
    fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLAreaElement> {
        Node::reflect_node(box HTMLAreaElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLAreaElementBinding::Wrap)
    }

    pub fn get_shape_from_coords(&self) -> Option<Area> {
       let elem = self.upcast::<Element>();
       let shape = elem.get_string_attribute(&"shape".into());
       let shp: Shape = match shape.to_lowercase().as_ref() {
           "circle" => Shape::Circle,
           "circ" => Shape::Circle,
           "rectangle" => Shape::Rectangle,
           "rect" => Shape::Rectangle,
           "polygon" => Shape::Rectangle,
           "poly" => Shape::Polygon,
           _ => return None,
        };
        if elem.has_attribute(&"coords".into()) {
            let attribute = elem.get_string_attribute(&"coords".into());
            Area::parse(&attribute, shp)
        } else {
            None
        }
    }
}

impl VirtualMethods for HTMLAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLAreaElementMethods for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#dom-area-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(self.upcast(), &local_name!("rel"))
        })
    }
}

impl Activatable for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        self.as_element().has_attribute(&local_name!("href"))
    }

    fn pre_click_activation(&self) {
    }

    fn canceled_activation(&self) {
    }

    fn implicit_submission(&self, _ctrl_key: bool, _shift_key: bool,
                           _alt_key: bool, _meta_key: bool) {
    }

    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        // Step 1
        let doc = document_from_node(self);
        if !doc.is_fully_active() {
            return;
        }
        // Step 2
        // TODO : We should be choosing a browsing context and navigating to it.
        // Step 3
        let referrer_policy = match self.RelList().Contains("noreferrer".into()) {
            true => Some(ReferrerPolicy::NoReferrer),
            false => None,
        };
        follow_hyperlink(self.upcast::<Element>(), None, referrer_policy);
    }
}
<|endoftext|>