<commit_before>//! Reconstruct AST from elaborated constructs.

use syntax::ast::*;
use syntax::tokens::*;
use source::Span;
use num::{BigInt, Zero};
use number::{LogicVec, LogicNumber, LogicValue};
use super::ty::{Ty, IntTy, Struct, Enum};
use super::expr::Val;
use super::hier::{self, HierItem};

pub fn reconstruct(source: &hier::Source) -> Vec<Vec<Item>> {
    let reconstructor = Reconstructor { source };
    reconstructor.reconstruct()
}

struct Reconstructor<'a> {
    source: &'a hier::Source,
}

impl<'a> Reconstructor<'a> {
    /// Given a i32, reconstruct the corresponding corresponding constant value.
    pub fn reconstruct_int(&self, val: i32, span: Span) -> Expr {
        let expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
            LogicNumber {
                value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(val.abs()).unwrap()),
                sized: false,
            }
        ), span)), span);
        if val >= 0 {
            expr
        } else {
            Spanned::new(ExprKind::Unary(UnaryOp::Sub, None, Box::new(expr)), span)
        }
    }

    // Given a LogicVec, reconstruct the corresponding corresponding constant value.
    pub fn reconstruct_const(&self, val: &LogicVec, span: Span) -> Expr {
        Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
            LogicNumber {
                value: val.clone(),
                sized: true,
            }
        ), span)), span)
    }

    /// Given an ty::Enum, reconstruct ast::EnumDecl
    pub fn reconstruct_enum(&self, enu: &Enum, prefix: &str) -> EnumDecl {
        let base = self.reconstruct_ty_int(&enu.base, Span::none());
        let members = enu.elements.borrow().iter().map(|(name, val)| {
            DeclAssign {
                name: Ident::new(format!("{}_{}", prefix, name.value), name.span),
                dim: Vec::new(),
                init: Some(Box::new(self.reconstruct_val_int(&enu.base, val, Span::none()))),
            }
        }).collect();
        EnumDecl {
            ty: Some(Box::new(base)),
            members,
        }
    }

    /// Given an ty::Struct, reconstruct ast::AggrDecl
    pub fn reconstruct_struct(&self, struc: &Struct) -> AggrDecl {
        let members = struc.members.iter().map(|(ty, name, init)| {
            let astty = self.reconstruct_ty_int(&ty, Span::none());
            AggrMember {
                attr: None,
                ty: astty,
                list: vec![DeclAssign {
                    name: name.clone(),
                    dim: Vec::new(),
                    init: init.as_ref().map(|init| {
                        Box::new(self.reconstruct_val_int(&ty, &init, Span::none()))
                    }),
                }],
            }
        }).collect();

        AggrDecl {
            kind: AggrType::Struct,
            packed: true,
            sign: if struc.sign { Signing::Signed } else { Signing::Unsigned },
            members,
        }
    }

    pub fn reconstruct_ty_int(&self, ty: &IntTy, span: Span) -> DataType {
        let mut inner = ty;
        let mut dim = Vec::new();
        while let IntTy::Array(element, ub, lb) = inner {
            inner = &**element;
            // Synthesis fake expression nodes from constants
            let a_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                LogicNumber {
                    value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(*ub).unwrap()),
                    sized: false,
                }
            ), span)), span);
            let b_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                LogicNumber {
                    value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(*lb).unwrap()),
                    sized: false,
                }
            ), span)), span);
            dim.push(Spanned::new(DimKind::Range(Box::new(a_expr), Box::new(b_expr)), span))
        }
        let kind = match inner {
            IntTy::Array(..) => unreachable!(),
            IntTy::Logic(two_state, sign) => {
                DataTypeKind::IntVec(
                    if *two_state { IntVecTy::Bit } else { IntVecTy::Logic },
                    if *sign { Signing::Signed} else { Signing::Unsigned },
                    dim
                )
            }
            IntTy::SimpleVec(width, two_state, sign) => {
                // Synthesis fake expression nodes from constants
                let a_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                    LogicNumber {
                        value: LogicVec::from(32, true, ::num::FromPrimitive::from_usize(width - 1).unwrap()),
                        sized: false,
                    }
                ), span)), span);
                let b_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                    LogicNumber {
                        value: LogicVec::from(32, true, BigInt::zero()),
                        sized: false,
                    }
                ), span)), span);
                dim.push(Spanned::new(DimKind::Range(Box::new(a_expr), Box::new(b_expr)), span));
                DataTypeKind::IntVec(
                    if *two_state { IntVecTy::Bit } else { IntVecTy::Logic },
                    if *sign { Signing::Signed} else { Signing::Unsigned},
                    dim
                )
            }
            IntTy::Struct(struc) => {
                DataTypeKind::HierName(None, Ident::new(format!("struct_{}", self.source.structs.iter().position(|x| x == struc).unwrap()), Span::none()), dim)
            }
            IntTy::Enum(enu) => {
                DataTypeKind::HierName(None, Ident::new(format!("enum_{}", self.source.enums.iter().position(|x| x == enu).unwrap()), Span::none()), dim)
            }
        };
        Spanned::new(kind, span)
    }

    pub fn reconstruct_ty(&self, ty: &Ty, span: Span) -> (DataType, Vec<Dim>) {
        let kind = match ty {
            Ty::Type => DataTypeKind::Type,
            Ty::Int(subty) => return (self.reconstruct_ty_int(subty, span), Vec::new()),
            Ty::String => DataTypeKind::String,
            v => unimplemented!("{:?}", v),
        };
        (Spanned::new(kind, span), Vec::new())
    }

    pub fn reconstruct_val_int(&self, ty: &IntTy, val: &LogicVec, span: Span) -> Expr {
        match ty {
            IntTy::SimpleVec(_, false, _) => {
                self.reconstruct_const(val, span)
            }
            IntTy::Enum(_) => {
                let ty = self.reconstruct_ty_int(ty, span);
                let val = self.reconstruct_const(val, span);
                Spanned::new(ExprKind::TypeCast(
                    Box::new(Spanned::new(ExprKind::Type(Box::new(ty)), span)),
                    Box::new(val)
                ), span)
            }
            _ => unimplemented!(),
        }
    }

    pub fn reconstruct_val(&self, ty: &Ty, val: &Val, span: Span) -> Expr {
        let kind = match ty {
            Ty::Type => {
                let ty = if let Val::Type(ty) = val { ty } else { unreachable!()};
                let (ty, dim) = self.reconstruct_ty(ty, span);
                // TODO: How to deal with dim
                assert!(dim.len() == 0);
                ExprKind::Type(Box::new(ty))
            }
            Ty::Int(subty) => {
                let val = if let Val::Int(v) = val { v } else { unreachable!() };
                return self.reconstruct_val_int(subty, val, span);
            }
            Ty::FixStr(_) => {
                let val = if let Val::FixStr(v) = val { v } else { unreachable!() };
                ExprKind::Literal(Spanned::new(TokenKind::StringLiteral(val.clone()), span))
            }
            _ => unimplemented!(),
        };
        Spanned::new(kind, span)
    }

    pub fn reconstruct_item(&self, item: &HierItem, list: &mut Vec<Item>) {
        match item {
            HierItem::Param(decl) => {
                let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                let expr = self.reconstruct_val(&decl.ty, &decl.init, Span::none());
                list.push(Item::ParamDecl(Box::new(ParamDecl {
                    kw: decl.kw,
                    ty: Some(Box::new(ty)),
                    list: vec![DeclAssign {
                        name: decl.name.clone(),
                        dim,
                        init: Some(Box::new(expr)),
                    }]
                })));
            }
            HierItem::Type(decl) => {
                let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                list.push(Item::Typedef(None, Box::new(ty), Box::new(decl.name.clone()), dim));
            }
            HierItem::Design(decl) => {
                // Reconstruct all instantiations and display them here
                for (_, inst) in decl.instances.borrow().iter() {
                    list.push(self.reconstruct_instantiation(inst));
                }
            }
            HierItem::Instance(decl) => {
                list.push(Item::HierInstantiation(Box::new(HierInstantiation {
                    attr: None,
                    name: decl.inst.name.clone(),
                    param: None,
                        inst: vec![HierInst {
                        name: decl.name.clone(),
                        dim: decl.dim.iter().map(|(ub, lb)| {
                            let ub_expr = self.reconstruct_int(*ub, Span::none());
                            let lb_expr = self.reconstruct_int(*lb, Span::none());
                            Spanned::new(DimKind::Range(Box::new(ub_expr), Box::new(lb_expr)), Span::none())
                        }).collect(),
                        ports: decl.port.clone(),
                    }]
                })))
            }
            HierItem::Other(item) => list.push(Item::clone(item)),
            HierItem::OtherName => (),
            HierItem::GenBlock(genblk) => {
                // Reconstruct all interior items
                let mut sublist = Vec::new();
                for item in &genblk.scope.items {
                    self.reconstruct_item(item, &mut sublist);
                }
                list.push(Item::IfGen(Box::new(IfGen {
                    attr: None,
                    if_block: vec![{
                        (self.reconstruct_const(&LogicValue::One.into(), Span::none()), GenBlock {
                            name: genblk.name.clone(),
                            items: sublist,
                        })
                    }],
                    else_block: None,
                })));
            },
            HierItem::LoopGenBlock(loopgenblk) => {
                for (_, genblk) in loopgenblk.instances.borrow().iter() {
                    // Reconstruct all interior items
                    let mut sublist = Vec::new();
                    for item in &genblk.scope.items {
                        self.reconstruct_item(item, &mut sublist);
                    }
                    list.push(Item::IfGen(Box::new(IfGen {
                        attr: None,
                        if_block: vec![{
                            (self.reconstruct_const(&LogicValue::One.into(), Span::none()), GenBlock {
                                name: genblk.name.clone(),
                                items: sublist,
                            })
                        }],
                        else_block: None,
                    })));
                }
            }
            HierItem::Modport(_) => (),
            HierItem::Enum(..) => (),
            v => unimplemented!("{:?}", std::mem::discriminant(v)),
        }
    }

    pub fn reconstruct_instantiation(&self, inst: &hier::DesignInstantiation) -> Item {
        // Reconstruct all parameters
        let mut params = Vec::new();
        for item in &inst.scope.items {
            if let HierItem::Param(_) = item {
                self.reconstruct_item(item, &mut params);
            } else {
                break;
            }
        }
        let params: Vec<_> = params.into_iter().map(|x| {
            if let Item::ParamDecl(decl) = x { *decl } else { unreachable!() }
        }).collect();

        // Reconstruct all ports
        let mut ports = Vec::new();
        for item in inst.scope.items.iter().skip(params.len()) {
            match item {
                HierItem::DataPort(decl) => {
                    let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                    ports.push(PortDecl::Data(decl.dir, decl.net.clone(), Box::new(ty), vec![DeclAssign {
                        name: decl.name.clone(),
                        dim,
                        init: decl.init.clone(),
                    }]));
                }
                HierItem::InterfacePort(decl) => {
                    ports.push(PortDecl::Interface(Some(Box::new(decl.inst.name.clone())), None, vec![DeclAssign {
                        name: decl.name.clone(),
                        dim: Vec::new(),
                        init: None,
                    }]));
                }
                _ => break,
            }
        }

        // Now chunk them into params, port and items
        let mut list = Vec::new();
        for item in inst.scope.items.iter().skip(params.len() + ports.len()) {
            self.reconstruct_item(&item, &mut list);
        }

        Item::DesignDecl(Box::new(DesignDecl {
            attr: None,
            kw: inst.ast.kw,
            lifetime: inst.ast.lifetime,
            name: inst.name.clone(),
            pkg_import: Vec::new(),
            param: if !params.is_empty() { Some(params) } else { None },
            port: ports,
            items: list,
        }))
    }

    pub fn reconstruct(&self) -> Vec<Vec<Item>> {
        let mut units = Vec::new();

        // Build global unit: contain packages and typedefs
        let mut list = Vec::new();
        list.extend(self.source.enums.iter().enumerate().map(|(index, enu)| {
            let prefix = format!("enum_{}", index);
            let enu = self.reconstruct_enum(enu, &prefix);
            let ty = Spanned::new(DataTypeKind::Enum(enu, Vec::new()), Span::none());
            let name = Ident::new(prefix, Span::none());
            Item::Typedef(None, Box::new(ty), Box::new(name), Vec::new())
        }));
        list.extend(self.source.structs.iter().enumerate().map(|(index, struc)| {
            let struc = self.reconstruct_struct(struc);
            let ty = Spanned::new(DataTypeKind::Aggr(struc, Vec::new()), Span::none());
            let name = Ident::new(format!("struct_{}", index), Span::none());
            Item::Typedef(None, Box::new(ty), Box::new(name), Vec::new())
        }));
        list.extend(self.source.pkgs.iter().map(|(_, decl)| {
            let mut list = Vec::new();
            for item in &decl.scope.items {
                self.reconstruct_item(item, &mut list);
            }
            Item::PkgDecl(Box::new(PkgDecl {
                attr: None,
                lifetime: Lifetime::Static,
                name: decl.name.clone(),
                items: list
            }))
        }));
        units.push(list);

        for unit in &self.source.units {
            let mut list = Vec::new();
            for item in &unit.items {
                self.reconstruct_item(item, &mut list);
            }
            units.push(list);
        }
        units
    }
}
<commit_msg>Reconstruction of enum<commit_after>//! Reconstruct AST from elaborated constructs.

