<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 flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
use std::fmt;
use std::sync::Arc;
use style::computed_values::float;
use style::dom::TRestyleDamage;
use style::properties::ComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

bitflags! {
    flags SpecialRestyleDamage: u8 {
        #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
                 temporary hack to deal with cases that we don't handle incrementally yet."]
        const REFLOW_ENTIRE_DOCUMENT = 0x01,
    }
}

impl TRestyleDamage for RestyleDamage {
    fn compute(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { compute_damage(old, new) }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
    /// unnecessary sequential resolution of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage, returns the damage that
    /// we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
        }
    })
);

pub fn compute_damage(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
    let old: &ComputedValues = match old {
        None => return RestyleDamage::rebuild_and_reflow(),
        Some(cv) => &**cv,
    };

    let mut damage = RestyleDamage::empty();

    // This checks every CSS property, as enumerated in
    // impl<'self> CssComputedStyle<'self>
    // in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.

    // FIXME: We can short-circuit more of this.

    add_if_not_equal!(old, new, damage,
                      [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_positionoffsets.top, get_positionoffsets.left,
        get_positionoffsets.right, get_positionoffsets.bottom
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], [
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_box.width, get_box.height,
        get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height
    ]);

    add_if_not_equal!(old, new, damage,
                      [
                        REPAINT,
                        STORE_OVERFLOW,
                        BUBBLE_ISIZES,
                        REFLOW_OUT_OF_FLOW,
                        REFLOW,
                        RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_box.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    // FIXME: test somehow that we checked every CSS property
    damage
}

pub trait LayoutDamageComputation {
    fn compute_layout_damage(self) -> SpecialRestyleDamage;
    fn reflow_entire_document(self);
}

impl<'a> LayoutDamageComputation for &'a mut Flow {
    fn compute_layout_damage(self) -> SpecialRestyleDamage {
        let mut special_damage = SpecialRestyleDamage::empty();
        let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED);

        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
        let mut has_counter_affecting_children = false;

        {
            let self_base = flow::mut_base(self);
            for kid in self_base.children.iter_mut() {
                let child_is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                flow::mut_base(kid).restyle_damage
                                   .insert(self_base.restyle_damage.damage_for_child(
                                            is_absolutely_positioned,
                                            child_is_absolutely_positioned));
                {
                    let kid: &mut Flow = kid;
                    special_damage.insert(kid.compute_layout_damage());
                }
                self_base.restyle_damage
                         .insert(flow::base(kid).restyle_damage.damage_for_parent(
                                 child_is_absolutely_positioned));

                has_counter_affecting_children = has_counter_affecting_children ||
                    flow::base(kid).flags.intersects(AFFECTS_COUNTERS |
                                                     HAS_COUNTER_AFFECTING_CHILDREN);

            }
        }

        let self_base = flow::mut_base(self);
        if self_base.flags.float_kind() != float::T::none &&
                self_base.restyle_damage.intersects(REFLOW) {
            special_damage.insert(REFLOW_ENTIRE_DOCUMENT);
        }

        if has_counter_affecting_children {
            self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN)
        } else {
            self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN)
        }

        special_damage
    }

    fn reflow_entire_document(self) {
        let self_base = flow::mut_base(self);
        self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
        self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
        for kid in self_base.children.iter_mut() {
            kid.reflow_entire_document();
        }
    }
}
<commit_msg>layout: Make transform changes require incremental repaint.<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 flow::{self, AFFECTS_COUNTERS, Flow, HAS_COUNTER_AFFECTING_CHILDREN, IS_ABSOLUTELY_POSITIONED};
use std::fmt;
use std::sync::Arc;
use style::computed_values::float;
use style::dom::TRestyleDamage;
use style::properties::ComputedValues;

bitflags! {
    #[doc = "Individual layout actions that may be necessary after restyling."]
    flags RestyleDamage: u8 {
        #[doc = "Repaint the node itself."]
        #[doc = "Currently unused; need to decide how this propagates."]
        const REPAINT = 0x01,

        #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
        #[doc = "Propagates down the flow tree because the computation is bottom-up."]
        const STORE_OVERFLOW = 0x02,

        #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
        #[doc = "Propagates down the flow tree because the computation is"]
        #[doc = "bottom-up."]
        const BUBBLE_ISIZES = 0x04,

        #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
                 into account. \
                 Propagates up the flow tree because the computation is top-down."]
        const REFLOW_OUT_OF_FLOW = 0x08,

        #[doc = "Recompute actual inline_sizes and block_sizes."]
        #[doc = "Propagates up the flow tree because the computation is"]
        #[doc = "top-down."]
        const REFLOW = 0x10,

        #[doc = "Re-resolve generated content. \
                 Propagates up the flow tree because the computation is inorder."]
        const RESOLVE_GENERATED_CONTENT = 0x20,

        #[doc = "The entire flow needs to be reconstructed."]
        const RECONSTRUCT_FLOW = 0x40
    }
}

bitflags! {
    flags SpecialRestyleDamage: u8 {
        #[doc = "If this flag is set, we need to reflow the entire document. This is more or less a \
                 temporary hack to deal with cases that we don't handle incrementally yet."]
        const REFLOW_ENTIRE_DOCUMENT = 0x01,
    }
}

impl TRestyleDamage for RestyleDamage {
    fn compute(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage { compute_damage(old, new) }

    /// Returns a bitmask that represents a flow that needs to be rebuilt and reflowed.
    ///
    /// Use this instead of `RestyleDamage::all()` because `RestyleDamage::all()` will result in
    /// unnecessary sequential resolution of generated content.
    fn rebuild_and_reflow() -> RestyleDamage {
        REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW
    }
}

impl RestyleDamage {
    /// Supposing a flow has the given `position` property and this damage, returns the damage that
    /// we should add to the *parent* of this flow.
    pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
        if child_is_absolutely_positioned {
            self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT)
        } else {
            self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
                    RESOLVE_GENERATED_CONTENT)
        }
    }

    /// Supposing the *parent* of a flow with the given `position` property has this damage,
    /// returns the damage that we should add to this flow.
    pub fn damage_for_child(self,
                            parent_is_absolutely_positioned: bool,
                            child_is_absolutely_positioned: bool)
                            -> RestyleDamage {
        match (parent_is_absolutely_positioned, child_is_absolutely_positioned) {
            (false, true) => {
                // Absolute children are out-of-flow and therefore insulated from changes.
                //
                // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
                self & REPAINT
            }
            (true, false) => {
                // Changing the position of an absolutely-positioned block requires us to reflow
                // its kids.
                if self.contains(REFLOW_OUT_OF_FLOW) {
                    self | REFLOW
                } else {
                    self
                }
            }
            _ => {
                // TODO(pcwalton): Take floatedness into account.
                self & (REPAINT | REFLOW)
            }
        }
    }
}

impl fmt::Display for RestyleDamage {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mut first_elem = true;

        let to_iter =
            [ (REPAINT, "Repaint")
            , (STORE_OVERFLOW, "StoreOverflow")
            , (BUBBLE_ISIZES, "BubbleISizes")
            , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
            , (REFLOW, "Reflow")
            , (RESOLVE_GENERATED_CONTENT, "ResolveGeneratedContent")
            , (RECONSTRUCT_FLOW, "ReconstructFlow")
            ];

        for &(damage, damage_str) in &to_iter {
            if self.contains(damage) {
                if !first_elem { try!(write!(f, " | ")); }
                try!(write!(f, "{}", damage_str));
                first_elem = false;
            }
        }

        if first_elem {
            try!(write!(f, "NoDamage"));
        }

        Ok(())
    }
}

// NB: We need the braces inside the RHS due to Rust #8012.  This particular
// version of this macro might be safe anyway, but we want to avoid silent
// breakage on modifications.
macro_rules! add_if_not_equal(
    ($old:ident, $new:ident, $damage:ident,
     [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
        if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
            $damage.insert($($effect)|*);
        }
    })
);