use syntax::ast::*;
use syntax::tokens::*;
use source::Span;
use num::{BigInt, Zero};
use number::{LogicVec, LogicNumber, LogicValue};
use super::ty::{Ty, IntTy, Struct, Enum};
use super::expr::Val;
use super::hier::{self, HierItem};

pub fn reconstruct(source: &hier::Source) -> Vec<Vec<Item>> {
    let mut reconstructor = Reconstructor {
        source,
        global_qualify: false,
    };
    reconstructor.reconstruct()
}

struct Reconstructor<'a> {
    source: &'a hier::Source,
    /// Whether reference to structs and enums need to be qualified by "global_types::"
    global_qualify: bool,
}

impl<'a> Reconstructor<'a> {
    /// Given a i32, reconstruct the corresponding corresponding constant value.
    pub fn reconstruct_int(&self, val: i32, span: Span) -> Expr {
        let expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
            LogicNumber {
                value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(val.abs()).unwrap()),
                sized: false,
            }
        ), span)), span);
        if val >= 0 {
            expr
        } else {
            Spanned::new(ExprKind::Unary(UnaryOp::Sub, None, Box::new(expr)), span)
        }
    }

    // Given a LogicVec, reconstruct the corresponding corresponding constant value.
    pub fn reconstruct_const(&self, val: &LogicVec, span: Span) -> Expr {
        Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
            LogicNumber {
                value: val.clone(),
                sized: true,
            }
        ), span)), span)
    }

    /// Given an ty::Enum, reconstruct ast::EnumDecl
    pub fn reconstruct_enum(&self, enu: &Enum, prefix: &str) -> EnumDecl {
        let base = self.reconstruct_ty_int(&enu.base, Span::none());
        let members = enu.elements.borrow().iter().map(|(name, val)| {
            DeclAssign {
                name: Ident::new(format!("{}_{}", prefix, name.value), name.span),
                dim: Vec::new(),
                init: Some(Box::new(self.reconstruct_val_int(&enu.base, val, Span::none()))),
            }
        }).collect();
        EnumDecl {
            ty: Some(Box::new(base)),
            members,
        }
    }

    /// Given an ty::Struct, reconstruct ast::AggrDecl
    pub fn reconstruct_struct(&self, struc: &Struct) -> AggrDecl {
        let members = struc.members.iter().map(|(ty, name, init)| {
            let astty = self.reconstruct_ty_int(&ty, Span::none());
            AggrMember {
                attr: None,
                ty: astty,
                list: vec![DeclAssign {
                    name: name.clone(),
                    dim: Vec::new(),
                    init: init.as_ref().map(|init| {
                        Box::new(self.reconstruct_val_int(&ty, &init, Span::none()))
                    }),
                }],
            }
        }).collect();

        AggrDecl {
            kind: AggrType::Struct,
            packed: true,
            sign: if struc.sign { Signing::Signed } else { Signing::Unsigned },
            members,
        }
    }

    pub fn reconstruct_ty_int(&self, ty: &IntTy, span: Span) -> DataType {
        let mut inner = ty;
        let mut dim = Vec::new();
        while let IntTy::Array(element, ub, lb) = inner {
            inner = &**element;
            // Synthesis fake expression nodes from constants
            let a_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                LogicNumber {
                    value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(*ub).unwrap()),
                    sized: false,
                }
            ), span)), span);
            let b_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                LogicNumber {
                    value: LogicVec::from(32, true, ::num::FromPrimitive::from_i32(*lb).unwrap()),
                    sized: false,
                }
            ), span)), span);
            dim.push(Spanned::new(DimKind::Range(Box::new(a_expr), Box::new(b_expr)), span))
        }
        let kind = match inner {
            IntTy::Array(..) => unreachable!(),
            IntTy::Logic(two_state, sign) => {
                DataTypeKind::IntVec(
                    if *two_state { IntVecTy::Bit } else { IntVecTy::Logic },
                    if *sign { Signing::Signed} else { Signing::Unsigned },
                    dim
                )
            }
            IntTy::SimpleVec(width, two_state, sign) => {
                // Synthesis fake expression nodes from constants
                let a_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                    LogicNumber {
                        value: LogicVec::from(32, true, ::num::FromPrimitive::from_usize(width - 1).unwrap()),
                        sized: false,
                    }
                ), span)), span);
                let b_expr = Spanned::new(ExprKind::Literal(Spanned::new(TokenKind::IntegerLiteral(
                    LogicNumber {
                        value: LogicVec::from(32, true, BigInt::zero()),
                        sized: false,
                    }
                ), span)), span);
                dim.push(Spanned::new(DimKind::Range(Box::new(a_expr), Box::new(b_expr)), span));
                DataTypeKind::IntVec(
                    if *two_state { IntVecTy::Bit } else { IntVecTy::Logic },
                    if *sign { Signing::Signed} else { Signing::Unsigned},
                    dim
                )
            }
            IntTy::Struct(struc) => {
                DataTypeKind::HierName(
                    if self.global_qualify {
                        Some(Scope::Name(None, Box::new(Ident::new_unspanned("global_types".to_owned()))))
                    } else {
                        None
                    },
                    Ident::new_unspanned(format!("struct_{}", self.source.structs.iter().position(|x| x == struc).unwrap())),
                    dim
                )
            }
            IntTy::Enum(enu) => {
                DataTypeKind::HierName(
                    if self.global_qualify {
                        Some(Scope::Name(None, Box::new(Ident::new_unspanned("global_types".to_owned()))))
                    } else {
                        None
                    },
                    Ident::new_unspanned(format!("enum_{}", self.source.enums.iter().position(|x| x == enu).unwrap())),
                    dim
                )
            }
        };
        Spanned::new(kind, span)
    }

    pub fn reconstruct_ty(&self, ty: &Ty, span: Span) -> (DataType, Vec<Dim>) {
        let kind = match ty {
            Ty::Type => DataTypeKind::Type,
            Ty::Int(subty) => return (self.reconstruct_ty_int(subty, span), Vec::new()),
            Ty::String => DataTypeKind::String,
            v => unimplemented!("{:?}", v),
        };
        (Spanned::new(kind, span), Vec::new())
    }

    pub fn reconstruct_val_int(&self, ty: &IntTy, val: &LogicVec, span: Span) -> Expr {
        match ty {
            IntTy::SimpleVec(_, false, _) => {
                self.reconstruct_const(val, span)
            }
            IntTy::Enum(_) => {
                let ty = self.reconstruct_ty_int(ty, span);
                let val = self.reconstruct_const(val, span);
                Spanned::new(ExprKind::TypeCast(
                    Box::new(Spanned::new(ExprKind::Type(Box::new(ty)), span)),
                    Box::new(val)
                ), span)
            }
            _ => unimplemented!(),
        }
    }

    pub fn reconstruct_val(&self, ty: &Ty, val: &Val, span: Span) -> Expr {
        let kind = match ty {
            Ty::Type => {
                let ty = if let Val::Type(ty) = val { ty } else { unreachable!()};
                let (ty, dim) = self.reconstruct_ty(ty, span);
                // TODO: How to deal with dim
                assert!(dim.len() == 0);
                ExprKind::Type(Box::new(ty))
            }
            Ty::Int(subty) => {
                let val = if let Val::Int(v) = val { v } else { unreachable!() };
                return self.reconstruct_val_int(subty, val, span);
            }
            Ty::FixStr(_) => {
                let val = if let Val::FixStr(v) = val { v } else { unreachable!() };
                ExprKind::Literal(Spanned::new(TokenKind::StringLiteral(val.clone()), span))
            }
            _ => unimplemented!(),
        };
        Spanned::new(kind, span)
    }

    pub fn reconstruct_item(&self, item: &HierItem, list: &mut Vec<Item>) {
        match item {
            HierItem::Param(decl) => {
                let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                let expr = self.reconstruct_val(&decl.ty, &decl.init, Span::none());
                list.push(Item::ParamDecl(Box::new(ParamDecl {
                    kw: decl.kw,
                    ty: Some(Box::new(ty)),
                    list: vec![DeclAssign {
                        name: decl.name.clone(),
                        dim,
                        init: Some(Box::new(expr)),
                    }]
                })));
            }
            HierItem::Type(decl) => {
                let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                list.push(Item::Typedef(None, Box::new(ty), Box::new(decl.name.clone()), dim));
            }
            HierItem::Design(decl) => {
                // Reconstruct all instantiations and display them here
                for (_, inst) in decl.instances.borrow().iter() {
                    list.push(self.reconstruct_instantiation(inst));
                }
            }
            HierItem::Instance(decl) => {
                list.push(Item::HierInstantiation(Box::new(HierInstantiation {
                    attr: None,
                    name: decl.inst.name.clone(),
                    param: None,
                        inst: vec![HierInst {
                        name: decl.name.clone(),
                        dim: decl.dim.iter().map(|(ub, lb)| {
                            let ub_expr = self.reconstruct_int(*ub, Span::none());
                            let lb_expr = self.reconstruct_int(*lb, Span::none());
                            Spanned::new(DimKind::Range(Box::new(ub_expr), Box::new(lb_expr)), Span::none())
                        }).collect(),
                        ports: decl.port.clone(),
                    }]
                })))
            }
            HierItem::Other(item) => list.push(Item::clone(item)),
            HierItem::OtherName => (),
            HierItem::GenBlock(genblk) => {
                // Reconstruct all interior items
                let mut sublist = Vec::new();
                for item in &genblk.scope.items {
                    self.reconstruct_item(item, &mut sublist);
                }
                list.push(Item::IfGen(Box::new(IfGen {
                    attr: None,
                    if_block: vec![{
                        (self.reconstruct_const(&LogicValue::One.into(), Span::none()), GenBlock {
                            name: genblk.name.clone(),
                            items: sublist,
                        })
                    }],
                    else_block: None,
                })));
            }
            HierItem::LoopGenBlock(loopgenblk) => {
                for (_, genblk) in loopgenblk.instances.borrow().iter() {
                    // Reconstruct all interior items
                    let mut sublist = Vec::new();
                    for item in &genblk.scope.items {
                        self.reconstruct_item(item, &mut sublist);
                    }
                    list.push(Item::IfGen(Box::new(IfGen {
                        attr: None,
                        if_block: vec![{
                            (self.reconstruct_const(&LogicValue::One.into(), Span::none()), GenBlock {
                                name: genblk.name.clone(),
                                items: sublist,
                            })
                        }],
                        else_block: None,
                    })));
                }
            }
            HierItem::Modport(_) => (),
            HierItem::Enum(enu, index) => {
                let enum_index = self.source.enums.iter().position(|x| x == enu).unwrap();
                let element_name = enu.elements.borrow()[*index].0.clone();
                let expr = Box::new(Spanned::new_unspanned(ExprKind::HierName(
                    Some(Scope::Name(None, Box::new(Ident::new_unspanned("global_types".to_owned())))),
                    HierId::Name(Box::new(Ident::new_unspanned(format!("enum_{}_{}", enum_index, element_name)))),
                )));

                list.push(Item::ParamDecl(Box::new(ParamDecl {
                    kw: Keyword::Parameter,
                    ty: None,
                    list: vec![DeclAssign {
                        name: element_name,
                        dim: Vec::new(),
                        init: Some(expr),
                    }]
                })));
            }
            v => unimplemented!("{:?}", std::mem::discriminant(v)),
        }
    }

    pub fn reconstruct_instantiation(&self, inst: &hier::DesignInstantiation) -> Item {
        // Reconstruct all parameters
        let mut params = Vec::new();
        for item in &inst.scope.items {
            if let HierItem::Param(_) = item {
                self.reconstruct_item(item, &mut params);
            } else {
                break;
            }
        }
        let params: Vec<_> = params.into_iter().map(|x| {
            if let Item::ParamDecl(decl) = x { *decl } else { unreachable!() }
        }).collect();

        // Reconstruct all ports
        let mut ports = Vec::new();
        for item in inst.scope.items.iter().skip(params.len()) {
            match item {
                HierItem::DataPort(decl) => {
                    let (ty, dim) = self.reconstruct_ty(&decl.ty, Span::none());
                    ports.push(PortDecl::Data(decl.dir, decl.net.clone(), Box::new(ty), vec![DeclAssign {
                        name: decl.name.clone(),
                        dim,
                        init: decl.init.clone(),
                    }]));
                }
                HierItem::InterfacePort(decl) => {
                    ports.push(PortDecl::Interface(Some(Box::new(decl.inst.name.clone())), None, vec![DeclAssign {
                        name: decl.name.clone(),
                        dim: Vec::new(),
                        init: None,
                    }]));
                }
                _ => break,
            }
        }

        // Now chunk them into params, port and items
        let mut list = Vec::new();
        for item in inst.scope.items.iter().skip(params.len() + ports.len()) {
            self.reconstruct_item(&item, &mut list);
        }

        Item::DesignDecl(Box::new(DesignDecl {
            attr: None,
            kw: inst.ast.kw,
            lifetime: inst.ast.lifetime,
            name: inst.name.clone(),
            pkg_import: Vec::new(),
            param: if !params.is_empty() { Some(params) } else { None },
            port: ports,
            items: list,
        }))
    }

    pub fn reconstruct(&mut self) -> Vec<Vec<Item>> {
        let mut units = Vec::new();

        // Build global unit: contain packages and typedefs
        let mut list = Vec::new();
        list.push({
            let mut types = Vec::new();
            types.extend(self.source.enums.iter().enumerate().map(|(index, enu)| {
                let prefix = format!("enum_{}", index);
                let enu = self.reconstruct_enum(enu, &prefix);
                let ty = Spanned::new(DataTypeKind::Enum(enu, Vec::new()), Span::none());
                let name = Ident::new(prefix, Span::none());
                Item::Typedef(None, Box::new(ty), Box::new(name), Vec::new())
            }));
            types.extend(self.source.structs.iter().enumerate().map(|(index, struc)| {
                let struc = self.reconstruct_struct(struc);
                let ty = Spanned::new(DataTypeKind::Aggr(struc, Vec::new()), Span::none());
                let name = Ident::new(format!("struct_{}", index), Span::none());
                Item::Typedef(None, Box::new(ty), Box::new(name), Vec::new())
            }));
            self.global_qualify = true;
            Item::PkgDecl(Box::new(PkgDecl {
                attr: None,
                lifetime: Lifetime::Static,
                name: Ident::new_unspanned("global_types".to_owned()),
                items: types
            }))
        });
        list.extend(self.source.pkgs.iter().map(|(_, decl)| {
            let mut list = Vec::new();
            for item in &decl.scope.items {
                self.reconstruct_item(item, &mut list);
            }
            Item::PkgDecl(Box::new(PkgDecl {
                attr: None,
                lifetime: Lifetime::Static,
                name: decl.name.clone(),
                items: list
            }))
        }));
        units.push(list);

        for unit in &self.source.units {
            let mut list = Vec::new();
            for item in &unit.items {
                self.reconstruct_item(item, &mut list);
            }
            units.push(list);
        }
        units
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GtkTreeSelection — The selection object for GtkTreeView

use glib;
use ffi;
use {TreeView, TreePath, TreeIter};

pub struct TreeSelection {
    pointer: *mut ffi::C_GtkTreeSelection
}

impl TreeSelection {
    pub fn set_mode(&self, mode: ::SelectionMode) {
        unsafe { ffi::gtk_tree_selection_set_mode(self.pointer, mode) }
    }

    pub fn get_mode(&self) -> ::SelectionMode{
        unsafe { ffi::gtk_tree_selection_get_mode(self.pointer) }
    }

    pub fn get_user_data<'r, T>(&self) -> &'r mut T {
        unsafe { ::std::mem::transmute(ffi::gtk_tree_selection_get_user_data(self.pointer)) }
    }

    pub fn get_tree_view(&self) -> Option<TreeView> {
        let tmp_pointer = unsafe { ffi::gtk_tree_selection_get_tree_view(self.pointer) } as *mut ffi::C_GtkWidget;

        if tmp_pointer.is_null() {
            None
        } else {
            Some(::FFIWidget::wrap_widget(tmp_pointer))
        }
    }

    pub fn get_selected(&self, model: &::TreeModel, iter: &mut ::TreeIter) -> bool {
        match unsafe { ffi::gtk_tree_selection_get_selected(self.pointer, &mut model.unwrap_pointer(),
            iter.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn count_selected_rows(&self) -> i32 {
        unsafe { ffi::gtk_tree_selection_count_selected_rows(self.pointer) }
    }

    pub fn select_path(&self, path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_select_path(self.pointer, path.unwrap_pointer()) }
    }

    pub fn unselect_path(&self, path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_unselect_path(self.pointer, path.unwrap_pointer()) }
    }

    pub fn path_is_selected(&self, path: &TreePath) -> bool {
        match unsafe { ffi::gtk_tree_selection_path_is_selected(self.pointer, path.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn select_iter(&self, iter: &TreeIter) {
        unsafe { ffi::gtk_tree_selection_select_iter(self.pointer, iter.unwrap_pointer()) }
    }

    pub fn unselect_iter(&self, iter: &TreeIter) {
        unsafe { ffi::gtk_tree_selection_unselect_iter(self.pointer, iter.unwrap_pointer()) }
    }

    pub fn iter_is_selected(&self, iter: &TreeIter) -> bool {
        match unsafe { ffi::gtk_tree_selection_iter_is_selected(self.pointer, iter.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn select_all(&self) {
        unsafe { ffi::gtk_tree_selection_select_all(self.pointer) }
    }

    pub fn unselect_all(&self) {
        unsafe { ffi::gtk_tree_selection_unselect_all(self.pointer) }
    }

    pub fn select_range(&self, start_path: &TreePath, end_path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_select_range(self.pointer, start_path.unwrap_pointer(),
            end_path.unwrap_pointer()) }
    }

    pub fn unselect_range(&self, start_path: &TreePath, end_path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_unselect_range(self.pointer, start_path.unwrap_pointer(),
            end_path.unwrap_pointer()) }
    }

    pub fn wrap(pointer: *mut ffi::C_GtkTreeSelection) -> Option<TreeSelection> {
        if pointer.is_null() {
            None
        } else {
            Some(TreeSelection { pointer: pointer })
        }
    }
}

impl glib::traits::FFIGObject for TreeSelection {
    fn unwrap_gobject(&self) -> *mut glib::ffi::C_GObject {
        ::cast::G_OBJECT_FROM_TREE_SELECTION(self.pointer)
    }

    fn wrap_object(object: *mut glib::ffi::C_GObject) -> TreeSelection {
        TreeSelection { pointer: object as *mut ffi::C_GtkTreeSelection }
    }
}

impl_drop!(TreeSelection, GTK_TREE_SELECTION);
<commit_msg>TreeSelection: increase refcount when wrapping a pointer<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! GtkTreeSelection — The selection object for GtkTreeView

use glib;
use ffi;
use {TreeView, TreePath, TreeIter};

pub struct TreeSelection {
    pointer: *mut ffi::C_GtkTreeSelection
}

impl TreeSelection {
    pub fn set_mode(&self, mode: ::SelectionMode) {
        unsafe { ffi::gtk_tree_selection_set_mode(self.pointer, mode) }
    }

    pub fn get_mode(&self) -> ::SelectionMode{
        unsafe { ffi::gtk_tree_selection_get_mode(self.pointer) }
    }

    pub fn get_user_data<'r, T>(&self) -> &'r mut T {
        unsafe { ::std::mem::transmute(ffi::gtk_tree_selection_get_user_data(self.pointer)) }
    }

    pub fn get_tree_view(&self) -> Option<TreeView> {
        let tmp_pointer = unsafe { ffi::gtk_tree_selection_get_tree_view(self.pointer) } as *mut ffi::C_GtkWidget;

        if tmp_pointer.is_null() {
            None
        } else {
            Some(::FFIWidget::wrap_widget(tmp_pointer))
        }
    }

    pub fn get_selected(&self, model: &::TreeModel, iter: &mut ::TreeIter) -> bool {
        match unsafe { ffi::gtk_tree_selection_get_selected(self.pointer, &mut model.unwrap_pointer(),
            iter.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn count_selected_rows(&self) -> i32 {
        unsafe { ffi::gtk_tree_selection_count_selected_rows(self.pointer) }
    }

    pub fn select_path(&self, path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_select_path(self.pointer, path.unwrap_pointer()) }
    }

    pub fn unselect_path(&self, path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_unselect_path(self.pointer, path.unwrap_pointer()) }
    }

    pub fn path_is_selected(&self, path: &TreePath) -> bool {
        match unsafe { ffi::gtk_tree_selection_path_is_selected(self.pointer, path.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn select_iter(&self, iter: &TreeIter) {
        unsafe { ffi::gtk_tree_selection_select_iter(self.pointer, iter.unwrap_pointer()) }
    }

    pub fn unselect_iter(&self, iter: &TreeIter) {
        unsafe { ffi::gtk_tree_selection_unselect_iter(self.pointer, iter.unwrap_pointer()) }
    }

    pub fn iter_is_selected(&self, iter: &TreeIter) -> bool {
        match unsafe { ffi::gtk_tree_selection_iter_is_selected(self.pointer, iter.unwrap_pointer()) } {
            0 => false,
            _ => true
        }
    }

    pub fn select_all(&self) {
        unsafe { ffi::gtk_tree_selection_select_all(self.pointer) }
    }

    pub fn unselect_all(&self) {
        unsafe { ffi::gtk_tree_selection_unselect_all(self.pointer) }
    }

    pub fn select_range(&self, start_path: &TreePath, end_path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_select_range(self.pointer, start_path.unwrap_pointer(),
            end_path.unwrap_pointer()) }
    }

    pub fn unselect_range(&self, start_path: &TreePath, end_path: &TreePath) {
        unsafe { ffi::gtk_tree_selection_unselect_range(self.pointer, start_path.unwrap_pointer(),
            end_path.unwrap_pointer()) }
    }

    pub fn wrap(pointer: *mut ffi::C_GtkTreeSelection) -> Option<TreeSelection> {
        if pointer.is_null() {
            None
        } else {
            unsafe { ::glib_ffi::g_object_ref(pointer as *mut _); }
            Some(TreeSelection { pointer: pointer })
        }
    }
}

impl glib::traits::FFIGObject for TreeSelection {
    fn unwrap_gobject(&self) -> *mut glib::ffi::C_GObject {
        ::cast::G_OBJECT_FROM_TREE_SELECTION(self.pointer)
    }

    fn wrap_object(object: *mut glib::ffi::C_GObject) -> TreeSelection {
        unsafe { ::glib_ffi::g_object_ref(object as *mut _); }
        TreeSelection { pointer: object as *mut ffi::C_GtkTreeSelection }
    }
}

impl_drop!(TreeSelection, GTK_TREE_SELECTION);
<|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 errors::Result;
use errors::ResultExt;
use zip::Zip;
use zip_library::ZipLibrary;
use zip_command::ZipCommand;

use std::path::Path;
use std::io::Read;
use std::io::Write;

/// Wrapper around either a ZipCommand or a ZipLibrary
///
/// Allows to create an EpubBuilder that can decide at runtime which to use.
pub enum ZipCommandOrLibrary {
    /// Command variant
    Command(ZipCommand),
    /// Library variant
    Library(ZipLibrary)
}

impl Zip for ZipCommandOrLibrary {
    fn write_file<P: AsRef<Path>, R: Read>(&mut self, path: P, content: R) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.write_file(path, content),
            ZipCommandOrLibrary::Library(ref mut library) => library.write_file(path, content)
        }
    }

    fn generate<W: Write>(&mut self, to: W) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.generate(to),
            ZipCommandOrLibrary::Library(ref mut library) => library.generate(to),
        }
    }
}

impl ZipCommandOrLibrary {
    /// Try to create a ZipCommand using `command`. If running `command` fails on the system,
    /// fall back to `ZipLibrary`.
    fn new(command: &str) -> Result<ZipCommandOrLibrary> {
        ZipCommand::new()
            .map(|mut z| {
            z.command(command);
            z})
            .and_then(|z| z.test().map(|_| z))
            .map(|z| ZipCommandOrLibrary::Command(z))
            .or_else(|_| ZipLibrary::new()
                     .map(|l| ZipCommandOrLibrary::Library(l)))
    }
}
<commit_msg>Make ZipCommandOrLibrary::new public<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 errors::Result;
use errors::ResultExt;
use zip::Zip;
use zip_library::ZipLibrary;
use zip_command::ZipCommand;

use std::path::Path;
use std::io::Read;
use std::io::Write;

/// Wrapper around either a ZipCommand or a ZipLibrary
///
/// Allows to create an EpubBuilder that can decide at runtime which to use.
pub enum ZipCommandOrLibrary {
    /// Command variant
    Command(ZipCommand),
    /// Library variant
    Library(ZipLibrary)
}

impl Zip for ZipCommandOrLibrary {
    fn write_file<P: AsRef<Path>, R: Read>(&mut self, path: P, content: R) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.write_file(path, content),
            ZipCommandOrLibrary::Library(ref mut library) => library.write_file(path, content)
        }
    }

    fn generate<W: Write>(&mut self, to: W) -> Result<()> {
        match self {
            ZipCommandOrLibrary::Command(ref mut command) => command.generate(to),
            ZipCommandOrLibrary::Library(ref mut library) => library.generate(to),
        }
    }
}

impl ZipCommandOrLibrary {
    /// Try to create a ZipCommand using `command`. If running `command` fails on the system,
    /// fall back to `ZipLibrary`.
    pub fn new(command: &str) -> Result<ZipCommandOrLibrary> {
        ZipCommand::new()
            .map(|mut z| {
            z.command(command);
            z})
            .and_then(|z| z.test().map(|_| z))
            .map(|z| ZipCommandOrLibrary::Command(z))
            .or_else(|_| ZipLibrary::new()
                     .map(|l| ZipCommandOrLibrary::Library(l)))
    }
}
<|endoftext|>


<commit_before>use super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use clippy_utils::{is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Ident};

const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";

pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    check_needless_collect_direct_usage(expr, cx);
    check_needless_collect_indirect_usage(expr, cx);
}
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
        if let ExprKind::MethodCall(ref chain_method, method0_span, _, _) = args[0].kind;
        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
        if let Some(ref generic_args) = chain_method.args;
        if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
        let ty = cx.typeck_results().node_type(ty.hir_id);
        if is_type_diagnostic_item(cx, ty, sym::vec_type)
            || is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
            || match_type(cx, ty, &paths::BTREEMAP)
            || is_type_diagnostic_item(cx, ty, sym::hashmap_type);
        if let Some(sugg) = match &*method.ident.name.as_str() {
            "len" => Some("count()".to_string()),
            "is_empty" => Some("next().is_none()".to_string()),
            "contains" => {
                let contains_arg = snippet(cx, args[1].span, "??");
                let (arg, pred) = contains_arg
                    .strip_prefix('&')
                    .map_or(("&x", &*contains_arg), |s| ("x", s));
                Some(format!("any(|{}| x == {})", arg, pred))
            }
            _ => None,
        };
        then {
            span_lint_and_sugg(
                cx,
                NEEDLESS_COLLECT,
                method0_span.with_hi(expr.span.hi()),
                NEEDLESS_COLLECT_MSG,
                "replace with",
                sugg,
                Applicability::MachineApplicable,
            );
        }
    }
}

fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    if let ExprKind::Block(ref block, _) = expr.kind {
        for ref stmt in block.stmts {
            if_chain! {
                if let StmtKind::Local(
                    Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
                    init: Some(ref init_expr), .. }
                ) = stmt.kind;
                if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
                if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator);
                if let Some(ref generic_args) = method_name.args;
                if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
                if let ty = cx.typeck_results().node_type(ty.hir_id);
                if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
                    is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
                    match_type(cx, ty, &paths::LINKED_LIST);
                if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
                if iter_calls.len() == 1;
                then {
                    let mut used_count_visitor = UsedCountVisitor {
                        cx,
                        id: *pat_id,
                        count: 0,
                    };
                    walk_block(&mut used_count_visitor, block);
                    if used_count_visitor.count > 1 {
                        return;
                    }

                    // Suggest replacing iter_call with iter_replacement, and removing stmt
                    let iter_call = &iter_calls[0];
                    span_lint_and_then(
                        cx,
                        super::NEEDLESS_COLLECT,
                        stmt.span.until(iter_call.span),
                        NEEDLESS_COLLECT_MSG,
                        |diag| {
                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
                            diag.multipart_suggestion(
                                iter_call.get_suggestion_text(),
                                vec![
                                    (stmt.span, String::new()),
                                    (iter_call.span, iter_replacement)
                                ],
                                Applicability::MachineApplicable,// MaybeIncorrect,
                            ).emit();
                        },
                    );
                }
            }
        }
    }
}