pub fn compute_damage(old: Option<&Arc<ComputedValues>>, new: &ComputedValues) -> RestyleDamage {
    let old: &ComputedValues = match old {
        None => return RestyleDamage::rebuild_and_reflow(),
        Some(cv) => &**cv,
    };

    let mut damage = RestyleDamage::empty();

    // This checks every CSS property, as enumerated in
    // impl<'self> CssComputedStyle<'self>
    // in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.

    // FIXME: We can short-circuit more of this.

    add_if_not_equal!(old, new, damage,
                      [ REPAINT ], [
        get_color.color, get_background.background_color,
        get_border.border_top_color, get_border.border_right_color,
        get_border.border_bottom_color, get_border.border_left_color,
        get_effects.transform
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [
        get_positionoffsets.top, get_positionoffsets.left,
        get_positionoffsets.right, get_positionoffsets.bottom
    ]);

    add_if_not_equal!(old, new, damage,
                      [ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], [
        get_border.border_top_width, get_border.border_right_width,
        get_border.border_bottom_width, get_border.border_left_width,
        get_margin.margin_top, get_margin.margin_right,
        get_margin.margin_bottom, get_margin.margin_left,
        get_padding.padding_top, get_padding.padding_right,
        get_padding.padding_bottom, get_padding.padding_left,
        get_box.width, get_box.height,
        get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height
    ]);

    add_if_not_equal!(old, new, damage,
                      [
                        REPAINT,
                        STORE_OVERFLOW,
                        BUBBLE_ISIZES,
                        REFLOW_OUT_OF_FLOW,
                        REFLOW,
                        RECONSTRUCT_FLOW
                      ], [
        get_box.float, get_box.display, get_box.position, get_box.content,
        get_counters.counter_reset, get_counters.counter_increment,
        get_list.quotes, get_list.list_style_type,

        // If these text or font properties change, we need to reconstruct the flow so that
        // text shaping is re-run.
        get_inheritedtext.letter_spacing, get_inheritedtext.text_rendering,
        get_inheritedtext.text_transform, get_inheritedtext.word_spacing,
        get_font.font_family, get_font.font_style, get_font.font_variant, get_font.font_weight,
        get_font.font_size, get_font.font_stretch
    ]);

    // If the layer requirements of this flow have changed due to the value
    // of the transform, then reflow is required to rebuild the layers.
    if old.transform_requires_layer() != new.transform_requires_layer() {
        damage.insert(RestyleDamage::rebuild_and_reflow());
    }

    // FIXME: test somehow that we checked every CSS property
    damage
}

pub trait LayoutDamageComputation {
    fn compute_layout_damage(self) -> SpecialRestyleDamage;
    fn reflow_entire_document(self);
}

impl<'a> LayoutDamageComputation for &'a mut Flow {
    fn compute_layout_damage(self) -> SpecialRestyleDamage {
        let mut special_damage = SpecialRestyleDamage::empty();
        let is_absolutely_positioned = flow::base(self).flags.contains(IS_ABSOLUTELY_POSITIONED);

        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
        let mut has_counter_affecting_children = false;

        {
            let self_base = flow::mut_base(self);
            for kid in self_base.children.iter_mut() {
                let child_is_absolutely_positioned =
                    flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
                flow::mut_base(kid).restyle_damage
                                   .insert(self_base.restyle_damage.damage_for_child(
                                            is_absolutely_positioned,
                                            child_is_absolutely_positioned));
                {
                    let kid: &mut Flow = kid;
                    special_damage.insert(kid.compute_layout_damage());
                }
                self_base.restyle_damage
                         .insert(flow::base(kid).restyle_damage.damage_for_parent(
                                 child_is_absolutely_positioned));

                has_counter_affecting_children = has_counter_affecting_children ||
                    flow::base(kid).flags.intersects(AFFECTS_COUNTERS |
                                                     HAS_COUNTER_AFFECTING_CHILDREN);

            }
        }

        let self_base = flow::mut_base(self);
        if self_base.flags.float_kind() != float::T::none &&
                self_base.restyle_damage.intersects(REFLOW) {
            special_damage.insert(REFLOW_ENTIRE_DOCUMENT);
        }

        if has_counter_affecting_children {
            self_base.flags.insert(HAS_COUNTER_AFFECTING_CHILDREN)
        } else {
            self_base.flags.remove(HAS_COUNTER_AFFECTING_CHILDREN)
        }

        special_damage
    }

    fn reflow_entire_document(self) {
        let self_base = flow::mut_base(self);
        self_base.restyle_damage.insert(RestyleDamage::rebuild_and_reflow());
        self_base.restyle_damage.remove(RECONSTRUCT_FLOW);
        for kid in self_base.children.iter_mut() {
            kid.reflow_entire_document();
        }
    }
}
<|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/. */

#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(fnbox)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![plugin(plugins)]

#![deny(unsafe_code)]

#[macro_use]
extern crate bitflags;
extern crate brotli;
extern crate content_blocker as content_blocker_parser;
extern crate cookie as cookie_rs;
extern crate device;
extern crate devtools_traits;
extern crate flate2;
extern crate hyper;
extern crate hyper_serde;
extern crate immeta;
extern crate ipc_channel;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] #[no_link] extern crate matches;
#[macro_use]
extern crate mime;
extern crate mime_guess;
extern crate msg;
extern crate net_traits;
extern crate openssl;
extern crate openssl_verify;
extern crate profile_traits;
extern crate rand;
extern crate rustc_serialize;
extern crate threadpool;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
extern crate tinyfiledialogs;
extern crate unicase;
extern crate url;
extern crate util;
extern crate uuid;
extern crate webrender_traits;
extern crate websocket;

pub mod about_loader;
pub mod blob_loader;
pub mod bluetooth_thread;
pub mod chrome_loader;
pub mod connector;
pub mod content_blocker;
pub mod cookie;
pub mod cookie_storage;
pub mod data_loader;
pub mod file_loader;
pub mod filemanager_thread;
pub mod hsts;
pub mod http_loader;
pub mod image_cache_thread;
pub mod mime_classifier;
pub mod resource_thread;
pub mod storage_thread;
pub mod websocket_loader;

/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/)
pub mod fetch {
    pub mod cors_cache;
    pub mod methods;
}
<commit_msg>Make the data_loader module private.<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/. */

#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(fnbox)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![plugin(plugins)]

#![deny(unsafe_code)]

#[macro_use]
extern crate bitflags;
extern crate brotli;
extern crate content_blocker as content_blocker_parser;
extern crate cookie as cookie_rs;
extern crate device;
extern crate devtools_traits;
extern crate flate2;
extern crate hyper;
extern crate hyper_serde;
extern crate immeta;
extern crate ipc_channel;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;
#[macro_use] #[no_link] extern crate matches;
#[macro_use]
extern crate mime;
extern crate mime_guess;
extern crate msg;
extern crate net_traits;
extern crate openssl;
extern crate openssl_verify;
extern crate profile_traits;
extern crate rand;
extern crate rustc_serialize;
extern crate threadpool;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
extern crate tinyfiledialogs;
extern crate unicase;
extern crate url;
extern crate util;
extern crate uuid;
extern crate webrender_traits;
extern crate websocket;

pub mod about_loader;
pub mod blob_loader;
pub mod bluetooth_thread;
pub mod chrome_loader;
pub mod connector;
pub mod content_blocker;
pub mod cookie;
pub mod cookie_storage;
mod data_loader;
pub mod file_loader;
pub mod filemanager_thread;
pub mod hsts;
pub mod http_loader;
pub mod image_cache_thread;
pub mod mime_classifier;
pub mod resource_thread;
pub mod storage_thread;
pub mod websocket_loader;

/// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/)
pub mod fetch {
    pub mod cors_cache;
    pub mod methods;
}
<|endoftext|>


<commit_before>//! Array type node attribute.

use std::fmt;
use std::marker::PhantomData;
use std::io;
use std::io::Read;
use byteorder::{ReadBytesExt, LittleEndian};
#[cfg(feature = "flate2")]
use flate2::read::ZlibDecoder;
#[cfg(feature = "libflate")]
use libflate::zlib;

use parser::binary::{BinaryParser, CountReader};
use parser::binary::error::{Result, Error, Warning};

/// Read array type attribute from the given parser.
pub fn read_array_attribute<R: Read>(
    parser: &mut BinaryParser<R>,
    type_code: u8
) -> Result<(ArrayAttribute<R>, u64)> {
    let header = try!(ArrayAttributeHeader::read_from_parser(parser));
    let current_pos = parser.source.count();
    let BinaryParser { ref mut source, ref mut warnings, .. } = *parser;
    let reader = try!(ArrayDecoder::new(source, &header));

    let value = match type_code {
        b'b' => ArrayAttribute::Bool(ArrayAttributeReader::new(&header, reader, warnings)),
        b'i' => ArrayAttribute::I32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'l' => ArrayAttribute::I64(ArrayAttributeReader::new(&header, reader, warnings)),
        b'f' => ArrayAttribute::F32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'd' => ArrayAttribute::F64(ArrayAttributeReader::new(&header, reader, warnings)),
        _ => unreachable!(),
    };
    Ok((value, current_pos + header.bytelen_elements as u64))
}

/// Header of an array attribute.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct ArrayAttributeHeader {
    /// Number of elements.
    num_elements: u32,
    /// Array encoding, i.e. compression method.
    encoding: u32,
    /// Length of the array (excluding this header) in bytes.
    bytelen_elements: u32,
}