struct IterFunction {
    func: IterFunctionKind,
    span: Span,
}
impl IterFunction {
    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
        match &self.func {
            IterFunctionKind::IntoIter => String::new(),
            IterFunctionKind::Len => String::from(".count()"),
            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
            IterFunctionKind::Contains(span) => {
                let s = snippet(cx, *span, "..");
                if let Some(stripped) = s.strip_prefix('&') {
                    format!(".any(|x| x == {})", stripped)
                } else {
                    format!(".any(|x| x == *{})", s)
                }
            },
        }
    }
    fn get_suggestion_text(&self) -> &'static str {
        match &self.func {
            IterFunctionKind::IntoIter => {
                "use the original Iterator instead of collecting it and then producing a new one"
            },
            IterFunctionKind::Len => {
                "take the original Iterator's count instead of collecting it and finding the length"
            },
            IterFunctionKind::IsEmpty => {
                "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
            },
            IterFunctionKind::Contains(_) => {
                "check if the original Iterator contains an element instead of collecting then checking"
            },
        }
    }
}
enum IterFunctionKind {
    IntoIter,
    Len,
    IsEmpty,
    Contains(Span),
}

struct IterFunctionVisitor {
    uses: Vec<IterFunction>,
    seen_other: bool,
    target: Ident,
}
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
        // Check function calls on our collection
        if_chain! {
            if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
            if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
            if let &[name] = &path.segments;
            if name.ident == self.target;
            then {
                let len = sym!(len);
                let is_empty = sym!(is_empty);
                let contains = sym!(contains);
                match method_name.ident.name {
                    sym::into_iter => self.uses.push(
                        IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
                    ),
                    name if name == len => self.uses.push(
                        IterFunction { func: IterFunctionKind::Len, span: expr.span }
                    ),
                    name if name == is_empty => self.uses.push(
                        IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
                    ),
                    name if name == contains => self.uses.push(
                        IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
                    ),
                    _ => self.seen_other = true,
                }
                return
            }
        }
        // Check if the collection is used for anything else
        if_chain! {
            if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
            if let &[name] = &path.segments;
            if name.ident == self.target;
            then {
                self.seen_other = true;
            } else {
                walk_expr(self, expr);
            }
        }
    }

    type Map = Map<'tcx>;
    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