impl ArrayAttributeHeader {
    fn read_from_parser<R: Read>(parser: &mut BinaryParser<R>) -> io::Result<Self> {
        let num_elements = try!(parser.source.read_u32());
        let encoding = try!(parser.source.read_u32());
        let bytelen_elements = try!(parser.source.read_u32());

        Ok(ArrayAttributeHeader {
            num_elements: num_elements,
            encoding: encoding,
            bytelen_elements: bytelen_elements,
        })
    }
}

/// Array type attribute.
#[derive(Debug)]
pub enum ArrayAttribute<'a, R: 'a> {
    /// Array of `bool`.
    Bool(ArrayAttributeReader<'a, R, bool>),
    /// Array of `i32`.
    I32(ArrayAttributeReader<'a, R, i32>),
    /// Array of `i64`.
    I64(ArrayAttributeReader<'a, R, i64>),
    /// Array of `f32`.
    F32(ArrayAttributeReader<'a, R, f32>),
    /// Array of `f64`.
    F64(ArrayAttributeReader<'a, R, f64>),
}

/// Reader of array attribute elements.
#[derive(Debug)]
pub struct ArrayAttributeReader<'a, R: 'a, T> {
    num_elements: u64,
    rest_elements: u64,
    reader: ArrayDecoder<'a, CountReader<R>>,
    warnings: &'a mut Vec<Warning>,
    _value_type: PhantomData<T>,
}

impl<'a, R: 'a + Read, T> ArrayAttributeReader<'a, R, T> {
    fn new<'b>(
        header: &'b ArrayAttributeHeader,
        reader: ArrayDecoder<'a, CountReader<R>>,
        warnings: &'a mut Vec<Warning>
    ) -> Self {
        ArrayAttributeReader {
            num_elements: header.num_elements as u64,
            rest_elements: header.num_elements as u64,
            reader: reader,
            warnings: warnings,
            _value_type: PhantomData,
        }
    }

    /// Returns number of elements.
    pub fn num_elements(&self) -> u64 {
        self.num_elements
    }

    /// Returns number of rest elements.
    pub fn rest_elements(&self) -> u64 {
        self.num_elements
    }
}

impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, bool> {
    type Item = io::Result<bool>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rest_elements == 0 {
            return None;
        }
        self.rest_elements -= 1;
        let raw = match self.reader.read_u8() {
            Ok(val) => val,
            Err(err) => return Some(Err(err)),
        };
        let val = (raw & 1) == 1;
        Some(Ok(val))
    }
}

macro_rules! impl_attr_array_iter {
    ($ty:ty, $f:ident) => {
        impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, $ty> {
            type Item = io::Result<$ty>;

            fn next(&mut self) -> Option<Self::Item> {
                if self.rest_elements == 0 {
                    return None;
                }
                self.rest_elements -= 1;
                Some(self.reader.$f::<LittleEndian>())
            }
        }
    }
}

impl_attr_array_iter!(i32, read_i32);
impl_attr_array_iter!(i64, read_i64);
impl_attr_array_iter!(f32, read_f32);
impl_attr_array_iter!(f64, read_f64);

/// Attribute array decoder.
enum ArrayDecoder<'a, R: 'a> {
    /// Non-compressed stream.
    ///
    /// `encoding` == 0.
    Raw(io::Take<&'a mut R>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "flate2")]
    Zlib(ZlibDecoder<io::Take<&'a mut R>>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "libflate")]
    Zlib(zlib::Decoder<io::Take<&'a mut R>>),
}

impl<'a, R: 'a + Read> ArrayDecoder<'a, R> {
    fn new(reader: &'a mut R, header: &ArrayAttributeHeader) -> Result<Self> {
        match header.encoding {
            0 => Ok(ArrayDecoder::Raw(reader.take(header.bytelen_elements as u64))),
            #[cfg(feature = "flate2")]
            1 => Ok(ArrayDecoder::Zlib(ZlibDecoder::new(reader.take(header.bytelen_elements as u64)))),
            #[cfg(feature = "libflate")]
            1 => Ok(ArrayDecoder::Zlib(try!(zlib::Decoder::new(reader.take(header.bytelen_elements as u64))))),
            _ => Err(Error::UnknownArrayAttributeEncoding(header.encoding)),
        }
    }
}

impl<'a, R: 'a + Read> Read for ArrayDecoder<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            ArrayDecoder::Raw(ref mut reader) => reader.read(buf),
            #[cfg(feature = "flate2")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
            #[cfg(feature = "libflate")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
        }
    }
}

impl<'a, R: 'a> fmt::Debug for ArrayDecoder<'a, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ArrayDecoder::{}",
               match *self {
                   ArrayDecoder::Raw(_) => "Raw",
                   #[cfg(feature = "flate2")]
                   ArrayDecoder::Zlib(_) => "Zlib",
                   #[cfg(feature = "libflate")]
                   ArrayDecoder::Zlib(_) => "Zlib",
               })
    }
}
<commit_msg>Implement `Iterator::size_hint` for `ArrayAttributeReader`<commit_after>//! Array type node attribute.

use std::fmt;
use std::marker::PhantomData;
use std::io;
use std::io::Read;
use byteorder::{ReadBytesExt, LittleEndian};
#[cfg(feature = "flate2")]
use flate2::read::ZlibDecoder;
#[cfg(feature = "libflate")]
use libflate::zlib;

use parser::binary::{BinaryParser, CountReader};
use parser::binary::error::{Result, Error, Warning};

/// Read array type attribute from the given parser.
pub fn read_array_attribute<R: Read>(
    parser: &mut BinaryParser<R>,
    type_code: u8
) -> Result<(ArrayAttribute<R>, u64)> {
    let header = try!(ArrayAttributeHeader::read_from_parser(parser));
    let current_pos = parser.source.count();
    let BinaryParser { ref mut source, ref mut warnings, .. } = *parser;
    let reader = try!(ArrayDecoder::new(source, &header));

    let value = match type_code {
        b'b' => ArrayAttribute::Bool(ArrayAttributeReader::new(&header, reader, warnings)),
        b'i' => ArrayAttribute::I32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'l' => ArrayAttribute::I64(ArrayAttributeReader::new(&header, reader, warnings)),
        b'f' => ArrayAttribute::F32(ArrayAttributeReader::new(&header, reader, warnings)),
        b'd' => ArrayAttribute::F64(ArrayAttributeReader::new(&header, reader, warnings)),
        _ => unreachable!(),
    };
    Ok((value, current_pos + header.bytelen_elements as u64))
}

/// Header of an array attribute.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct ArrayAttributeHeader {
    /// Number of elements.
    num_elements: u32,
    /// Array encoding, i.e. compression method.
    encoding: u32,
    /// Length of the array (excluding this header) in bytes.
    bytelen_elements: u32,
}

impl ArrayAttributeHeader {
    fn read_from_parser<R: Read>(parser: &mut BinaryParser<R>) -> io::Result<Self> {
        let num_elements = try!(parser.source.read_u32());
        let encoding = try!(parser.source.read_u32());
        let bytelen_elements = try!(parser.source.read_u32());

        Ok(ArrayAttributeHeader {
            num_elements: num_elements,
            encoding: encoding,
            bytelen_elements: bytelen_elements,
        })
    }
}