struct UsedCountVisitor<'a, 'tcx> {
    cx: &'a LateContext<'tcx>,
    id: HirId,
    count: usize,
}

impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if path_to_local_id(expr, self.id) {
            self.count += 1;
        } else {
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
    }
}

/// Detect the occurrences of calls to `iter` or `into_iter` for the
/// given identifier
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
    let mut visitor = IterFunctionVisitor {
        uses: Vec::new(),
        target: identifier,
        seen_other: false,
    };
    visitor.visit_block(block);
    if visitor.seen_other { None } else { Some(visitor.uses) }
}
<commit_msg>Remove redundant emit()<commit_after>use super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use clippy_utils::{is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, Ident};

const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";

pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    check_needless_collect_direct_usage(expr, cx);
    check_needless_collect_indirect_usage(expr, cx);
}
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    if_chain! {
        if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
        if let ExprKind::MethodCall(ref chain_method, method0_span, _, _) = args[0].kind;
        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
        if let Some(ref generic_args) = chain_method.args;
        if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
        let ty = cx.typeck_results().node_type(ty.hir_id);
        if is_type_diagnostic_item(cx, ty, sym::vec_type)
            || is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
            || match_type(cx, ty, &paths::BTREEMAP)
            || is_type_diagnostic_item(cx, ty, sym::hashmap_type);
        if let Some(sugg) = match &*method.ident.name.as_str() {
            "len" => Some("count()".to_string()),
            "is_empty" => Some("next().is_none()".to_string()),
            "contains" => {
                let contains_arg = snippet(cx, args[1].span, "??");
                let (arg, pred) = contains_arg
                    .strip_prefix('&')
                    .map_or(("&x", &*contains_arg), |s| ("x", s));
                Some(format!("any(|{}| x == {})", arg, pred))
            }
            _ => None,
        };
        then {
            span_lint_and_sugg(
                cx,
                NEEDLESS_COLLECT,
                method0_span.with_hi(expr.span.hi()),
                NEEDLESS_COLLECT_MSG,
                "replace with",
                sugg,
                Applicability::MachineApplicable,
            );
        }
    }
}

fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
    if let ExprKind::Block(ref block, _) = expr.kind {
        for ref stmt in block.stmts {
            if_chain! {
                if let StmtKind::Local(
                    Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
                    init: Some(ref init_expr), .. }
                ) = stmt.kind;
                if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
                if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator);
                if let Some(ref generic_args) = method_name.args;
                if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
                if let ty = cx.typeck_results().node_type(ty.hir_id);
                if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
                    is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
                    match_type(cx, ty, &paths::LINKED_LIST);
                if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
                if iter_calls.len() == 1;
                then {
                    let mut used_count_visitor = UsedCountVisitor {
                        cx,
                        id: *pat_id,
                        count: 0,
                    };
                    walk_block(&mut used_count_visitor, block);
                    if used_count_visitor.count > 1 {
                        return;
                    }

                    // Suggest replacing iter_call with iter_replacement, and removing stmt
                    let iter_call = &iter_calls[0];
                    span_lint_and_then(
                        cx,
                        super::NEEDLESS_COLLECT,
                        stmt.span.until(iter_call.span),
                        NEEDLESS_COLLECT_MSG,
                        |diag| {
                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
                            diag.multipart_suggestion(
                                iter_call.get_suggestion_text(),
                                vec![
                                    (stmt.span, String::new()),
                                    (iter_call.span, iter_replacement)
                                ],
                                Applicability::MachineApplicable,// MaybeIncorrect,
                            );
                        },
                    );
                }
            }
        }
    }
}

struct IterFunction {
    func: IterFunctionKind,
    span: Span,
}
impl IterFunction {
    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
        match &self.func {
            IterFunctionKind::IntoIter => String::new(),
            IterFunctionKind::Len => String::from(".count()"),
            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
            IterFunctionKind::Contains(span) => {
                let s = snippet(cx, *span, "..");
                if let Some(stripped) = s.strip_prefix('&') {
                    format!(".any(|x| x == {})", stripped)
                } else {
                    format!(".any(|x| x == *{})", s)
                }
            },
        }
    }
    fn get_suggestion_text(&self) -> &'static str {
        match &self.func {
            IterFunctionKind::IntoIter => {
                "use the original Iterator instead of collecting it and then producing a new one"
            },
            IterFunctionKind::Len => {
                "take the original Iterator's count instead of collecting it and finding the length"
            },
            IterFunctionKind::IsEmpty => {
                "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
            },
            IterFunctionKind::Contains(_) => {
                "check if the original Iterator contains an element instead of collecting then checking"
            },
        }
    }
}
enum IterFunctionKind {
    IntoIter,
    Len,
    IsEmpty,
    Contains(Span),
}

struct IterFunctionVisitor {
    uses: Vec<IterFunction>,
    seen_other: bool,
    target: Ident,
}
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
        // Check function calls on our collection
        if_chain! {
            if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind;
            if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0);
            if let &[name] = &path.segments;
            if name.ident == self.target;
            then {
                let len = sym!(len);
                let is_empty = sym!(is_empty);
                let contains = sym!(contains);
                match method_name.ident.name {
                    sym::into_iter => self.uses.push(
                        IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
                    ),
                    name if name == len => self.uses.push(
                        IterFunction { func: IterFunctionKind::Len, span: expr.span }
                    ),
                    name if name == is_empty => self.uses.push(
                        IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
                    ),
                    name if name == contains => self.uses.push(
                        IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
                    ),
                    _ => self.seen_other = true,
                }
                return
            }
        }
        // Check if the collection is used for anything else
        if_chain! {
            if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr;
            if let &[name] = &path.segments;
            if name.ident == self.target;
            then {
                self.seen_other = true;
            } else {
                walk_expr(self, expr);
            }
        }
    }

    type Map = Map<'tcx>;
    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::None
    }
}

struct UsedCountVisitor<'a, 'tcx> {
    cx: &'a LateContext<'tcx>,
    id: HirId,
    count: usize,
}

impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
    type Map = Map<'tcx>;

    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
        if path_to_local_id(expr, self.id) {
            self.count += 1;
        } else {
            walk_expr(self, expr);
        }
    }

    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
    }
}

/// Detect the occurrences of calls to `iter` or `into_iter` for the
/// given identifier
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
    let mut visitor = IterFunctionVisitor {
        uses: Vec::new(),
        target: identifier,
        seen_other: false,
    };
    visitor.visit_block(block);
    if visitor.seen_other { None } else { Some(visitor.uses) }
}
<|endoftext|>


<commit_before>#![feature(allocator_api)]
#![feature(box_syntax)]
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(map_first_last)]
#![feature(new_uninit)]
#![feature(pattern)]
#![feature(trusted_len)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(associated_type_bounds)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(binary_heap_drain_sorted)]
#![feature(vec_remove_item)]

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

mod arc;
mod binary_heap;
mod boxed;
mod btree;
mod cow_str;
mod fmt;
mod heap;
mod linked_list;
mod rc;
mod slice;
mod str;
mod string;
mod vec;
mod vec_deque;

fn hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten.
// See https://github.com/kripken/emscripten-fastcomp/issues/169
#[cfg(not(target_os = "emscripten"))]
#[test]
fn test_boxed_hasher() {
    let ordinary_hash = hash(&5u32);

    let mut hasher_1 = Box::new(DefaultHasher::new());
    5u32.hash(&mut hasher_1);
    assert_eq!(ordinary_hash, hasher_1.finish());

    let mut hasher_2 = Box::new(DefaultHasher::new()) as Box<dyn Hasher>;
    5u32.hash(&mut hasher_2);
    assert_eq!(ordinary_hash, hasher_2.finish());
}
<commit_msg>oh the one that was left behind<commit_after>#![feature(allocator_api)]
#![feature(box_syntax)]
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(map_first_last)]
#![feature(new_uninit)]
#![feature(pattern)]
#![feature(trusted_len)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
#![feature(associated_type_bounds)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(binary_heap_drain_sorted)]

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

mod arc;
mod binary_heap;
mod boxed;
mod btree;
mod cow_str;
mod fmt;
mod heap;
mod linked_list;
mod rc;
mod slice;
mod str;
mod string;
mod vec;
mod vec_deque;

fn hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten.
// See https://github.com/kripken/emscripten-fastcomp/issues/169
#[cfg(not(target_os = "emscripten"))]
#[test]
fn test_boxed_hasher() {
    let ordinary_hash = hash(&5u32);

    let mut hasher_1 = Box::new(DefaultHasher::new());
    5u32.hash(&mut hasher_1);
    assert_eq!(ordinary_hash, hasher_1.finish());

    let mut hasher_2 = Box::new(DefaultHasher::new()) as Box<dyn Hasher>;
    5u32.hash(&mut hasher_2);
    assert_eq!(ordinary_hash, hasher_2.finish());
}
<|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 canvas_traits::{CanvasMsg, FromScriptMsg};
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
use dom::bindings::codegen::UnionTypes::CanvasRenderingContext2DOrWebGLRenderingContext;
use dom::bindings::conversions::ConversionResult;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, Root};
use dom::bindings::num::Finite;
use dom::bindings::str::DOMString;
use dom::canvasrenderingcontext2d::{CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers};
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use dom::globalscope::GlobalScope;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
use euclid::size::Size2D;
use html5ever_atoms::LocalName;
use image::ColorType;
use image::png::PNGEncoder;
use ipc_channel::ipc::{self, IpcSender};
use js::error::throw_type_error;
use js::jsapi::{HandleValue, JSContext};
use offscreen_gl_context::GLContextAttributes;
use rustc_serialize::base64::{STANDARD, ToBase64};
use script_layout_interface::HTMLCanvasData;
use std::iter::repeat;
use style::attr::AttrValue;

const DEFAULT_WIDTH: u32 = 300;
const DEFAULT_HEIGHT: u32 = 150;

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
pub enum CanvasContext {
    Context2d(JS<CanvasRenderingContext2D>),
    WebGL(JS<WebGLRenderingContext>),
}

#[dom_struct]
pub struct HTMLCanvasElement {
    htmlelement: HTMLElement,
    context: DOMRefCell<Option<CanvasContext>>,
}

impl HTMLCanvasElement {
    fn new_inherited(local_name: LocalName,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLCanvasElement {
        HTMLCanvasElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            context: DOMRefCell::new(None),
        }
    }

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