/// Array type attribute.
#[derive(Debug)]
pub enum ArrayAttribute<'a, R: 'a> {
    /// Array of `bool`.
    Bool(ArrayAttributeReader<'a, R, bool>),
    /// Array of `i32`.
    I32(ArrayAttributeReader<'a, R, i32>),
    /// Array of `i64`.
    I64(ArrayAttributeReader<'a, R, i64>),
    /// Array of `f32`.
    F32(ArrayAttributeReader<'a, R, f32>),
    /// Array of `f64`.
    F64(ArrayAttributeReader<'a, R, f64>),
}

/// Reader of array attribute elements.
#[derive(Debug)]
pub struct ArrayAttributeReader<'a, R: 'a, T> {
    num_elements: u64,
    rest_elements: u64,
    reader: ArrayDecoder<'a, CountReader<R>>,
    warnings: &'a mut Vec<Warning>,
    _value_type: PhantomData<T>,
}

impl<'a, R: 'a + Read, T> ArrayAttributeReader<'a, R, T> {
    fn new<'b>(
        header: &'b ArrayAttributeHeader,
        reader: ArrayDecoder<'a, CountReader<R>>,
        warnings: &'a mut Vec<Warning>
    ) -> Self {
        ArrayAttributeReader {
            num_elements: header.num_elements as u64,
            rest_elements: header.num_elements as u64,
            reader: reader,
            warnings: warnings,
            _value_type: PhantomData,
        }
    }

    /// Returns number of elements.
    pub fn num_elements(&self) -> u64 {
        self.num_elements
    }

    /// Returns number of rest elements.
    pub fn rest_elements(&self) -> u64 {
        self.num_elements
    }
}

impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, bool> {
    type Item = io::Result<bool>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rest_elements == 0 {
            return None;
        }
        self.rest_elements -= 1;
        let raw = match self.reader.read_u8() {
            Ok(val) => val,
            Err(err) => return Some(Err(err)),
        };
        let val = (raw & 1) == 1;
        Some(Ok(val))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.rest_elements as usize, Some(self.rest_elements as usize))
    }
}

macro_rules! impl_attr_array_iter {
    ($ty:ty, $f:ident) => {
        impl<'a, R: 'a + Read> Iterator for ArrayAttributeReader<'a, R, $ty> {
            type Item = io::Result<$ty>;

            fn next(&mut self) -> Option<Self::Item> {
                if self.rest_elements == 0 {
                    return None;
                }
                self.rest_elements -= 1;
                Some(self.reader.$f::<LittleEndian>())
            }

            fn size_hint(&self) -> (usize, Option<usize>) {
                (self.rest_elements as usize, Some(self.rest_elements as usize))
            }
        }
    }
}

impl_attr_array_iter!(i32, read_i32);
impl_attr_array_iter!(i64, read_i64);
impl_attr_array_iter!(f32, read_f32);
impl_attr_array_iter!(f64, read_f64);

/// Attribute array decoder.
enum ArrayDecoder<'a, R: 'a> {
    /// Non-compressed stream.
    ///
    /// `encoding` == 0.
    Raw(io::Take<&'a mut R>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "flate2")]
    Zlib(ZlibDecoder<io::Take<&'a mut R>>),
    /// Zlib-compressed stream.
    ///
    /// `encoding` == 1.
    #[cfg(feature = "libflate")]
    Zlib(zlib::Decoder<io::Take<&'a mut R>>),
}

impl<'a, R: 'a + Read> ArrayDecoder<'a, R> {
    fn new(reader: &'a mut R, header: &ArrayAttributeHeader) -> Result<Self> {
        match header.encoding {
            0 => Ok(ArrayDecoder::Raw(reader.take(header.bytelen_elements as u64))),
            #[cfg(feature = "flate2")]
            1 => Ok(ArrayDecoder::Zlib(ZlibDecoder::new(reader.take(header.bytelen_elements as u64)))),
            #[cfg(feature = "libflate")]
            1 => Ok(ArrayDecoder::Zlib(try!(zlib::Decoder::new(reader.take(header.bytelen_elements as u64))))),
            _ => Err(Error::UnknownArrayAttributeEncoding(header.encoding)),
        }
    }
}

impl<'a, R: 'a + Read> Read for ArrayDecoder<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            ArrayDecoder::Raw(ref mut reader) => reader.read(buf),
            #[cfg(feature = "flate2")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
            #[cfg(feature = "libflate")]
            ArrayDecoder::Zlib(ref mut reader) => reader.read(buf),
        }
    }
}

impl<'a, R: 'a> fmt::Debug for ArrayDecoder<'a, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "ArrayDecoder::{}",
               match *self {
                   ArrayDecoder::Raw(_) => "Raw",
                   #[cfg(feature = "flate2")]
                   ArrayDecoder::Zlib(_) => "Zlib",
                   #[cfg(feature = "libflate")]
                   ArrayDecoder::Zlib(_) => "Zlib",
               })
    }
}
<|endoftext|>


<commit_before>//! Removes assignments to ZST places.

use crate::transform::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::mir::{Body, StatementKind};
use rustc_middle::ty::TyCtxt;

pub struct RemoveZsts;

impl<'tcx> MirPass<'tcx> for RemoveZsts {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(body.source.def_id());

        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();

        let mut is_zst_cache = FxHashMap::default();