    fn recreate_contexts(&self) {
        let size = self.get_size();
        if let Some(ref context) = *self.context.borrow() {
            match *context {
                CanvasContext::Context2d(ref context) => context.set_bitmap_dimensions(size),
                CanvasContext::WebGL(ref context) => context.recreate(size),
            }
        }
    }

    pub fn get_size(&self) -> Size2D<i32> {
        Size2D::new(self.Width() as i32, self.Height() as i32)
    }

    pub fn origin_is_clean(&self) -> bool {
        match *self.context.borrow() {
            Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(),
            _ => true,
        }
    }
}

pub trait LayoutHTMLCanvasElementHelpers {
    fn data(&self) -> HTMLCanvasData;
}

impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> {
    #[allow(unsafe_code)]
    fn data(&self) -> HTMLCanvasData {
        unsafe {
            let canvas = &*self.unsafe_get();
            let ipc_renderer = canvas.context.borrow_for_layout().as_ref().map(|context| {
                match *context {
                    CanvasContext::Context2d(ref context) => {
                        context.to_layout().get_ipc_renderer()
                    },
                    CanvasContext::WebGL(ref context) => {
                        context.to_layout().get_ipc_renderer()
                    },
                }
            });

            let width_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("width"));
            let height_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("height"));
            HTMLCanvasData {
                ipc_renderer: ipc_renderer,
                width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
                height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
            }
        }
    }
}

impl HTMLCanvasElement {
    pub fn ipc_renderer(&self) -> Option<IpcSender<CanvasMsg>> {
        self.context.borrow().as_ref().map(|context| {
            match *context {
                CanvasContext::Context2d(ref context) => context.ipc_renderer(),
                CanvasContext::WebGL(ref context) => context.ipc_renderer(),
            }
        })
    }

    pub fn get_or_init_2d_context(&self) -> Option<Root<CanvasRenderingContext2D>> {
        if self.context.borrow().is_none() {
            let window = window_from_node(self);
            let size = self.get_size();
            let context = CanvasRenderingContext2D::new(window.upcast::<GlobalScope>(), self, size);
            *self.context.borrow_mut() = Some(CanvasContext::Context2d(JS::from_ref(&*context)));
        }

        match *self.context.borrow().as_ref().unwrap() {
            CanvasContext::Context2d(ref context) => Some(Root::from_ref(&*context)),
            _   => None,
        }
    }

    #[allow(unsafe_code)]
    pub fn get_or_init_webgl_context(&self,
                                 cx: *mut JSContext,
                                 attrs: Option<HandleValue>) -> Option<Root<WebGLRenderingContext>> {
        if self.context.borrow().is_none() {
            let window = window_from_node(self);
            let size = self.get_size();

            let attrs = if let Some(webgl_attributes) = attrs {
                match unsafe {
                    WebGLContextAttributes::new(cx, webgl_attributes) } {
                    Ok(ConversionResult::Success(ref attrs)) => From::from(attrs),
                    Ok(ConversionResult::Failure(ref error)) => {
                        unsafe { throw_type_error(cx, &error); }
                        return None;
                    }
                    _ => {
                        debug!("Unexpected error on conversion of WebGLContextAttributes");
                        return None;
                    }
                }
            } else {
                GLContextAttributes::default()
            };

            let maybe_ctx = WebGLRenderingContext::new(&window, self, size, attrs);

            *self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL(JS::from_ref(&*ctx)));
        }

        if let Some(CanvasContext::WebGL(ref context)) = *self.context.borrow() {
            Some(Root::from_ref(&*context))
        } else {
            None
        }
    }

    pub fn is_valid(&self) -> bool {
        self.Height() != 0 && self.Width() != 0
    }

    pub fn fetch_all_data(&self) -> Option<(Vec<u8>, Size2D<i32>)> {
        let size = self.get_size();

        if size.width == 0 || size.height == 0 {
            return None
        }

        let data = if let Some(renderer) = self.ipc_renderer() {
            let (sender, receiver) = ipc::channel().unwrap();
            let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
            renderer.send(msg).unwrap();

            match receiver.recv().unwrap() {
                Some(pixels) => pixels,
                None => {
                    // TODO(emilio, #14109): Not sure if WebGL canvas is
                    // required for 2d spec, but I think it's not, if so, make
                    // this return a readback from the GL context.
                    return None;
                }
            }
        } else {
            repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
        };

        Some((data, size))
    }
}

impl HTMLCanvasElementMethods for HTMLCanvasElement {
    // https://html.spec.whatwg.org/multipage/#dom-canvas-width
    make_uint_getter!(Width, "width", DEFAULT_WIDTH);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-width
    make_uint_setter!(SetWidth, "width", DEFAULT_WIDTH);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-height
    make_uint_getter!(Height, "height", DEFAULT_HEIGHT);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-height
    make_uint_setter!(SetHeight, "height", DEFAULT_HEIGHT);

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#dom-canvas-getcontext
    unsafe fn GetContext(&self,
                  cx: *mut JSContext,
                  id: DOMString,
                  attributes: Vec<HandleValue>)
        -> Option<CanvasRenderingContext2DOrWebGLRenderingContext> {
        match &*id {
            "2d" => {
                self.get_or_init_2d_context()
                    .map(CanvasRenderingContext2DOrWebGLRenderingContext::CanvasRenderingContext2D)
            }
            "webgl" | "experimental-webgl" => {
                self.get_or_init_webgl_context(cx, attributes.get(0).cloned())
                    .map(CanvasRenderingContext2DOrWebGLRenderingContext::WebGLRenderingContext)
            }
            _ => None
        }
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#dom-canvas-todataurl
    unsafe fn ToDataURL(&self,
                 _context: *mut JSContext,
                 _mime_type: Option<DOMString>,
                 _arguments: Vec<HandleValue>) -> Fallible<DOMString> {
        // Step 1.
        if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
            if !context.origin_is_clean() {
                return Err(Error::Security);
            }
        }

        // Step 2.
        if self.Width() == 0 || self.Height() == 0 {
            return Ok(DOMString::from("data:,"));
        }

        // Step 3.
        let raw_data = match *self.context.borrow() {
            Some(CanvasContext::Context2d(ref context)) => {
                let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64),
                                                           Finite::wrap(self.Width() as f64),
                                                           Finite::wrap(self.Height() as f64)));
                image_data.get_data_array()
            }
            None => {
                // Each pixel is fully-transparent black.
                vec![0; (self.Width() * self.Height() * 4) as usize]
            }
            _ => return Err(Error::NotSupported) // WebGL
        };

        // Only handle image/png for now.
        let mime_type = "image/png";

        let mut encoded = Vec::new();
        {
            let encoder: PNGEncoder<&mut Vec<u8>> = PNGEncoder::new(&mut encoded);
            encoder.encode(&raw_data, self.Width(), self.Height(), ColorType::RGBA(8)).unwrap();
        }

        let encoded = encoded.to_base64(STANDARD);
        Ok(DOMString::from(format!("data:{};base64,{}", mime_type, encoded)))
    }
}

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

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &local_name!("width") | &local_name!("height") => self.recreate_contexts(),
            _ => (),
        };
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH),
            &local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl<'a> From<&'a WebGLContextAttributes> for GLContextAttributes {
    fn from(attrs: &'a WebGLContextAttributes) -> GLContextAttributes {
        GLContextAttributes {
            alpha: attrs.alpha,
            depth: attrs.depth,
            stencil: attrs.stencil,
            antialias: attrs.antialias,
            premultiplied_alpha: attrs.premultipliedAlpha,
            preserve_drawing_buffer: attrs.preserveDrawingBuffer,
        }
    }
}

pub mod utils {
    use dom::window::Window;
    use net_traits::image_cache_thread::ImageResponse;
    use servo_url::ServoUrl;

    pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
        panic!()
        /*let image_cache = window.image_cache_thread();
        let (response_chan, response_port) = ipc::channel().unwrap();
        image_cache.request_image(url.into(), ImageCacheChan(response_chan), None);
        let result = response_port.recv().unwrap();
        match result {
            ImageCacheResult::InitiateRequest(..) => panic!("unexpected image request initiator"),
            ImageCacheResult::Response(result) => result.image_response,
        }*/
    }
}
<commit_msg>Fetch complete images from the cache for canvas operations.<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 canvas_traits::{CanvasMsg, FromScriptMsg};
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
use dom::bindings::codegen::UnionTypes::CanvasRenderingContext2DOrWebGLRenderingContext;
use dom::bindings::conversions::ConversionResult;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, LayoutJS, Root};
use dom::bindings::num::Finite;
use dom::bindings::str::DOMString;
use dom::canvasrenderingcontext2d::{CanvasRenderingContext2D, LayoutCanvasRenderingContext2DHelpers};
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use dom::globalscope::GlobalScope;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
use euclid::size::Size2D;
use html5ever_atoms::LocalName;
use image::ColorType;
use image::png::PNGEncoder;
use ipc_channel::ipc::{self, IpcSender};
use js::error::throw_type_error;
use js::jsapi::{HandleValue, JSContext};
use offscreen_gl_context::GLContextAttributes;
use rustc_serialize::base64::{STANDARD, ToBase64};
use script_layout_interface::HTMLCanvasData;
use std::iter::repeat;
use style::attr::AttrValue;