        for block in basic_blocks.iter_mut() {
            for statement in block.statements.iter_mut() {
                match statement.kind {
                    StatementKind::Assign(box (place, _)) => {
                        let place_ty = place.ty(local_decls, tcx).ty;

                        let is_inhabited_zst = *is_zst_cache.entry(place_ty).or_insert_with(|| {
                            if let Ok(layout) = tcx.layout_of(param_env.and(place_ty)) {
                                if layout.is_zst() && !layout.abi.is_uninhabited() {
                                    return true;
                                }
                            }
                            false
                        });

                        if is_inhabited_zst {
                            if tcx.consider_optimizing(|| {
                                format!(
                                    "RemoveZsts - Place: {:?} SourceInfo: {:?}",
                                    place, statement.source_info
                                )
                            }) {
                                statement.make_nop();
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}
<commit_msg>remove unnecessary and ineffective caching<commit_after>//! Removes assignments to ZST places.

use crate::transform::MirPass;
use rustc_middle::mir::{Body, StatementKind};
use rustc_middle::ty::TyCtxt;

pub struct RemoveZsts;

impl<'tcx> MirPass<'tcx> for RemoveZsts {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(body.source.def_id());
        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
        for block in basic_blocks.iter_mut() {
            for statement in block.statements.iter_mut() {
                match statement.kind {
                    StatementKind::Assign(box (place, _)) => {
                        let place_ty = place.ty(local_decls, tcx).ty;
                        if let Ok(layout) = tcx.layout_of(param_env.and(place_ty)) {
                            if layout.is_zst() && !layout.abi.is_uninhabited() {
                                if tcx.consider_optimizing(|| {
                                    format!(
                                        "RemoveZsts - Place: {:?} SourceInfo: {:?}",
                                        place, statement.source_info
                                    )
                                }) {
                                    statement.make_nop();
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

//! A module contains test cases for lease read on Raft leader.

use std::sync::atomic::*;
use std::sync::Arc;
use std::thread;
use std::time::*;

use kvproto::raft_serverpb::RaftLocalState;
use raft::eraftpb::{ConfChangeType, MessageType};

use test_raftstore::*;
use tikv::raftstore::store::engine::Peekable;
use tikv::raftstore::store::keys;
use tikv::util::config::*;
use tikv::util::HandyRwLock;

// A helper function for testing the lease reads and lease renewing.
// The leader keeps a record of its leader lease, and uses the system's
// monotonic raw clocktime to check whether its lease has expired.
// If the leader lease has not expired, when the leader receives a read request
//   1. with `read_quorum == false`, the leader will serve it by reading local data.
//      This way of handling request is called "lease read".
//   2. with `read_quorum == true`, the leader will serve it by doing index read (see raft's doc).
//      This way of handling request is called "index read".
// If the leader lease has expired, leader will serve both kinds of requests by index read, and
// propose an no-op entry to raft quorum to renew the lease.
// No matter what status the leader lease is, a write request is always served by writing a Raft
// log to the Raft quorum. It is called "consistent write". All writes are consistent writes.
// Every time the leader performs a consistent read/write, it will try to renew its lease.
fn test_renew_lease<T: Simulator>(cluster: &mut Cluster<T>) {
    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    // Use large election timeout to make leadership stable.
    configure_for_lease_read(cluster, Some(50), Some(10_000));
    // Override max leader lease to 2 seconds.
    let max_lease = Duration::from_secs(2);
    cluster.cfg.raft_store.raft_store_max_leader_lease = ReadableDuration(max_lease);

    let node_id = 1u64;
    let store_id = 1u64;
    let peer = new_peer(store_id, node_id);
    cluster.pd_client.disable_default_operator();
    let region_id = cluster.run_conf_change();
    for id in 2..cluster.engines.len() as u64 + 1 {
        cluster.pd_client.must_add_peer(region_id, new_peer(id, id));
    }

    // Write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer` to become leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());
    let engine = cluster.get_raft_engine(store_id);
    let state_key = keys::raft_state_key(region_id);
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    let last_index = state.get_last_index();

    let detector = LeaseReadFilter::default();
    cluster.add_send_filter(CloneFilterFactory(detector.clone()));

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 0);

    let mut expect_lease_read = 0;

    if cluster.engines.len() > 1 {
        // Wait for the leader lease to expire.
        thread::sleep(max_lease);

        // Issue a read request and check the value on response.
        must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");

        // Check if the leader does a index read and renewed its lease.
        assert_eq!(cluster.leader_of_region(region_id), Some(peer.clone()));
        expect_lease_read += 1;
        assert_eq!(detector.ctx.rl().len(), expect_lease_read);
    }

    // Wait for the leader lease to expire.
    thread::sleep(max_lease);

    // Issue a write request.
    cluster.must_put(key, b"v2");

    // Check if the leader has renewed its lease so that it can do lease read.
    assert_eq!(cluster.leader_of_region(region_id), Some(peer.clone()));
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v2");

    // Check if the leader does a local read.
    assert_eq!(detector.ctx.rl().len(), expect_lease_read);
}

#[test]
fn test_one_node_renew_lease() {
    let count = 1;
    let mut cluster = new_node_cluster(0, count);
    test_renew_lease(&mut cluster);
}

#[test]
fn test_node_renew_lease() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_renew_lease(&mut cluster);
}

// A helper function for testing the lease reads when the lease has expired.
// If the leader lease has expired, there may be new leader elected and
// the old leader will fail to renew its lease.
fn test_lease_expired<T: Simulator>(cluster: &mut Cluster<T>) {
    let pd_client = Arc::clone(&cluster.pd_client);
    // Disable default max peer number check.
    pd_client.disable_default_operator();

    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    let election_timeout = configure_for_lease_read(cluster, Some(50), None);

    let node_id = 3u64;
    let store_id = 3u64;
    let peer = new_peer(store_id, node_id);
    cluster.run();

    // Write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer` to become leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());

    // Isolate the leader `peer` from other peers.
    cluster.add_send_filter(IsolationFilterFactory::new(store_id));

    // Wait for the leader lease to expire and a new leader is elected.
    thread::sleep(election_timeout * 2);

    // Issue a read request and check the value on response.
    must_error_read_on_peer(
        cluster,
        peer.clone(),
        region.clone(),
        key,
        Duration::from_secs(1),
    );
}

#[test]
fn test_node_lease_expired() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_lease_expired(&mut cluster);
}

// A helper function for testing the leader holds unsafe lease during the leader transfer
// procedure, so it will not do lease read.
// Since raft will not propose any request during leader transfer procedure, consistent read/write
// could not be performed neither.
// When leader transfer procedure aborts later, the leader would use and update the lease as usual.
fn test_lease_unsafe_during_leader_transfers<T: Simulator>(cluster: &mut Cluster<T>) {
    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    let election_timeout = configure_for_lease_read(cluster, Some(50), None);

    let store_id = 1u64;
    let peer = new_peer(store_id, 1);
    let peer3_store_id = 3u64;
    let peer3 = new_peer(peer3_store_id, 3);
    cluster.run();

    let detector = LeaseReadFilter::default();
    cluster.add_send_filter(CloneFilterFactory(detector.clone()));

    // write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer1` to became leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");

    let engine = cluster.get_raft_engine(store_id);
    let state_key = keys::raft_state_key(region_id);
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    let last_index = state.get_last_index();

    // Check if the leader does a local read.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index);
    assert_eq!(detector.ctx.rl().len(), 0);

    // Drop MsgTimeoutNow to `peer3` so that the leader transfer procedure would abort later.
    cluster.add_send_filter(CloneFilterFactory(
        RegionPacketFilter::new(region_id, peer3_store_id)
            .msg_type(MessageType::MsgTimeoutNow)
            .direction(Direction::Recv),
    ));

    // Issue a transfer leader request to transfer leader from `peer` to `peer3`.
    cluster.transfer_leader(region_id, peer3);

    // Delay a while to ensure transfer leader procedure is triggered inside raft module.
    thread::sleep(election_timeout / 2);

    // Issue a read request and it will fall back to read index.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 1);

    // And read index should not update lease.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 2);

    // Make sure the leader transfer procedure timeouts.
    thread::sleep(election_timeout * 2);

    // Then the leader transfer procedure aborts, now the leader could do lease read or consistent
    // read/write and renew/reuse the lease as usual.

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 3);

    // Check if the leader also propose an entry to renew its lease.
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);

    // wait some time for the proposal to be applied.
    thread::sleep(election_timeout / 2);

    // Check if the leader does a local read.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);
    assert_eq!(detector.ctx.rl().len(), 3);
}

#[test]
fn test_node_lease_unsafe_during_leader_transfers() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_lease_unsafe_during_leader_transfers(&mut cluster);
}

/// test whether the read index callback will be handled when a region is destroyed.
/// If it's not handled properly, it will cause dead lock in transaction scheduler.
#[test]
fn test_node_callback_when_destroyed() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    cluster.run();
    cluster.must_put(b"k1", b"v1");
    let leader = cluster.leader_of_region(1).unwrap();
    let cc = new_change_peer_request(ConfChangeType::RemoveNode, leader.clone());
    let epoch = cluster.get_region_epoch(1);
    let req = new_admin_request(1, &epoch, cc);
    // so the leader can't commit the conf change yet.
    let block = Arc::new(AtomicBool::new(true));
    cluster.add_send_filter(CloneFilterFactory(
        RegionPacketFilter::new(1, leader.get_store_id())
            .msg_type(MessageType::MsgAppendResponse)
            .direction(Direction::Recv)
            .when(Arc::clone(&block)),
    ));
    let mut filter = LeaseReadFilter::default();
    filter.take = true;
    // so the leader can't perform read index.
    cluster.add_send_filter(CloneFilterFactory(filter.clone()));
    // it always timeout, no need to wait.
    let _ = cluster.call_command_on_leader(req, Duration::from_millis(500));

    let get = new_get_cmd(b"k1");
    let req = new_request(1, epoch, vec![get], true);
    block.store(false, Ordering::SeqCst);
    let resp = cluster
        .call_command_on_leader(req, Duration::from_secs(3))
        .unwrap();
    assert!(
        !filter.ctx.rl().is_empty(),
        "read index should be performed"
    );
    assert!(
        resp.get_header().get_error().has_region_not_found(),
        "{:?}",
        resp
    );
}
<commit_msg>tests: stable test_node_callback_when_destroyed (#3852)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

//! A module contains test cases for lease read on Raft leader.

use std::sync::atomic::*;
use std::sync::Arc;
use std::thread;
use std::time::*;

use kvproto::raft_serverpb::RaftLocalState;
use raft::eraftpb::{ConfChangeType, MessageType};

use test_raftstore::*;
use tikv::raftstore::store::engine::Peekable;
use tikv::raftstore::store::keys;
use tikv::util::config::*;
use tikv::util::HandyRwLock;

// A helper function for testing the lease reads and lease renewing.
// The leader keeps a record of its leader lease, and uses the system's
// monotonic raw clocktime to check whether its lease has expired.
// If the leader lease has not expired, when the leader receives a read request
//   1. with `read_quorum == false`, the leader will serve it by reading local data.
//      This way of handling request is called "lease read".
//   2. with `read_quorum == true`, the leader will serve it by doing index read (see raft's doc).
//      This way of handling request is called "index read".
// If the leader lease has expired, leader will serve both kinds of requests by index read, and
// propose an no-op entry to raft quorum to renew the lease.
// No matter what status the leader lease is, a write request is always served by writing a Raft
// log to the Raft quorum. It is called "consistent write". All writes are consistent writes.
// Every time the leader performs a consistent read/write, it will try to renew its lease.
fn test_renew_lease<T: Simulator>(cluster: &mut Cluster<T>) {
    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    // Use large election timeout to make leadership stable.
    configure_for_lease_read(cluster, Some(50), Some(10_000));
    // Override max leader lease to 2 seconds.
    let max_lease = Duration::from_secs(2);
    cluster.cfg.raft_store.raft_store_max_leader_lease = ReadableDuration(max_lease);

    let node_id = 1u64;
    let store_id = 1u64;
    let peer = new_peer(store_id, node_id);
    cluster.pd_client.disable_default_operator();
    let region_id = cluster.run_conf_change();
    for id in 2..cluster.engines.len() as u64 + 1 {
        cluster.pd_client.must_add_peer(region_id, new_peer(id, id));
    }

    // Write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer` to become leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());
    let engine = cluster.get_raft_engine(store_id);
    let state_key = keys::raft_state_key(region_id);
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    let last_index = state.get_last_index();

    let detector = LeaseReadFilter::default();
    cluster.add_send_filter(CloneFilterFactory(detector.clone()));

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 0);

    let mut expect_lease_read = 0;

    if cluster.engines.len() > 1 {
        // Wait for the leader lease to expire.
        thread::sleep(max_lease);

        // Issue a read request and check the value on response.
        must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");

        // Check if the leader does a index read and renewed its lease.
        assert_eq!(cluster.leader_of_region(region_id), Some(peer.clone()));
        expect_lease_read += 1;
        assert_eq!(detector.ctx.rl().len(), expect_lease_read);
    }

    // Wait for the leader lease to expire.
    thread::sleep(max_lease);

    // Issue a write request.
    cluster.must_put(key, b"v2");

    // Check if the leader has renewed its lease so that it can do lease read.
    assert_eq!(cluster.leader_of_region(region_id), Some(peer.clone()));
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v2");

    // Check if the leader does a local read.
    assert_eq!(detector.ctx.rl().len(), expect_lease_read);
}

#[test]
fn test_one_node_renew_lease() {
    let count = 1;
    let mut cluster = new_node_cluster(0, count);
    test_renew_lease(&mut cluster);
}

#[test]
fn test_node_renew_lease() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_renew_lease(&mut cluster);
}

// A helper function for testing the lease reads when the lease has expired.
// If the leader lease has expired, there may be new leader elected and
// the old leader will fail to renew its lease.
fn test_lease_expired<T: Simulator>(cluster: &mut Cluster<T>) {
    let pd_client = Arc::clone(&cluster.pd_client);
    // Disable default max peer number check.
    pd_client.disable_default_operator();

    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    let election_timeout = configure_for_lease_read(cluster, Some(50), None);

    let node_id = 3u64;
    let store_id = 3u64;
    let peer = new_peer(store_id, node_id);
    cluster.run();

    // Write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer` to become leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());

    // Isolate the leader `peer` from other peers.
    cluster.add_send_filter(IsolationFilterFactory::new(store_id));

    // Wait for the leader lease to expire and a new leader is elected.
    thread::sleep(election_timeout * 2);

    // Issue a read request and check the value on response.
    must_error_read_on_peer(
        cluster,
        peer.clone(),
        region.clone(),
        key,
        Duration::from_secs(1),
    );
}

#[test]
fn test_node_lease_expired() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_lease_expired(&mut cluster);
}

// A helper function for testing the leader holds unsafe lease during the leader transfer
// procedure, so it will not do lease read.
// Since raft will not propose any request during leader transfer procedure, consistent read/write
// could not be performed neither.
// When leader transfer procedure aborts later, the leader would use and update the lease as usual.
fn test_lease_unsafe_during_leader_transfers<T: Simulator>(cluster: &mut Cluster<T>) {
    // Avoid triggering the log compaction in this test case.
    cluster.cfg.raft_store.raft_log_gc_threshold = 100;
    // Increase the Raft tick interval to make this test case running reliably.
    let election_timeout = configure_for_lease_read(cluster, Some(50), None);

    let store_id = 1u64;
    let peer = new_peer(store_id, 1);
    let peer3_store_id = 3u64;
    let peer3 = new_peer(peer3_store_id, 3);
    cluster.run();

    let detector = LeaseReadFilter::default();
    cluster.add_send_filter(CloneFilterFactory(detector.clone()));

    // write the initial value for a key.
    let key = b"k";
    cluster.must_put(key, b"v1");
    // Force `peer1` to became leader.
    let region = cluster.get_region(key);
    let region_id = region.get_id();
    cluster.must_transfer_leader(region_id, peer.clone());

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");

    let engine = cluster.get_raft_engine(store_id);
    let state_key = keys::raft_state_key(region_id);
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    let last_index = state.get_last_index();

    // Check if the leader does a local read.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index);
    assert_eq!(detector.ctx.rl().len(), 0);

    // Drop MsgTimeoutNow to `peer3` so that the leader transfer procedure would abort later.
    cluster.add_send_filter(CloneFilterFactory(
        RegionPacketFilter::new(region_id, peer3_store_id)
            .msg_type(MessageType::MsgTimeoutNow)
            .direction(Direction::Recv),
    ));

    // Issue a transfer leader request to transfer leader from `peer` to `peer3`.
    cluster.transfer_leader(region_id, peer3);

    // Delay a while to ensure transfer leader procedure is triggered inside raft module.
    thread::sleep(election_timeout / 2);

    // Issue a read request and it will fall back to read index.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 1);

    // And read index should not update lease.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 2);

    // Make sure the leader transfer procedure timeouts.
    thread::sleep(election_timeout * 2);

    // Then the leader transfer procedure aborts, now the leader could do lease read or consistent
    // read/write and renew/reuse the lease as usual.

    // Issue a read request and check the value on response.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    assert_eq!(detector.ctx.rl().len(), 3);

    // Check if the leader also propose an entry to renew its lease.
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);

    // wait some time for the proposal to be applied.
    thread::sleep(election_timeout / 2);

    // Check if the leader does a local read.
    must_read_on_peer(cluster, peer.clone(), region.clone(), key, b"v1");
    let state: RaftLocalState = engine.get_msg(&state_key).unwrap().unwrap();
    assert_eq!(state.get_last_index(), last_index + 1);
    assert_eq!(detector.ctx.rl().len(), 3);
}

#[test]
fn test_node_lease_unsafe_during_leader_transfers() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    test_lease_unsafe_during_leader_transfers(&mut cluster);
}

/// test whether the read index callback will be handled when a region is destroyed.
/// If it's not handled properly, it will cause dead lock in transaction scheduler.
#[test]
fn test_node_callback_when_destroyed() {
    let count = 3;
    let mut cluster = new_node_cluster(0, count);
    // Increase the election tick to make this test case running reliably.
    configure_for_lease_read(&mut cluster, None, Some(50));
    cluster.run();
    cluster.must_put(b"k1", b"v1");
    let leader = cluster.leader_of_region(1).unwrap();
    let cc = new_change_peer_request(ConfChangeType::RemoveNode, leader.clone());
    let epoch = cluster.get_region_epoch(1);
    let req = new_admin_request(1, &epoch, cc);
    // so the leader can't commit the conf change yet.
    let block = Arc::new(AtomicBool::new(true));
    cluster.add_send_filter(CloneFilterFactory(
        RegionPacketFilter::new(1, leader.get_store_id())
            .msg_type(MessageType::MsgAppendResponse)
            .direction(Direction::Recv)
            .when(Arc::clone(&block)),
    ));
    let mut filter = LeaseReadFilter::default();
    filter.take = true;
    // so the leader can't perform read index.
    cluster.add_send_filter(CloneFilterFactory(filter.clone()));
    // it always timeout, no need to wait.
    let _ = cluster.call_command_on_leader(req, Duration::from_millis(500));

    // To make sure `get` is handled before destroy leader, we must issue
    // `get` then unblock append responses.
    let leader_node_id = leader.get_store_id();
    let get = new_get_cmd(b"k1");
    let mut req = new_request(1, epoch, vec![get], true);
    req.mut_header().set_peer(leader);
    let (cb, rx) = make_cb(&req);
    cluster
        .sim
        .rl()
        .async_command_on_node(leader_node_id, req, cb)
        .unwrap();
    // Unblock append responses after we issue the req.
    block.store(false, Ordering::SeqCst);
    let resp = rx.recv_timeout(Duration::from_secs(3)).unwrap();

    assert!(
        !filter.ctx.rl().is_empty(),
        "read index should be performed"
    );
    assert!(
        resp.get_header().get_error().has_region_not_found(),
        "{:?}",
        resp
    );
}
<|endoftext|>


<commit_before>
use super::common::*;

use std::iter;

use idents;
use utils;
use methodinfo::Direction;
use model;

extern crate proc_macro;
use self::proc_macro::TokenStream;
use syn::*;

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: &TokenStream,
    item_tokens: TokenStream,
) -> Result<TokenStream, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(), &attr_tokens.to_string(), &item_tokens.to_string() )?;
    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf.name() );
    let vtable_ident = idents::vtable_struct( itf.name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // IidOf implementation.
    let iidof_doc = format!( "Returns `{}`.", iid_ident );
    output.push( quote!(
        impl ::intercom::IidOf for #itf_ident {
            #[doc = #iidof_doc]
            fn iid() -> &'static ::intercom::IID {
                & #iid_ident
            }
        }
    ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut fields = vec![];
    if let Some( base ) = *itf.base_interface() {
        let vtbl = match base.as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( base ); quote!( #vtbl ) }
        };
        fields.push( quote!( pub __base : #vtbl ) );
    }

    // Process the trait items. Each COM-callable method on the trait will
    // result in a field in the virtual table.
    //
    // We will also create the delegating call from Rust to COM for these
    // methods.
    //
    // NOTE: Currently we are skipping methods that aren't "COM compatible".
    //       However as we need to be able to delegate the calls from Rust
    //       to COM and this requires implementing the trait for a random
    //       COM pointer, we might need to fail the traits that have COM
    //       incompatible functions instead.
    let calling_convention = get_calling_convetion();
    let mut impls = vec![];
    for method_info in itf.methods() {

        let method_ident = &method_info.name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty();
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );

        // COM delegate implementation.

        // Format the method arguments into tokens.
        let impl_args = method_info.args.iter().map( |ca| {
            let name = &ca.name;
            let ty = &ca.ty;
            quote!( #name : #ty )
        } );

        // The COM out-arguments that mirror the Rust return value will
        // require temporary variables during the COM call. Format their
        // declarations.
        let out_arg_declarations = method_info.returnhandler.com_out_args()
                .iter()
                .map( |ca| {
                    let ident = &ca.name;
                    let ty = &ca.handler.com_ty();
                    let default = ca.handler.default_value();
                    quote!( let mut #ident : #ty = #default; )
                } ).collect::<Vec<_>>();

        // Format the in and out parameters for the COM call.
        let params = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = com_arg.name;
                    match com_arg.dir {
                        Direction::In => {
                            let param = com_arg.handler.rust_to_com( name );
                            quote!( #param )
                        },
                        Direction::Out | Direction::Retval
                            => quote!( &mut #name ),
                    }
                } );

        // Combine the parameters into the final parameter list.
        // This includes the 'this' pointer and both the IN and OUT
        // parameters.
        let params = iter::once( quote!( comptr ) ).chain( params );

        // Create the return statement.
        let return_ident = Ident::from( "__result" );
        let return_statement = method_info
                .returnhandler
                .com_to_rust_return( return_ident );

        // Create the method implementation using the bits defined above.
        let self_arg = &method_info.rust_self_arg;
        let return_ty = &method_info.rust_return_ty;
        let unsafety = if method_info.is_unsafe { quote!( unsafe ) } else { quote!() };
        impls.push( quote!(
            #unsafety fn #method_ident(
                #self_arg, #( #impl_args ),*
            ) -> #return_ty
            {
                let comptr = ::intercom::ComItf::ptr( self );
                let vtbl = comptr as *const *const #vtable_ident;

                #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
                unsafe {
                    #( #out_arg_declarations )*;
                    let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );
                    #return_statement
                }
            }
        ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #fields, )* }
    ) );

    // If this is a trait (as opposed to an implicit struct `impl`), include
    // the Rust-to-COM call implementations.
    //
    // If the [com_interface] is on an implicit struct `impl` we'd end up with
    // `impl StructName for intercom::ComItf<StructName>`, which is invalid
    // syntax when `StructName` is struct instead of a trait.
    if itf.item_type() == utils::InterfaceType::Trait {
        let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
        output.push( quote!(
            #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                #( #impls )*
            }
        ) );
    }

    // If this is a trait based interface, implement Deref for ComItf.
    //
    // Trait based interfaces can always be Deref'd from ComItf into &Trait.
    // Struct based interfaces can only be Deref'd if the struct has the
    // interface in its vtable list. This is decided in the com_class
    // attribute.
    if itf.item_type() == utils::InterfaceType::Trait {
        output.push( quote!(
            impl ::std::ops::Deref for ::intercom::ComItf< #itf_ident > {
                type Target = #itf_ident;
                fn deref( &self ) -> &Self::Target {
                    self
                }
            }
        ) );
    }

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

<commit_msg>Add error handling to COM delegates<commit_after>
use super::common::*;

use std::iter;

use idents;
use utils;
use methodinfo::Direction;
use model;

extern crate proc_macro;
use self::proc_macro::TokenStream;
use syn::*;

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: &TokenStream,
    item_tokens: TokenStream,
) -> Result<TokenStream, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(), &attr_tokens.to_string(), &item_tokens.to_string() )?;
    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf.name() );
    let vtable_ident = idents::vtable_struct( itf.name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // IidOf implementation.
    let iidof_doc = format!( "Returns `{}`.", iid_ident );
    output.push( quote!(
        impl ::intercom::IidOf for #itf_ident {
            #[doc = #iidof_doc]
            fn iid() -> &'static ::intercom::IID {
                & #iid_ident
            }
        }
    ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut fields = vec![];
    if let Some( base ) = *itf.base_interface() {
        let vtbl = match base.as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( base ); quote!( #vtbl ) }
        };
        fields.push( quote!( pub __base : #vtbl ) );
    }

    // Process the trait items. Each COM-callable method on the trait will
    // result in a field in the virtual table.
    //
    // We will also create the delegating call from Rust to COM for these
    // methods.
    //
    // NOTE: Currently we are skipping methods that aren't "COM compatible".
    //       However as we need to be able to delegate the calls from Rust
    //       to COM and this requires implementing the trait for a random
    //       COM pointer, we might need to fail the traits that have COM
    //       incompatible functions instead.
    let calling_convention = get_calling_convetion();
    let mut impls = vec![];
    for method_info in itf.methods() {

        let method_ident = &method_info.name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty();
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );

        // COM delegate implementation.

        // Format the method arguments into tokens.
        let impl_args = method_info.args.iter().map( |ca| {
            let name = &ca.name;
            let ty = &ca.ty;
            quote!( #name : #ty )
        } );

        // The COM out-arguments that mirror the Rust return value will
        // require temporary variables during the COM call. Format their
        // declarations.
        let out_arg_declarations = method_info.returnhandler.com_out_args()
                .iter()
                .map( |ca| {
                    let ident = &ca.name;
                    let ty = &ca.handler.com_ty();
                    let default = ca.handler.default_value();
                    quote!( let mut #ident : #ty = #default; )
                } ).collect::<Vec<_>>();

        // Format the in and out parameters for the COM call.
        let params = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = com_arg.name;
                    match com_arg.dir {
                        Direction::In => {
                            let param = com_arg.handler.rust_to_com( name );
                            quote!( #param )
                        },
                        Direction::Out | Direction::Retval
                            => quote!( &mut #name ),
                    }
                } );

        // Combine the parameters into the final parameter list.
        // This includes the 'this' pointer and both the IN and OUT
        // parameters.
        let params = iter::once( quote!( comptr ) ).chain( params );

        // Create the return statement.
        let return_ident = Ident::from( "__result" );
        let return_statement = method_info
                .returnhandler
                .com_to_rust_return( return_ident );

        // Create the method implementation using the bits defined above.
        let self_arg = &method_info.rust_self_arg;
        let return_ty = &method_info.rust_return_ty;
        let unsafety = if method_info.is_unsafe { quote!( unsafe ) } else { quote!() };
        impls.push( quote!(
            #unsafety fn #method_ident(
                #self_arg, #( #impl_args ),*
            ) -> #return_ty
            {
                use ::intercom::ComInto;

                let comptr = ::intercom::ComItf::ptr( self );
                let vtbl = comptr as *const *const #vtable_ident;

                #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
                let result : Result< #return_ty, ComError > = ( || unsafe {
                    #( #out_arg_declarations )*;
                    let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );

                    Ok( { #return_statement } )
                } )();

                use ::intercom::ReturnError;
                match result {
                    Ok( v ) => v,
                    Err( err ) => < #return_ty as ReturnError >::handle(
                            ::intercom::return_hresult( err ) ),
                }
            }
        ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #fields, )* }
    ) );

    // If this is a trait (as opposed to an implicit struct `impl`), include
    // the Rust-to-COM call implementations.
    //
    // If the [com_interface] is on an implicit struct `impl` we'd end up with
    // `impl StructName for intercom::ComItf<StructName>`, which is invalid
    // syntax when `StructName` is struct instead of a trait.
    if itf.item_type() == utils::InterfaceType::Trait {
        let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
        output.push( quote!(
            #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                #( #impls )*
            }
        ) );
    }

    // If this is a trait based interface, implement Deref for ComItf.
    //
    // Trait based interfaces can always be Deref'd from ComItf into &Trait.
    // Struct based interfaces can only be Deref'd if the struct has the
    // interface in its vtable list. This is decided in the com_class
    // attribute.
    if itf.item_type() == utils::InterfaceType::Trait {
        output.push( quote!(
            impl ::std::ops::Deref for ::intercom::ComItf< #itf_ident > {
                type Target = #itf_ident;
                fn deref( &self ) -> &Self::Target {
                    self
                }
            }
        ) );
    }

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

<|endoftext|>


<commit_before>#![no_std]

#[macro_use]
extern crate static_assertions;

// Placed in separate module so that DropCounter's fields are private
mod dc {
    /// A type that acts somewhat of a reference counter.
    pub struct DropCounter<'a> {
        count: &'a mut i32
    }

    impl<'a> DropCounter<'a> {
        pub fn new(count: &'a mut i32) -> DropCounter<'a> {
            *count += 1;
            DropCounter { count: count }
        }

        pub fn count(&self) -> &i32 {
            self.count
        }
    }

    impl<'a> Drop for DropCounter<'a> {
        fn drop(&mut self) {
            *self.count -= 1
        }
    }
}
use dc::*;

/// A type that panics on drop.
struct PanicDrop<T>(T);

impl<T> Drop for PanicDrop<T> {
    fn drop(&mut self) {
        panic!("Dropped!");
    }
}

#[test]
fn test_eq_size() {
    assert_eq_size!([u8; 2], u16);
    assert_eq_size!([u8; 2], u16, (u8, u8));
    assert_eq_size!([u8; 4], u32, (u16, u8, u8), (u16, u16));

    assert_eq_size_val!([0u8; 2], 0u16);
    assert_eq_size_val!([0u8; 2], 0u16, (0u8, 0u8));
    assert_eq_size_val!([0u8; 4], 0u32, (0u16, 0u8, 0u8), (0u16, 0u16));

    #[deny(unused_unsafe)]
    unsafe {
        assert_eq_size!(u8, u8);
        assert_eq_size_val!(0u8, 0u8);
    }

    // Should fail to compile (un-comment to test manually):
    // assert_eq_size!(u8, u16);
    // assert_eq_size_val!(0u8, 0u16);
}