const DEFAULT_WIDTH: u32 = 300;
const DEFAULT_HEIGHT: u32 = 150;

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
pub enum CanvasContext {
    Context2d(JS<CanvasRenderingContext2D>),
    WebGL(JS<WebGLRenderingContext>),
}

#[dom_struct]
pub struct HTMLCanvasElement {
    htmlelement: HTMLElement,
    context: DOMRefCell<Option<CanvasContext>>,
}

impl HTMLCanvasElement {
    fn new_inherited(local_name: LocalName,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLCanvasElement {
        HTMLCanvasElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            context: DOMRefCell::new(None),
        }
    }

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

    fn recreate_contexts(&self) {
        let size = self.get_size();
        if let Some(ref context) = *self.context.borrow() {
            match *context {
                CanvasContext::Context2d(ref context) => context.set_bitmap_dimensions(size),
                CanvasContext::WebGL(ref context) => context.recreate(size),
            }
        }
    }

    pub fn get_size(&self) -> Size2D<i32> {
        Size2D::new(self.Width() as i32, self.Height() as i32)
    }

    pub fn origin_is_clean(&self) -> bool {
        match *self.context.borrow() {
            Some(CanvasContext::Context2d(ref context)) => context.origin_is_clean(),
            _ => true,
        }
    }
}

pub trait LayoutHTMLCanvasElementHelpers {
    fn data(&self) -> HTMLCanvasData;
}

impl LayoutHTMLCanvasElementHelpers for LayoutJS<HTMLCanvasElement> {
    #[allow(unsafe_code)]
    fn data(&self) -> HTMLCanvasData {
        unsafe {
            let canvas = &*self.unsafe_get();
            let ipc_renderer = canvas.context.borrow_for_layout().as_ref().map(|context| {
                match *context {
                    CanvasContext::Context2d(ref context) => {
                        context.to_layout().get_ipc_renderer()
                    },
                    CanvasContext::WebGL(ref context) => {
                        context.to_layout().get_ipc_renderer()
                    },
                }
            });

            let width_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("width"));
            let height_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("height"));
            HTMLCanvasData {
                ipc_renderer: ipc_renderer,
                width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
                height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
            }
        }
    }
}

impl HTMLCanvasElement {
    pub fn ipc_renderer(&self) -> Option<IpcSender<CanvasMsg>> {
        self.context.borrow().as_ref().map(|context| {
            match *context {
                CanvasContext::Context2d(ref context) => context.ipc_renderer(),
                CanvasContext::WebGL(ref context) => context.ipc_renderer(),
            }
        })
    }

    pub fn get_or_init_2d_context(&self) -> Option<Root<CanvasRenderingContext2D>> {
        if self.context.borrow().is_none() {
            let window = window_from_node(self);
            let size = self.get_size();
            let context = CanvasRenderingContext2D::new(window.upcast::<GlobalScope>(), self, size);
            *self.context.borrow_mut() = Some(CanvasContext::Context2d(JS::from_ref(&*context)));
        }

        match *self.context.borrow().as_ref().unwrap() {
            CanvasContext::Context2d(ref context) => Some(Root::from_ref(&*context)),
            _   => None,
        }
    }

    #[allow(unsafe_code)]
    pub fn get_or_init_webgl_context(&self,
                                 cx: *mut JSContext,
                                 attrs: Option<HandleValue>) -> Option<Root<WebGLRenderingContext>> {
        if self.context.borrow().is_none() {
            let window = window_from_node(self);
            let size = self.get_size();

            let attrs = if let Some(webgl_attributes) = attrs {
                match unsafe {
                    WebGLContextAttributes::new(cx, webgl_attributes) } {
                    Ok(ConversionResult::Success(ref attrs)) => From::from(attrs),
                    Ok(ConversionResult::Failure(ref error)) => {
                        unsafe { throw_type_error(cx, &error); }
                        return None;
                    }
                    _ => {
                        debug!("Unexpected error on conversion of WebGLContextAttributes");
                        return None;
                    }
                }
            } else {
                GLContextAttributes::default()
            };

            let maybe_ctx = WebGLRenderingContext::new(&window, self, size, attrs);

            *self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL(JS::from_ref(&*ctx)));
        }

        if let Some(CanvasContext::WebGL(ref context)) = *self.context.borrow() {
            Some(Root::from_ref(&*context))
        } else {
            None
        }
    }

    pub fn is_valid(&self) -> bool {
        self.Height() != 0 && self.Width() != 0
    }

    pub fn fetch_all_data(&self) -> Option<(Vec<u8>, Size2D<i32>)> {
        let size = self.get_size();

        if size.width == 0 || size.height == 0 {
            return None
        }

        let data = if let Some(renderer) = self.ipc_renderer() {
            let (sender, receiver) = ipc::channel().unwrap();
            let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
            renderer.send(msg).unwrap();

            match receiver.recv().unwrap() {
                Some(pixels) => pixels,
                None => {
                    // TODO(emilio, #14109): Not sure if WebGL canvas is
                    // required for 2d spec, but I think it's not, if so, make
                    // this return a readback from the GL context.
                    return None;
                }
            }
        } else {
            repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
        };

        Some((data, size))
    }
}

impl HTMLCanvasElementMethods for HTMLCanvasElement {
    // https://html.spec.whatwg.org/multipage/#dom-canvas-width
    make_uint_getter!(Width, "width", DEFAULT_WIDTH);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-width
    make_uint_setter!(SetWidth, "width", DEFAULT_WIDTH);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-height
    make_uint_getter!(Height, "height", DEFAULT_HEIGHT);

    // https://html.spec.whatwg.org/multipage/#dom-canvas-height
    make_uint_setter!(SetHeight, "height", DEFAULT_HEIGHT);

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#dom-canvas-getcontext
    unsafe fn GetContext(&self,
                  cx: *mut JSContext,
                  id: DOMString,
                  attributes: Vec<HandleValue>)
        -> Option<CanvasRenderingContext2DOrWebGLRenderingContext> {
        match &*id {
            "2d" => {
                self.get_or_init_2d_context()
                    .map(CanvasRenderingContext2DOrWebGLRenderingContext::CanvasRenderingContext2D)
            }
            "webgl" | "experimental-webgl" => {
                self.get_or_init_webgl_context(cx, attributes.get(0).cloned())
                    .map(CanvasRenderingContext2DOrWebGLRenderingContext::WebGLRenderingContext)
            }
            _ => None
        }
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#dom-canvas-todataurl
    unsafe fn ToDataURL(&self,
                 _context: *mut JSContext,
                 _mime_type: Option<DOMString>,
                 _arguments: Vec<HandleValue>) -> Fallible<DOMString> {
        // Step 1.
        if let Some(CanvasContext::Context2d(ref context)) = *self.context.borrow() {
            if !context.origin_is_clean() {
                return Err(Error::Security);
            }
        }

        // Step 2.
        if self.Width() == 0 || self.Height() == 0 {
            return Ok(DOMString::from("data:,"));
        }

        // Step 3.
        let raw_data = match *self.context.borrow() {
            Some(CanvasContext::Context2d(ref context)) => {
                let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64),
                                                           Finite::wrap(self.Width() as f64),
                                                           Finite::wrap(self.Height() as f64)));
                image_data.get_data_array()
            }
            None => {
                // Each pixel is fully-transparent black.
                vec![0; (self.Width() * self.Height() * 4) as usize]
            }
            _ => return Err(Error::NotSupported) // WebGL
        };

        // Only handle image/png for now.
        let mime_type = "image/png";

        let mut encoded = Vec::new();
        {
            let encoder: PNGEncoder<&mut Vec<u8>> = PNGEncoder::new(&mut encoded);
            encoder.encode(&raw_data, self.Width(), self.Height(), ColorType::RGBA(8)).unwrap();
        }

        let encoded = encoded.to_base64(STANDARD);
        Ok(DOMString::from(format!("data:{};base64,{}", mime_type, encoded)))
    }
}

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

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        match attr.local_name() {
            &local_name!("width") | &local_name!("height") => self.recreate_contexts(),
            _ => (),
        };
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH),
            &local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl<'a> From<&'a WebGLContextAttributes> for GLContextAttributes {
    fn from(attrs: &'a WebGLContextAttributes) -> GLContextAttributes {
        GLContextAttributes {
            alpha: attrs.alpha,
            depth: attrs.depth,
            stencil: attrs.stencil,
            antialias: attrs.antialias,
            premultiplied_alpha: attrs.premultipliedAlpha,
            preserve_drawing_buffer: attrs.preserveDrawingBuffer,
        }
    }
}

pub mod utils {
    use dom::window::Window;
    use net_traits::image_cache_thread::{ImageResponse, UsePlaceholder, ImageOrMetadataAvailable};
    use net_traits::image_cache_thread::CanRequestImages;
    use servo_url::ServoUrl;

    pub fn request_image_from_cache(window: &Window, url: ServoUrl) -> ImageResponse {
        let image_cache = window.image_cache_thread();
        //XXXjdm add a image cache mode that doesn't store anything for NotRequested?
        let response =
            image_cache.find_image_or_metadata(url.into(), UsePlaceholder::No);
        match response {
            Ok(ImageOrMetadataAvailable::ImageAvailable(image)) =>
                ImageResponse::Loaded(image),
            _ => ImageResponse::None,
        }
    }
}
<|endoftext|>