#[test]
fn test_eq_size_no_drop() {
    assert_eq_size!(u32, PanicDrop<u32>);
    assert_eq_size!(PanicDrop<u32>, u32);
    assert_eq_size!(PanicDrop<u32>, PanicDrop<u32>);
}

#[test]
fn test_eq_size_drop_count() {
    let mut count = 0;
    {
        let dc = DropCounter::new(&mut count);
        assert_eq!(*dc.count(), 1);
        assert_eq_size_val!(dc, 0usize);
    }
    assert_eq!(count, 0);

    assert_eq_size_val!(DropCounter::new(&mut count), 0usize);
    assert_eq!(count, 0);
}
<commit_msg>[Tests] Add more assert_eq_size_val calls for DC<commit_after>#![no_std]

#[macro_use]
extern crate static_assertions;

// Placed in separate module so that DropCounter's fields are private
mod dc {
    /// A type that acts somewhat of a reference counter.
    pub struct DropCounter<'a> {
        count: &'a mut i32
    }

    impl<'a> DropCounter<'a> {
        pub fn new(count: &'a mut i32) -> DropCounter<'a> {
            *count += 1;
            DropCounter { count: count }
        }

        pub fn count(&self) -> &i32 {
            self.count
        }
    }

    impl<'a> Drop for DropCounter<'a> {
        fn drop(&mut self) {
            *self.count -= 1
        }
    }
}
use dc::*;

/// A type that panics on drop.
struct PanicDrop<T>(T);

impl<T> Drop for PanicDrop<T> {
    fn drop(&mut self) {
        panic!("Dropped!");
    }
}

#[test]
fn test_eq_size() {
    assert_eq_size!([u8; 2], u16);
    assert_eq_size!([u8; 2], u16, (u8, u8));
    assert_eq_size!([u8; 4], u32, (u16, u8, u8), (u16, u16));

    assert_eq_size_val!([0u8; 2], 0u16);
    assert_eq_size_val!([0u8; 2], 0u16, (0u8, 0u8));
    assert_eq_size_val!([0u8; 4], 0u32, (0u16, 0u8, 0u8), (0u16, 0u16));

    #[deny(unused_unsafe)]
    unsafe {
        assert_eq_size!(u8, u8);
        assert_eq_size_val!(0u8, 0u8);
    }

    // Should fail to compile (un-comment to test manually):
    // assert_eq_size!(u8, u16);
    // assert_eq_size_val!(0u8, 0u16);
}

#[test]
fn test_eq_size_no_drop() {
    assert_eq_size!(u32, PanicDrop<u32>);
    assert_eq_size!(PanicDrop<u32>, u32);
    assert_eq_size!(PanicDrop<u32>, PanicDrop<u32>);
}

#[test]
fn test_eq_size_drop_count() {
    let mut count = 0;
    {
        let dc = DropCounter::new(&mut count);
        assert_eq!(*dc.count(), 1);
        assert_eq_size_val!(dc, 0usize);
        assert_eq!(*dc.count(), 1);
        assert_eq_size_val!(dc, 0usize, dc);
        assert_eq!(*dc.count(), 1);
    }
    assert_eq!(count, 0);

    assert_eq_size_val!(DropCounter::new(&mut count), 0usize);
    assert_eq!(count, 0);
}
<|endoftext|>


<commit_before>#![no_std]

#[macro_use]
extern crate static_assertions;

#[test]
fn test_eq_size() {
    assert_eq_size!([u8; 2], u16);
    assert_eq_size!([u8; 2], u16, (u8, u8));
    assert_eq_size!([u8; 4], u32, (u16, u8, u8), (u16, u16));
    // assert_eq_size!(u8, u16); // Fails to compile
}
<commit_msg>[Tests] Ensure no 'unused_unsafe' warning is given<commit_after>#![no_std]

#[macro_use]
extern crate static_assertions;

#[test]
fn test_eq_size() {
    assert_eq_size!([u8; 2], u16);
    assert_eq_size!([u8; 2], u16, (u8, u8));
    assert_eq_size!([u8; 4], u32, (u16, u8, u8), (u16, u16));
    // assert_eq_size!(u8, u16); // Fails to compile

    #[deny(unused_unsafe)]
    unsafe { assert_eq_size!(u8, u8); }
}
<|endoftext|>


<commit_before>// A unit struct
#[derive(Debug)]
struct Nil;

// A tuple struct
#[derive(Debug)]
struct Pair(i32, f32);

// A struct with two fields
#[derive(Debug)]
struct Point {
    x: f32,
    y: f32,
}

// Structs can be reused as fields of another struct
#[allow(dead_code)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

pub fn main() {
    // Instantiate a `Point`
    let point: Point = Point { x: 0.3, y: 0.4 };

    // Access the fields of the point
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Destructure the point using a `let` binding
    let Point { x: my_x, y: my_y } = point;
    println!("point coordinates: ({}, {})", my_x, my_y);

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };
    //println!("Rectangle: {:?}", _rectangle);

    // Instantiate a unit struct
    let _nil = Nil;
    // Instantiate a tuple struct
    let pair = Pair(1, 0.1);

    println!("Unit struct {:?}", _nil);

    // Access the fields of a tuple struct
    println!("pair contains {:?} and {:?}", pair.0, pair.1);
    println!("pair struct {:?}", pair);

    // Destructure a tuple struct
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);

    // Calculates the area of a rectangle
    let rect: Rectangle = Rectangle{
        p1: Point{x:-2.0, y: 3.2},
        p2: Point{x: 3.2, y:-1.8},
    };
    println!("Rectangle Area: {:?}", rect_area(rect));
}

// Activity
// Calculates the area of a rectangle
fn rect_area(rect: Rectangle) -> f32 {
    1.0
}<commit_msg>Rust By Examples [RBE] - 3.1 Structures - activity - square<commit_after>// A unit struct
#[derive(Debug)]
struct Nil;

// A tuple struct
#[derive(Debug)]
struct Pair(i32, f32);

// A struct with two fields
#[derive(Debug)]
struct Point {
    x: f32,
    y: f32,
}

// Structs can be reused as fields of another struct
#[allow(dead_code)]
struct Rectangle {
    p1: Point,
    p2: Point,
}

pub fn main() {
    // Instantiate a `Point`
    let point: Point = Point { x: 0.3, y: 0.4 };

    // Access the fields of the point
    println!("point coordinates: ({}, {})", point.x, point.y);

    // Destructure the point using a `let` binding
    let Point { x: my_x, y: my_y } = point;
    println!("point coordinates: ({}, {})", my_x, my_y);

    let _rectangle = Rectangle {
        // struct instantiation is an expression too
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };
    //println!("Rectangle: {:?}", _rectangle);

    // Instantiate a unit struct
    let _nil = Nil;
    // Instantiate a tuple struct
    let pair = Pair(1, 0.1);

    println!("Unit struct {:?}", _nil);

    // Access the fields of a tuple struct
    println!("pair contains {:?} and {:?}", pair.0, pair.1);
    println!("pair struct {:?}", pair);

    // Destructure a tuple struct
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);

    // Calculates the area of a rectangle
    let rect: Rectangle = Rectangle{
        p1: Point{x:-2.0, y: 3.2},
        p2: Point{x: 3.2, y:-1.8},
    };
    println!("Rectangle Area: {:?}", rect_area(rect));
}

// Activity
// Calculates the area of a rectangle
fn rect_area(rect: Rectangle) -> f32 {
    1.0
}

// Activity
// returns a Rectangle with its lower left corner on the point
fn square(p: Point, w: f32) -> Rectangle {
    let point = &p;
    return Rectangle{
        p1: *point,
        p2: Point{x: p.x + w, y: p.y + w},
    };
}<|endoftext|>