<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::store::Store;
use libimagerror::trace::trace_error;
use std::error::Error;
use std::ops::Deref;
use std::ops::DerefMut;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;

use storeid::RStoreId;
use entry::RFileLockEntry;
use cache::StoreHandle;

wrappable_struct!(StoreHandle, StoreWrapper, STORE_WRAPPER);
class!(RStore);
impl_wrap!(StoreHandle, STORE_WRAPPER);
impl_unwrap!(RStore, StoreHandle, STORE_WRAPPER);
impl_verified_object!(RStore);

macro_rules! call_on_store_by_handle {
    {
        $store_handle:ident named $name:ident inside $operation:block
    }=> {{
        call_on_store_by_handle! {
            $store_handle
            named $name
            inside $operation
            on fail return NilClass::new().to_any_object()
        }
    }};

    {
        $store_handle:ident named $name:ident inside $operation:block on fail return $ex:expr
    } => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok(mut hm) => {
                    match hm.get($store_handle) {
                        Some($name) => { $operation },
                        None => {
                            VM::raise(Class::from_existing("RuntimeError"),
                                    "Tried to operate on non-existing object");
                            $ex
                        }
                    }
                },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    $ex
                }
            }
        }
    }};
}

macro_rules! call_on_store {
    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        $fle_name:ident <- fetch $fle_handle_name:ident
        operation $operation:block
    } => {
        call_on_store! {
            $store_name <- $itself wrapped inside $wrapper,
            $fle_name <- fetch $fle_handle_name,
            operation $operation,
            on fail return NilClass::new()
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        $fle_name:ident <- fetch $fle_handle_name:ident,
        operation $operation:block,
        on fail return $fail_expr:expr
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! {
            handle named $store_name inside {
                let $fle_name = match $store_name.get($fle_handle_name) {
                    Ok(Some(fle)) => fle,
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        return $fail_expr
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return $fail_expr
                    },
                };
                $operation
            }
            on fail return $fail_expr
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        operation $operation:block,
        on fail return $fail_expr:expr
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! {
            handle named $store_name inside $operation on fail return $fail_expr
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        operation $block
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! { handle named $name inside $operation }
    };
}

methods!(
    RStore,
    itself,

    // Create an FileLockEntry in the store
    //
    // # Returns:
    //
    // On success: A RFileLockEntry
    // On failure: Nil
    // On error: Nil + Exception
    //
    fn create(id: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Retrieve an FileLockEntry from the store
    //
    // # Returns:
    //
    // On success: A RFileLockEntry
    // On error: Nil + Exception
    //
    fn retrieve(id: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Get an FileLockEntry from the store
    //
    // # Returns:
    //
    // On success, if there is some: A RFileLockEntry
    // On success, if there is none: Nil
    // On error: Nil + Exception
    //
    fn get(sid: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Get all FileLockEntry of a module from the store
    //
    // # Returns:
    //
    // On success: A Array[RFileLockEntry]
    // On error: Nil + Exception
    //
    fn retrieve_for_module(name: RString) -> AnyObject {
        unimplemented!()
    }

    // Update a FileLockEntry in the store
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn update(fle: RFileLockEntry) -> NilClass {
        unimplemented!()
    }

    // Delete a FileLockEntry from the store
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn delete(sid: RStoreId) -> NilClass {
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                if let Err(e) = store.delete(sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Save a FileLockEntry in a new path inside the store, keep the RFileLockEntry
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn save_to(fle: RFileLockEntry, sid: RStoreId) -> NilClass {
        let fle = typecheck!(fle).unwrap().clone();
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            real_fle <- fetch fle,
            operation {
                if let Err(e) = store.save_to(&real_fle, sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Save a FileLockEntry in a new path inside the store, move the RFileLockEntry
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn save_as(fle: RFileLockEntry, sid: RStoreId) -> NilClass {
        let fle = typecheck!(fle).unwrap().clone();
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            real_fle <- fetch fle,
            operation {
                if let Err(e) = store.save_as(real_fle, sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Move one entry in the store to another place, by its ID
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn move_by_id(old: RStoreId, nw: RStoreId) -> NilClass {
        let old = typecheck!(old).unwrap().clone();
        let nw  = typecheck!(nw).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                if let Err(e) = store.move_by_id(old, nw) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Get the path of the store object
    //
    // # Returns:
    //
    // A RString
    //
    fn path() -> RString {
        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                store.path()
                    .clone()
                    .to_str()
                    .map(RString::new)
                    .unwrap_or(RString::new(""))
            },
            on fail return RString::new("")
        }
    }

);

pub fn setup() {
    unimplemented!()
}
<commit_msg>Impl Store::update() interface function<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use libimagstore::store::Store;
use libimagerror::trace::trace_error;
use std::error::Error;
use std::ops::Deref;
use std::ops::DerefMut;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;

use storeid::RStoreId;
use entry::RFileLockEntry;
use cache::StoreHandle;

wrappable_struct!(StoreHandle, StoreWrapper, STORE_WRAPPER);
class!(RStore);
impl_wrap!(StoreHandle, STORE_WRAPPER);
impl_unwrap!(RStore, StoreHandle, STORE_WRAPPER);
impl_verified_object!(RStore);

macro_rules! call_on_store_by_handle {
    {
        $store_handle:ident named $name:ident inside $operation:block
    }=> {{
        call_on_store_by_handle! {
            $store_handle
            named $name
            inside $operation
            on fail return NilClass::new().to_any_object()
        }
    }};

    {
        $store_handle:ident named $name:ident inside $operation:block on fail return $ex:expr
    } => {{
        use cache::RUBY_STORE_CACHE;

        let arc = RUBY_STORE_CACHE.clone();
        {
            let lock = arc.lock();
            match lock {
                Ok(mut hm) => {
                    match hm.get($store_handle) {
                        Some($name) => { $operation },
                        None => {
                            VM::raise(Class::from_existing("RuntimeError"),
                                    "Tried to operate on non-existing object");
                            $ex
                        }
                    }
                },
                Err(e) => {
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                    $ex
                }
            }
        }
    }};
}

macro_rules! call_on_store {
    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        $fle_name:ident <- fetch $fle_handle_name:ident
        operation $operation:block
    } => {
        call_on_store! {
            $store_name <- $itself wrapped inside $wrapper,
            $fle_name <- fetch $fle_handle_name,
            operation $operation,
            on fail return NilClass::new()
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        $fle_name:ident <- fetch $fle_handle_name:ident,
        operation $operation:block,
        on fail return $fail_expr:expr
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! {
            handle named $store_name inside {
                let $fle_name = match $store_name.get($fle_handle_name) {
                    Ok(Some(fle)) => fle,
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        return $fail_expr
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return $fail_expr
                    },
                };
                $operation
            }
            on fail return $fail_expr
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        operation $operation:block,
        on fail return $fail_expr:expr
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! {
            handle named $store_name inside $operation on fail return $fail_expr
        }
    };

    {
        $store_name:ident <- $itself:ident wrapped inside $wrapper:ident,
        operation $block
    } => {
        let handle = $itself.get_data(&*$wrapper);
        call_on_store_by_handle! { handle named $name inside $operation }
    };
}

methods!(
    RStore,
    itself,

    // Create an FileLockEntry in the store
    //
    // # Returns:
    //
    // On success: A RFileLockEntry
    // On failure: Nil
    // On error: Nil + Exception
    //
    fn create(id: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Retrieve an FileLockEntry from the store
    //
    // # Returns:
    //
    // On success: A RFileLockEntry
    // On error: Nil + Exception
    //
    fn retrieve(id: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Get an FileLockEntry from the store
    //
    // # Returns:
    //
    // On success, if there is some: A RFileLockEntry
    // On success, if there is none: Nil
    // On error: Nil + Exception
    //
    fn get(sid: RStoreId) -> AnyObject {
        unimplemented!()
    }

    // Get all FileLockEntry of a module from the store
    //
    // # Returns:
    //
    // On success: A Array[RFileLockEntry]
    // On error: Nil + Exception
    //
    fn retrieve_for_module(name: RString) -> AnyObject {
        unimplemented!()
    }

    // Update a FileLockEntry in the store
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn update(fle: RFileLockEntry) -> NilClass {
        let fle = typecheck!(fle).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            real_fle <- fetch fle,
            operation {
                if let Err(e) = store.update(real_fle) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Delete a FileLockEntry from the store
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn delete(sid: RStoreId) -> NilClass {
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                if let Err(e) = store.delete(sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Save a FileLockEntry in a new path inside the store, keep the RFileLockEntry
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn save_to(fle: RFileLockEntry, sid: RStoreId) -> NilClass {
        let fle = typecheck!(fle).unwrap().clone();
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            real_fle <- fetch fle,
            operation {
                if let Err(e) = store.save_to(&real_fle, sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Save a FileLockEntry in a new path inside the store, move the RFileLockEntry
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn save_as(fle: RFileLockEntry, sid: RStoreId) -> NilClass {
        let fle = typecheck!(fle).unwrap().clone();
        let sid = typecheck!(sid).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            real_fle <- fetch fle,
            operation {
                if let Err(e) = store.save_as(real_fle, sid) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Move one entry in the store to another place, by its ID
    //
    // # Returns:
    //
    // On success: Nil
    // On error: Nil + Exception
    //
    fn move_by_id(old: RStoreId, nw: RStoreId) -> NilClass {
        let old = typecheck!(old).unwrap().clone();
        let nw  = typecheck!(nw).unwrap().clone();

        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                if let Err(e) = store.move_by_id(old, nw) {
                    trace_error(&e);
                    VM::raise(Class::from_existing("RuntimeError"), e.description());
                }
                NilClass::new()
            },
            on fail return NilClass::new()
        }
    }

    // Get the path of the store object
    //
    // # Returns:
    //
    // A RString
    //
    fn path() -> RString {
        call_on_store! {
            store <- itself wrapped inside STORE_WRAPPER,
            operation {
                store.path()
                    .clone()
                    .to_str()
                    .map(RString::new)
                    .unwrap_or(RString::new(""))
            },
            on fail return RString::new("")
        }
    }

);

pub fn setup() {
    unimplemented!()
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Bounded version of Ukkonens DP algorithm for approximate pattern matching.
//! Complexity: O(n * k) on random texts.
//!
//! The algorithm finds all matches of a pattern in a text with up to k errors.
//! Idea is to use dynamic programming to column-wise explore the edit matrix, but to omit
//! parts of the matrix for which the error exceeds k. To achieve this, a value `lastk` is
//! maintained that provides the lower feasible boundary of the matrix.
//! Initially, lastk = min(k, m). In each iteration (over a column), lastk can increase by at most 1.
//!
//! # Example
//!
//! ```
//! use bio::pattern_matching::ukkonen::{Ukkonen, unit_cost};
//!
//! let mut ukkonen = Ukkonen::with_capacity(10, unit_cost);
//! let text = b"ACCGTGGATGAGCGCCATAG";
//! let pattern = b"TGAGCGA";
//! let occ: Vec<(usize, usize)> = ukkonen.find_all_end(pattern, text, 1).collect();
//! assert_eq!(occ, [(13, 1), (14, 1)]);
//! ```

use std::iter;
use std::cmp::min;
use std::iter::repeat;

use utils::{TextSlice, IntoTextIterator, TextIterator};

/// Default cost function (unit costs).
pub fn unit_cost(a: u8, b: u8) -> u32 {
    (a != b) as u32
}

/// Ukkonens algorithm.
#[allow(non_snake_case)]
pub struct Ukkonen<F>
    where F: Fn(u8, u8) -> u32
{
    D: [Vec<usize>; 2],
    cost: F,
}

impl<F> Ukkonen<F>
    where F: Fn(u8, u8) -> u32
{
    /// Initialize algorithm with given capacity and cost function.
    pub fn with_capacity(m: usize, cost: F) -> Self {
        let get_vec = || Vec::with_capacity(m + 1);
        Ukkonen {
            D: [get_vec(), get_vec()],
            cost: cost,
        }
    }

    /// Find all matches between pattern and text with up to k errors.
    /// Matches are returned as an iterator over pairs of end position and distance.
    pub fn find_all_end<'a, T: IntoTextIterator<'a>>(&'a mut self,
                                                     pattern: TextSlice<'a>,
                                                     text: T,
                                                     k: usize)
                                                     -> Matches<F, T::IntoIter> {
        let m = pattern.len();
        self.D[1].clear();
        self.D[1].extend(repeat(k + 1).take(m + 1));
        self.D[0].clear();
        self.D[0].extend(0..m + 1);
        Matches {
            ukkonen: self,
            pattern: pattern,
            text: text.into_iter().enumerate(),
            lastk: min(k, m),
            m: m,
            k: k,
        }
    }
}

/// Iterator over pairs of end positions and distance of matches.
pub struct Matches<'a, F, T: TextIterator<'a>>
    where F: 'a + Fn(u8, u8) -> u32
{
    ukkonen: &'a mut Ukkonen<F>,
    pattern: TextSlice<'a>,
    text: iter::Enumerate<T>,
    lastk: usize,
    m: usize,
    k: usize,
}

impl<'a, F, T: TextIterator<'a>> Iterator for Matches<'a, F, T>
    where F: 'a + Fn(u8, u8) -> u32
{
    type Item = (usize, usize);

    fn next(&mut self) -> Option<(usize, usize)> {
        let cost = &self.ukkonen.cost;
        for (i, c) in &mut self.text {
            let col = i % 2;
            let prev = 1 - col;

            // start with zero edit distance (semi-global alignment)
            self.ukkonen.D[col][0] = 0;
            self.lastk = min(self.lastk + 1, self.m);
            // in each column, go at most one cell further than before
            // do not look at cells with too big k
            for j in 1..self.lastk + 1 {
                self.ukkonen.D[col][j] =
                    min(min(self.ukkonen.D[prev][j] + 1, self.ukkonen.D[col][j - 1] + 1),
                        self.ukkonen.D[prev][j - 1] + (cost)(self.pattern[j - 1], *c) as usize);
            }

            // reduce lastk as long as k is exceeded: while lastk can increase by at most 1, it can
            // decrease more in one iteration.
            while self.ukkonen.D[col][self.lastk] > self.k {
                self.lastk -= 1;
            }

            if self.lastk == self.m {
                return Some((i, self.ukkonen.D[col][self.m]));
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_find_all_end() {
        let mut ukkonen = Ukkonen::with_capacity(10, unit_cost);
        let text = b"ACCGTGGATGAGCGCCATAG";
        let pattern = b"TGAGCGT";
        let occ: Vec<(usize, usize)> = ukkonen.find_all_end(pattern, text, 1).collect();
        assert_eq!(occ, [(13, 1), (14, 1)]);
    }
}
<commit_msg>Fix for #117 (#162)<commit_after>// Copyright 2014-2016 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Bounded version of Ukkonens DP algorithm for approximate pattern matching.
//! Complexity: O(n * k) on random texts.
//!
//! The algorithm finds all matches of a pattern in a text with up to k errors.
//! Idea is to use dynamic programming to column-wise explore the edit matrix, but to omit
//! parts of the matrix for which the error exceeds k. To achieve this, a value `lastk` is
//! maintained that provides the lower feasible boundary of the matrix.
//! Initially, lastk = min(k, m). In each iteration (over a column), lastk can increase by at most 1.
//!
//! # Example
//!
//! ```
//! use bio::pattern_matching::ukkonen::{Ukkonen, unit_cost};
//!
//! let mut ukkonen = Ukkonen::with_capacity(10, unit_cost);
//! let text = b"ACCGTGGATGAGCGCCATAG";
//! let pattern = b"TGAGCGA";
//! let occ: Vec<(usize, usize)> = ukkonen.find_all_end(pattern, text, 1).collect();
//! assert_eq!(occ, [(13, 1), (14, 1)]);
//! ```

use std::iter;
use std::cmp::min;
use std::iter::repeat;

use utils::{TextSlice, IntoTextIterator, TextIterator};

/// Default cost function (unit costs).
pub fn unit_cost(a: u8, b: u8) -> u32 {
    (a != b) as u32
}

/// Ukkonens algorithm.
#[allow(non_snake_case)]
pub struct Ukkonen<F>
    where F: Fn(u8, u8) -> u32
{
    D: [Vec<usize>; 2],
    cost: F,
}

impl<F> Ukkonen<F>
    where F: Fn(u8, u8) -> u32
{
    /// Initialize algorithm with given capacity and cost function.
    pub fn with_capacity(m: usize, cost: F) -> Self {
        let get_vec = || Vec::with_capacity(m + 1);
        Ukkonen {
            D: [get_vec(), get_vec()],
            cost: cost,
        }
    }

    /// Find all matches between pattern and text with up to k errors.
    /// Matches are returned as an iterator over pairs of end position and distance.
    pub fn find_all_end<'a, T: IntoTextIterator<'a>>(&'a mut self,
                                                     pattern: TextSlice<'a>,
                                                     text: T,
                                                     k: usize)
                                                     -> Matches<F, T::IntoIter> {
        let m = pattern.len();
        self.D[0].clear();
        self.D[0].extend(repeat(k + 1).take(m + 1));
        self.D[1].clear();
        self.D[1].extend(0..m + 1);
        Matches {
            ukkonen: self,
            pattern: pattern,
            text: text.into_iter().enumerate(),
            lastk: min(k, m),
            m: m,
            k: k,
        }
    }
}

/// Iterator over pairs of end positions and distance of matches.
pub struct Matches<'a, F, T: TextIterator<'a>>
    where F: 'a + Fn(u8, u8) -> u32
{
    ukkonen: &'a mut Ukkonen<F>,
    pattern: TextSlice<'a>,
    text: iter::Enumerate<T>,
    lastk: usize,
    m: usize,
    k: usize,
}

impl<'a, F, T: TextIterator<'a>> Iterator for Matches<'a, F, T>
    where F: 'a + Fn(u8, u8) -> u32
{
    type Item = (usize, usize);

    fn next(&mut self) -> Option<(usize, usize)> {
        let cost = &self.ukkonen.cost;
        for (i, c) in &mut self.text {
            let col = i % 2;
            let prev = 1 - col;

            // start with zero edit distance (semi-global alignment)
            self.ukkonen.D[col][0] = 0;
            self.lastk = min(self.lastk + 1, self.m);
            // in each column, go at most one cell further than before
            // do not look at cells with too big k
            for j in 1..self.lastk + 1 {
                self.ukkonen.D[col][j] =
                    min(min(self.ukkonen.D[prev][j] + 1, self.ukkonen.D[col][j - 1] + 1),
                        self.ukkonen.D[prev][j - 1] + (cost)(self.pattern[j - 1], *c) as usize);
            }

            // reduce lastk as long as k is exceeded: while lastk can increase by at most 1, it can
            // decrease more in one iteration.
            while self.ukkonen.D[col][self.lastk] > self.k {
                self.lastk -= 1;
            }

            if self.lastk == self.m {
                return Some((i, self.ukkonen.D[col][self.m]));
            }
        }

        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_find_all_end() {
        let mut ukkonen = Ukkonen::with_capacity(10, unit_cost);
        let text = b"ACCGTGGATGAGCGCCATAG";
        let pattern = b"TGAGCGT";
        let occ: Vec<(usize, usize)> = ukkonen.find_all_end(pattern, text, 1).collect();
        assert_eq!(occ, [(13, 1), (14, 1)]);
    }

    #[test]
    fn test_find_start() {
        let mut u = Ukkonen::with_capacity(10, unit_cost);

        let pattern = b"ACCGT";
        // hit begins at 1st position
        let text1 =   b"ACCGTGGATGAGCGCCATAG";
        // hit begins at 2nd position
        let text2 =  b"AACCGTGGATGAGCGCCATAG";

        let occ: Vec<(usize, usize)> = u.find_all_end(pattern, text1, 1).collect();
        assert_eq!(occ, [(3, 1), (4, 0), (5, 1)]);
        let occ: Vec<(usize, usize)> = u.find_all_end(pattern, text2, 1).collect();
        assert_eq!(occ, [(4, 1), (5, 0), (6, 1)]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Syn Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

extern crate syntax;
extern crate walkdir;

use std;
use std::env;
use std::process::Command;

use self::walkdir::DirEntry;

pub mod eq;
pub mod parse;

pub fn check_min_stack() {
    let min_stack_value = match env::var("RUST_MIN_STACK") {
        Ok(s) => s,
        Err(_) => {
            env::set_var("RUST_MIN_STACK", 16000000.to_string());
            return;
        }
    };
    let min_stack_value: usize = min_stack_value
        .parse()
        .expect("RUST_MIN_STACK env var should be set since some tests require it.");
    assert!(min_stack_value >= 16_000_000);
}

/// Read the `ABORT_AFTER_FAILURE` environment variable, and parse it.
pub fn abort_after() -> usize {
    match env::var("ABORT_AFTER_FAILURE") {
        Ok(s) => s.parse().expect("failed to parse ABORT_AFTER_FAILURE"),
        Err(_) => std::usize::MAX,
    }
}

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }
    let path_string = path.to_string_lossy();
    let path_string = if cfg!(windows) {
        path_string.replace('\\', "/").into()
    } else {
        path_string
    };
    // TODO assert that parsing fails on the parse-fail cases
    if path_string.starts_with("tests/rust/src/test/parse-fail")
        || path_string.starts_with("tests/rust/src/test/compile-fail")
        || path_string.starts_with("tests/rust/src/test/rustfix")
    {
        return false;
    }

    if path_string.starts_with("tests/rust/src/test/ui") {
        let stderr_path = path.with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    match path_string.as_ref() {
        // TODO outer attributes on literals
        //
        //     #[noop] "Hello, world!"
        //     #[cfg(unset)] 341
        "tests/rust/src/test/run-pass-fulldeps/proc-macro/attr-stmt-expr.rs" |
        "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
        // TODO outer attributes on closures
        //
        //     #[inline] || {}
        "tests/rust/src/test/run-pass/issue-49632.rs" |
        // TODO inner attributes within expressions
        //
        //     { #![foo] }
        "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
        // TODO inner attributes within blocks
        //
        //     fn test_fn_inner() { #![inner_fn_attr] }
        "tests/rust/src/test/run-pass/item-attributes.rs" |
        // TODO inclusive range syntax
        "tests/rust/src/test/run-pass/range-inclusive-pattern-precedence.rs" |
        // TODO feature(extern_in_paths)
        //
        //      use extern::xcrate;
        "tests/rust/src/test/run-make-fulldeps/save-analysis-rfc2126/extern_in_paths.rs" |
        "tests/rust/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs" |
        // TODO feature(macros_in_extern)
        //
        // https://github.com/dtolnay/syn/issues/390
        "tests/rust/src/test/run-pass-fulldeps/proc-macro/macros-in-extern.rs" |
        "tests/rust/src/test/run-pass/macros-in-extern.rs" |
        // TODO async closures and async functions
        //
        // https://github.com/dtolnay/syn/issues/396
        // https://github.com/dtolnay/syn/issues/395
        "tests/rust/src/test/run-pass/async-await.rs" |
        // TODO feature(label_break_value)
        //
        //     'block: { break 'block "value" }
        "tests/rust/src/test/run-pass/label_break_value.rs" |
        // TODO unreserve pure, offsetof, sizeof, alignof
        "tests/rust/src/test/run-pass/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs" |
        // TODO where-clause on associated types
        //
        //     impl Foo for Bar {
        //         type Assoc3<T> where T: Iterator = Vec<T>;
        //     }
        "tests/rust/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.rs" |
        // Deprecated placement syntax
        "tests/rust/src/test/run-pass/new-box-syntax.rs" |
        "tests/rust/src/test/ui/obsolete-in-place/bad.rs" |
        // not actually test cases
        "tests/rust/src/test/run-pass/auxiliary/macro-comma-support.rs" |
        "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" => false,
        _ => true,
    }
}

pub fn clone_rust() {
    let result = Command::new("tests/clone.sh").status().unwrap();
    println!("result: {}", result);
    assert!(result.success());
}
<commit_msg>File tickets against unsupported syntax<commit_after>// Copyright 2018 Syn Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

extern crate syntax;
extern crate walkdir;

use std;
use std::env;
use std::process::Command;

use self::walkdir::DirEntry;

pub mod eq;
pub mod parse;

pub fn check_min_stack() {
    let min_stack_value = match env::var("RUST_MIN_STACK") {
        Ok(s) => s,
        Err(_) => {
            env::set_var("RUST_MIN_STACK", 16000000.to_string());
            return;
        }
    };
    let min_stack_value: usize = min_stack_value
        .parse()
        .expect("RUST_MIN_STACK env var should be set since some tests require it.");
    assert!(min_stack_value >= 16_000_000);
}

/// Read the `ABORT_AFTER_FAILURE` environment variable, and parse it.
pub fn abort_after() -> usize {
    match env::var("ABORT_AFTER_FAILURE") {
        Ok(s) => s.parse().expect("failed to parse ABORT_AFTER_FAILURE"),
        Err(_) => std::usize::MAX,
    }
}

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }
    let path_string = path.to_string_lossy();
    let path_string = if cfg!(windows) {
        path_string.replace('\\', "/").into()
    } else {
        path_string
    };
    // TODO assert that parsing fails on the parse-fail cases
    if path_string.starts_with("tests/rust/src/test/parse-fail")
        || path_string.starts_with("tests/rust/src/test/compile-fail")
        || path_string.starts_with("tests/rust/src/test/rustfix")
    {
        return false;
    }

    if path_string.starts_with("tests/rust/src/test/ui") {
        let stderr_path = path.with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    match path_string.as_ref() {
        // TODO outer attributes on literals
        //
        //     #[noop] "Hello, world!"
        //     #[cfg(unset)] 341
        "tests/rust/src/test/run-pass-fulldeps/proc-macro/attr-stmt-expr.rs" |
        "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
        // TODO outer attributes on closures
        //
        // https://github.com/dtolnay/syn/issues/457
        "tests/rust/src/test/run-pass/issue-49632.rs" |
        // TODO inner attributes within expressions
        //
        // https://github.com/dtolnay/syn/issues/458
        // https://github.com/dtolnay/syn/issues/459
        // https://github.com/dtolnay/syn/issues/460
        "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
        // TODO inner attributes within foreign mod
        //
        // https://github.com/dtolnay/syn/issues/456
        "tests/rust/src/test/run-pass/item-attributes.rs" |
        // TODO feature(extern_in_paths)
        //
        // https://github.com/dtolnay/syn/issues/337
        "tests/rust/src/test/run-make-fulldeps/save-analysis-rfc2126/extern_in_paths.rs" |
        "tests/rust/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs" |
        // TODO feature(macros_in_extern)
        //
        // https://github.com/dtolnay/syn/issues/390
        "tests/rust/src/test/run-pass-fulldeps/proc-macro/macros-in-extern.rs" |
        "tests/rust/src/test/run-pass/macros-in-extern.rs" |
        // TODO async closures and async functions
        //
        // https://github.com/dtolnay/syn/issues/396
        // https://github.com/dtolnay/syn/issues/395
        "tests/rust/src/test/run-pass/async-await.rs" |
        // TODO feature(label_break_value)
        //
        // https://github.com/dtolnay/syn/issues/455
        "tests/rust/src/test/run-pass/label_break_value.rs" |
        // TODO unreserve pure, offsetof, sizeof, alignof
        //
        // https://github.com/dtolnay/syn/issues/454
        "tests/rust/src/test/run-pass/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs" |
        // TODO where-clause on associated types
        //
        // https://github.com/dtolnay/syn/issues/453
        "tests/rust/src/test/ui/rfc1598-generic-associated-types/generic-associated-types-where.rs" |
        // Deprecated placement syntax
        "tests/rust/src/test/run-pass/new-box-syntax.rs" |
        "tests/rust/src/test/ui/obsolete-in-place/bad.rs" |
        // not actually test cases
        "tests/rust/src/test/run-pass/auxiliary/macro-comma-support.rs" |
        "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" => false,
        _ => true,
    }
}

pub fn clone_rust() {
    let result = Command::new("tests/clone.sh").status().unwrap();
    println!("result: {}", result);
    assert!(result.success());
}
<|endoftext|>


<commit_before>//! A collection of methods that modify the background of the outputs.
use super::super::{Container, ContainerType, LayoutTree, TreeError,
                   MaybeBackground, IncompleteBackground};
use super::super::commands::CommandResult;

use uuid::Uuid;
use wayland_sys::server::wl_client;
use rustwlc::WlcView;
use rustwlc::wayland::wlc_view_get_wl_client;

impl LayoutTree {
    /// Attempts to attach the `bg` to the `outputs`.
    ///
    /// If any of them already have a background attached,
    pub fn attach_background(&mut self, bg: WlcView, output_id: Uuid)
                             -> Result<bool, TreeError> {
        match *self.lookup_mut(output_id)? {
            Container::Output { ref mut background, .. } => {
                match *background {
                    None => {
                        // The background wasn't expecting it
                        Ok(false)
                    }
                    Some(MaybeBackground::Incomplete(incomplete)) => {
                        let client: *mut wl_client;
                        unsafe {
                            client = wlc_view_get_wl_client(bg.0 as _) as _;
                        }
                        *background = Some(incomplete.build(client, bg));
                        Ok(match background.unwrap() {
                            MaybeBackground::Incomplete(_) => false,
                            MaybeBackground::Complete(_) => true,
                        })
                    },
                    _ => Ok(false)
                }
            },
            _ => Err(TreeError::UuidWrongType(output_id,
                                              vec![ContainerType::Output]))
        }
    }

    pub fn attach_incomplete_background(&mut self,
                                        bg: IncompleteBackground,
                                        output_id: Uuid) -> CommandResult {
        match *self.lookup_mut(output_id)? {
            Container::Output { ref mut background, .. } => {
                match *background {
                    None => {
                        *background = Some(bg.into());
                        Ok(())
                    },
                    Some(MaybeBackground::Complete(complete)) => {
                        complete.handle.close();
                        *background = Some(bg.into());
                        Ok(())
                    }
                    _ => {
                        warn!("Tried to set background while the other was still loading!");
                        Ok(())
                    }
                }
            },
            _ => Err(TreeError::UuidWrongType(output_id,
                                              vec![ContainerType::Output]))
        }
    }
}
<commit_msg>Fixed segfault, but at cost of replacing bg by running new wc-bg command<commit_after>//! A collection of methods that modify the background of the outputs.
use super::super::{Container, ContainerType, LayoutTree, TreeError,
                   MaybeBackground, IncompleteBackground};
use super::super::commands::CommandResult;

use uuid::Uuid;
use wayland_sys::server::wl_client;
use rustwlc::WlcView;
use rustwlc::wayland::wlc_view_get_wl_client;

impl LayoutTree {
    /// Attempts to attach the `bg` to the `outputs`.
    ///
    /// If any of them already have a background attached,
    pub fn attach_background(&mut self, bg: WlcView, output_id: Uuid)
                             -> Result<bool, TreeError> {
        match *self.lookup_mut(output_id)? {
            Container::Output { ref mut background, .. } => {
                match *background {
                    None => {
                        // The background wasn't expecting it
                        Ok(false)
                    }
                    Some(MaybeBackground::Incomplete(incomplete)) => {
                        let client: *mut wl_client;
                        unsafe {
                            client = wlc_view_get_wl_client(bg.0 as _) as _;
                        }
                        *background = Some(incomplete.build(client, bg));
                        Ok(match background.unwrap() {
                            MaybeBackground::Incomplete(_) => false,
                            MaybeBackground::Complete(_) => true,
                        })
                    },
                    _ => Ok(false)
                }
            },
            _ => Err(TreeError::UuidWrongType(output_id,
                                              vec![ContainerType::Output]))
        }
    }

    pub fn attach_incomplete_background(&mut self,
                                        bg: IncompleteBackground,
                                        output_id: Uuid) -> CommandResult {
        match *self.lookup_mut(output_id)? {
            Container::Output { ref mut background, .. } => {
                match *background {
                    None => {
                        *background = Some(bg.into());
                        Ok(())
                    },
                    Some(MaybeBackground::Complete(complete)) => {
                        warn!("Tried to set background while one is still active");
                        warn!("This operation is not allowed, due to a bug with xwayland");
                        Ok(())
                    }
                    _ => {
                        warn!("Tried to set background while the other was still loading!");
                        Ok(())
                    }
                }
            },
            _ => Err(TreeError::UuidWrongType(output_id,
                                              vec![ContainerType::Output]))
        }
    }
}
<|endoftext|>


<commit_before>// needs-sanitizer-support
// only-x86_64
//
// compile-flags: -Z sanitizer=address -O
//
// run-fail
// error-pattern: AddressSanitizer: stack-buffer-overflow
// error-pattern: 'xs' <== Memory access at offset

#![feature(test)]

use std::hint::black_box;
use std::mem;

fn main() {
    let xs = [0, 1, 2, 3];
    // Avoid optimizing everything out.
    let xs = black_box(xs.as_ptr());
    let code = unsafe { *xs.offset(4) };
    std::process::exit(code);
}
<commit_msg>Rollup merge of #69800 - tmiasko:address-g, r=nikic<commit_after>// needs-sanitizer-support
// only-x86_64
//
// compile-flags: -Z sanitizer=address -O -g
//
// run-fail
// error-pattern: AddressSanitizer: stack-buffer-overflow
// error-pattern: 'xs' (line 15) <== Memory access at offset

#![feature(test)]

use std::hint::black_box;

fn main() {
    let xs = [0, 1, 2, 3];
    // Avoid optimizing everything out.
    let xs = black_box(xs.as_ptr());
    let code = unsafe { *xs.offset(4) };
    std::process::exit(code);
}
<|endoftext|>


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

use 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();
        }
    }
}
<commit_msg>layout: Repaint when `z-index` is modified.<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, get_box.z_index
    ]);

    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/. */

//! Native representation of JS Promise values.
//!
//! This implementation differs from the traditional Rust DOM object, because the reflector
//! is provided by SpiderMonkey and has no knowledge of an associated native representation
//! (ie. dom::Promise). This means that native instances use native reference counting (Rc)
//! to ensure that no memory is leaked, which means that there can be multiple instances of
//! native Promise values that refer to the same JS value yet are distinct native objects
//! (ie. address equality for the native objects is meaningless).

use dom::bindings::conversions::root_from_object;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::reflector::{DomObject, MutDomObject, Reflector};
use dom::bindings::utils::AsCCharPtrPtr;
use dom::globalscope::GlobalScope;
use dom::promisenativehandler::PromiseNativeHandler;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{CallOriginalPromiseResolve, CallOriginalPromiseReject};
use js::jsapi::{JSAutoCompartment, CallArgs, JS_GetFunctionObject, JS_NewFunction};
use js::jsapi::{JSContext, HandleValue, HandleObject, IsPromiseObject, GetFunctionNativeReserved};
use js::jsapi::{JS_ClearPendingException, JSObject, AddRawValueRoot, RemoveRawValueRoot, PromiseState};
use js::jsapi::{MutableHandleObject, NewPromiseObject, ResolvePromise, RejectPromise, GetPromiseState};
use js::jsapi::{SetFunctionNativeReserved, NewFunctionWithReserved, AddPromiseReactions};
use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue, ObjectValue, Int32Value};
use std::ptr;
use std::rc::Rc;

#[dom_struct]
pub struct Promise {
    reflector: Reflector,
    /// Since Promise values are natively reference counted without the knowledge of
    /// the SpiderMonkey GC, an explicit root for the reflector is stored while any
    /// native instance exists. This ensures that the reflector will never be GCed
    /// while native code could still interact with its native representation.
    #[ignore_heap_size_of = "SM handles JS values"]
    permanent_js_root: Heap<JSVal>,
}

/// Private helper to enable adding new methods to Rc<Promise>.
trait PromiseHelper {
    #[allow(unsafe_code)]
    unsafe fn initialize(&self, cx: *mut JSContext);
}

impl PromiseHelper for Rc<Promise> {
    #[allow(unsafe_code)]
    unsafe fn initialize(&self, cx: *mut JSContext) {
        let obj = self.reflector().get_jsobject();
        self.permanent_js_root.set(ObjectValue(*obj));
        assert!(AddRawValueRoot(cx,
                                self.permanent_js_root.get_unsafe(),
                                b"Promise::root\0".as_c_char_ptr()));
    }
}

impl Drop for Promise {
    #[allow(unsafe_code)]
    fn drop(&mut self) {
        let cx = self.global().get_cx();
        unsafe {
            RemoveRawValueRoot(cx, self.permanent_js_root.get_unsafe());
        }
    }
}

impl Promise {
    #[allow(unsafe_code)]
    pub fn new(global: &GlobalScope) -> Rc<Promise> {
        let cx = global.get_cx();
        rooted!(in(cx) let mut obj = ptr::null_mut());
        unsafe {
            Promise::create_js_promise(cx, HandleObject::null(), obj.handle_mut());
            Promise::new_with_js_promise(obj.handle(), cx)
        }
    }

    #[allow(unsafe_code, unrooted_must_root)]
    pub fn duplicate(&self) -> Rc<Promise> {
        let cx = self.global().get_cx();
        unsafe {
            Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
        }
    }

    #[allow(unsafe_code, unrooted_must_root)]
    unsafe fn new_with_js_promise(obj: HandleObject, cx: *mut JSContext) -> Rc<Promise> {
        assert!(IsPromiseObject(obj));
        let promise = Promise {
            reflector: Reflector::new(),
            permanent_js_root: Heap::default(),
        };
        let mut promise = Rc::new(promise);
        Rc::get_mut(&mut promise).unwrap().init_reflector(obj.get());
        promise.initialize(cx);
        promise
    }

    #[allow(unsafe_code)]
    unsafe fn create_js_promise(cx: *mut JSContext, proto: HandleObject, obj: MutableHandleObject) {
        let do_nothing_func = JS_NewFunction(cx, Some(do_nothing_promise_executor), /* nargs = */ 2,
                                             /* flags = */ 0, ptr::null());
        assert!(!do_nothing_func.is_null());
        rooted!(in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
        assert!(!do_nothing_obj.is_null());
        obj.set(NewPromiseObject(cx, do_nothing_obj.handle(), proto));
        assert!(!obj.is_null());
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub fn new_resolved(
        global: &GlobalScope,
        cx: *mut JSContext,
        value: HandleValue,
    ) -> Fallible<Rc<Promise>> {
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        rooted!(in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, value) });
        assert!(!p.handle().is_null());
        unsafe {
            Ok(Promise::new_with_js_promise(p.handle(), cx))
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub fn new_rejected(
        global: &GlobalScope,
        cx: *mut JSContext,
        value: HandleValue,
    ) -> Fallible<Rc<Promise>> {
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        rooted!(in(cx) let p = unsafe { CallOriginalPromiseReject(cx, value) });
        assert!(!p.handle().is_null());
        unsafe {
            Ok(Promise::new_with_js_promise(p.handle(), cx))
        }
    }

    #[allow(unsafe_code)]
    pub fn resolve_native<T>(&self, val: &T) where T: ToJSValConvertible {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            val.to_jsval(cx, v.handle_mut());
            self.resolve(cx, v.handle());
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub unsafe fn resolve(&self, cx: *mut JSContext, value: HandleValue) {
        if !ResolvePromise(cx, self.promise_obj(), value) {
            JS_ClearPendingException(cx);
        }
    }

    #[allow(unsafe_code)]
    pub fn reject_native<T>(&self, val: &T) where T: ToJSValConvertible {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            val.to_jsval(cx, v.handle_mut());
            self.reject(cx, v.handle());
        }
    }

    #[allow(unsafe_code)]
    pub fn reject_error(&self, error: Error) {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            error.to_jsval(cx, &self.global(), v.handle_mut());
            self.reject(cx, v.handle());
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub unsafe fn reject(&self, cx: *mut JSContext, value: HandleValue) {
        if !RejectPromise(cx, self.promise_obj(), value) {
            JS_ClearPendingException(cx);
        }
    }

    #[allow(unsafe_code)]
    pub fn is_fulfilled(&self) -> bool {
        let state = unsafe { GetPromiseState(self.promise_obj()) };
        match state {
            PromiseState::Rejected | PromiseState::Fulfilled => true,
            _ => false
        }
    }

    #[allow(unsafe_code)]
    fn promise_obj(&self) -> HandleObject {
        let obj = self.reflector().get_jsobject();
        unsafe {
            assert!(IsPromiseObject(obj));
        }
        obj
    }

    #[allow(unsafe_code)]
    pub fn append_native_handler(&self, handler: &PromiseNativeHandler) {
        let cx = self.global().get_cx();
        rooted!(in(cx) let resolve_func =
                create_native_handler_function(cx,
                                               handler.reflector().get_jsobject(),
                                               NativeHandlerTask::Resolve));

        rooted!(in(cx) let reject_func =
                create_native_handler_function(cx,
                                               handler.reflector().get_jsobject(),
                                               NativeHandlerTask::Reject));

        unsafe {
            let ok = AddPromiseReactions(cx,
                                         self.promise_obj(),
                                         resolve_func.handle(),
                                         reject_func.handle());
            assert!(ok);
        }
    }
}

#[allow(unsafe_code)]
unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
    let args = CallArgs::from_vp(vp, argc);
    *args.rval() = UndefinedValue();
    true
}

const SLOT_NATIVEHANDLER: usize = 0;
const SLOT_NATIVEHANDLER_TASK: usize = 1;

#[derive(PartialEq)]
enum NativeHandlerTask {
    Resolve = 0,
    Reject = 1,
}

#[allow(unsafe_code)]
unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
    let args = CallArgs::from_vp(vp, argc);
    rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER));
    assert!(v.get().is_object());

    let handler = root_from_object::<PromiseNativeHandler>(v.to_object())
        .ok().expect("unexpected value for native handler in promise native handler callback");

    rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK));
    match v.to_int32() {
        v if v == NativeHandlerTask::Resolve as i32 => handler.resolved_callback(cx, args.get(0)),
        v if v == NativeHandlerTask::Reject as i32 => handler.rejected_callback(cx, args.get(0)),
        _ => panic!("unexpected native handler task value"),
    };

    true
}

#[allow(unsafe_code)]
fn create_native_handler_function(cx: *mut JSContext,
                                  holder: HandleObject,
                                  task: NativeHandlerTask) -> *mut JSObject {
    unsafe {
        let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
        assert!(!func.is_null());

        rooted!(in(cx) let obj = JS_GetFunctionObject(func));
        assert!(!obj.is_null());
        SetFunctionNativeReserved(obj.get(),
                                  SLOT_NATIVEHANDLER,
                                  &ObjectValue(*holder));
        SetFunctionNativeReserved(obj.get(),
                                  SLOT_NATIVEHANDLER_TASK,
                                  &Int32Value(task as i32));
        obj.get()
    }
}

<commit_msg>Make Promise::new_rejected unsafe<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/. */

//! Native representation of JS Promise values.
//!
//! This implementation differs from the traditional Rust DOM object, because the reflector
//! is provided by SpiderMonkey and has no knowledge of an associated native representation
//! (ie. dom::Promise). This means that native instances use native reference counting (Rc)
//! to ensure that no memory is leaked, which means that there can be multiple instances of
//! native Promise values that refer to the same JS value yet are distinct native objects
//! (ie. address equality for the native objects is meaningless).

use dom::bindings::conversions::root_from_object;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::reflector::{DomObject, MutDomObject, Reflector};
use dom::bindings::utils::AsCCharPtrPtr;
use dom::globalscope::GlobalScope;
use dom::promisenativehandler::PromiseNativeHandler;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{CallOriginalPromiseResolve, CallOriginalPromiseReject};
use js::jsapi::{JSAutoCompartment, CallArgs, JS_GetFunctionObject, JS_NewFunction};
use js::jsapi::{JSContext, HandleValue, HandleObject, IsPromiseObject, GetFunctionNativeReserved};
use js::jsapi::{JS_ClearPendingException, JSObject, AddRawValueRoot, RemoveRawValueRoot, PromiseState};
use js::jsapi::{MutableHandleObject, NewPromiseObject, ResolvePromise, RejectPromise, GetPromiseState};
use js::jsapi::{SetFunctionNativeReserved, NewFunctionWithReserved, AddPromiseReactions};
use js::jsapi::Heap;
use js::jsval::{JSVal, UndefinedValue, ObjectValue, Int32Value};
use std::ptr;
use std::rc::Rc;

#[dom_struct]
pub struct Promise {
    reflector: Reflector,
    /// Since Promise values are natively reference counted without the knowledge of
    /// the SpiderMonkey GC, an explicit root for the reflector is stored while any
    /// native instance exists. This ensures that the reflector will never be GCed
    /// while native code could still interact with its native representation.
    #[ignore_heap_size_of = "SM handles JS values"]
    permanent_js_root: Heap<JSVal>,
}

/// Private helper to enable adding new methods to Rc<Promise>.
trait PromiseHelper {
    #[allow(unsafe_code)]
    unsafe fn initialize(&self, cx: *mut JSContext);
}

impl PromiseHelper for Rc<Promise> {
    #[allow(unsafe_code)]
    unsafe fn initialize(&self, cx: *mut JSContext) {
        let obj = self.reflector().get_jsobject();
        self.permanent_js_root.set(ObjectValue(*obj));
        assert!(AddRawValueRoot(cx,
                                self.permanent_js_root.get_unsafe(),
                                b"Promise::root\0".as_c_char_ptr()));
    }
}

impl Drop for Promise {
    #[allow(unsafe_code)]
    fn drop(&mut self) {
        let cx = self.global().get_cx();
        unsafe {
            RemoveRawValueRoot(cx, self.permanent_js_root.get_unsafe());
        }
    }
}

impl Promise {
    #[allow(unsafe_code)]
    pub fn new(global: &GlobalScope) -> Rc<Promise> {
        let cx = global.get_cx();
        rooted!(in(cx) let mut obj = ptr::null_mut());
        unsafe {
            Promise::create_js_promise(cx, HandleObject::null(), obj.handle_mut());
            Promise::new_with_js_promise(obj.handle(), cx)
        }
    }

    #[allow(unsafe_code, unrooted_must_root)]
    pub fn duplicate(&self) -> Rc<Promise> {
        let cx = self.global().get_cx();
        unsafe {
            Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
        }
    }

    #[allow(unsafe_code, unrooted_must_root)]
    unsafe fn new_with_js_promise(obj: HandleObject, cx: *mut JSContext) -> Rc<Promise> {
        assert!(IsPromiseObject(obj));
        let promise = Promise {
            reflector: Reflector::new(),
            permanent_js_root: Heap::default(),
        };
        let mut promise = Rc::new(promise);
        Rc::get_mut(&mut promise).unwrap().init_reflector(obj.get());
        promise.initialize(cx);
        promise
    }

    #[allow(unsafe_code)]
    unsafe fn create_js_promise(cx: *mut JSContext, proto: HandleObject, obj: MutableHandleObject) {
        let do_nothing_func = JS_NewFunction(cx, Some(do_nothing_promise_executor), /* nargs = */ 2,
                                             /* flags = */ 0, ptr::null());
        assert!(!do_nothing_func.is_null());
        rooted!(in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
        assert!(!do_nothing_obj.is_null());
        obj.set(NewPromiseObject(cx, do_nothing_obj.handle(), proto));
        assert!(!obj.is_null());
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub fn new_resolved(
        global: &GlobalScope,
        cx: *mut JSContext,
        value: HandleValue,
    ) -> Fallible<Rc<Promise>> {
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        rooted!(in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, value) });
        assert!(!p.handle().is_null());
        unsafe {
            Ok(Promise::new_with_js_promise(p.handle(), cx))
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub unsafe fn new_rejected(
        global: &GlobalScope,
        cx: *mut JSContext,
        value: HandleValue,
    ) -> Fallible<Rc<Promise>> {
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        rooted!(in(cx) let p = CallOriginalPromiseReject(cx, value));
        assert!(!p.handle().is_null());
        Ok(Promise::new_with_js_promise(p.handle(), cx))
    }

    #[allow(unsafe_code)]
    pub fn resolve_native<T>(&self, val: &T) where T: ToJSValConvertible {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            val.to_jsval(cx, v.handle_mut());
            self.resolve(cx, v.handle());
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub unsafe fn resolve(&self, cx: *mut JSContext, value: HandleValue) {
        if !ResolvePromise(cx, self.promise_obj(), value) {
            JS_ClearPendingException(cx);
        }
    }

    #[allow(unsafe_code)]
    pub fn reject_native<T>(&self, val: &T) where T: ToJSValConvertible {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            val.to_jsval(cx, v.handle_mut());
            self.reject(cx, v.handle());
        }
    }

    #[allow(unsafe_code)]
    pub fn reject_error(&self, error: Error) {
        let cx = self.global().get_cx();
        let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get());
        rooted!(in(cx) let mut v = UndefinedValue());
        unsafe {
            error.to_jsval(cx, &self.global(), v.handle_mut());
            self.reject(cx, v.handle());
        }
    }

    #[allow(unrooted_must_root, unsafe_code)]
    pub unsafe fn reject(&self, cx: *mut JSContext, value: HandleValue) {
        if !RejectPromise(cx, self.promise_obj(), value) {
            JS_ClearPendingException(cx);
        }
    }

    #[allow(unsafe_code)]
    pub fn is_fulfilled(&self) -> bool {
        let state = unsafe { GetPromiseState(self.promise_obj()) };
        match state {
            PromiseState::Rejected | PromiseState::Fulfilled => true,
            _ => false
        }
    }

    #[allow(unsafe_code)]
    fn promise_obj(&self) -> HandleObject {
        let obj = self.reflector().get_jsobject();
        unsafe {
            assert!(IsPromiseObject(obj));
        }
        obj
    }

    #[allow(unsafe_code)]
    pub fn append_native_handler(&self, handler: &PromiseNativeHandler) {
        let cx = self.global().get_cx();
        rooted!(in(cx) let resolve_func =
                create_native_handler_function(cx,
                                               handler.reflector().get_jsobject(),
                                               NativeHandlerTask::Resolve));

        rooted!(in(cx) let reject_func =
                create_native_handler_function(cx,
                                               handler.reflector().get_jsobject(),
                                               NativeHandlerTask::Reject));

        unsafe {
            let ok = AddPromiseReactions(cx,
                                         self.promise_obj(),
                                         resolve_func.handle(),
                                         reject_func.handle());
            assert!(ok);
        }
    }
}

#[allow(unsafe_code)]
unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
    let args = CallArgs::from_vp(vp, argc);
    *args.rval() = UndefinedValue();
    true
}

const SLOT_NATIVEHANDLER: usize = 0;
const SLOT_NATIVEHANDLER_TASK: usize = 1;

#[derive(PartialEq)]
enum NativeHandlerTask {
    Resolve = 0,
    Reject = 1,
}

#[allow(unsafe_code)]
unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
    let args = CallArgs::from_vp(vp, argc);
    rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER));
    assert!(v.get().is_object());

    let handler = root_from_object::<PromiseNativeHandler>(v.to_object())
        .ok().expect("unexpected value for native handler in promise native handler callback");

    rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK));
    match v.to_int32() {
        v if v == NativeHandlerTask::Resolve as i32 => handler.resolved_callback(cx, args.get(0)),
        v if v == NativeHandlerTask::Reject as i32 => handler.rejected_callback(cx, args.get(0)),
        _ => panic!("unexpected native handler task value"),
    };

    true
}

#[allow(unsafe_code)]
fn create_native_handler_function(cx: *mut JSContext,
                                  holder: HandleObject,
                                  task: NativeHandlerTask) -> *mut JSObject {
    unsafe {
        let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
        assert!(!func.is_null());

        rooted!(in(cx) let obj = JS_GetFunctionObject(func));
        assert!(!obj.is_null());
        SetFunctionNativeReserved(obj.get(),
                                  SLOT_NATIVEHANDLER,
                                  &ObjectValue(*holder));
        SetFunctionNativeReserved(obj.get(),
                                  SLOT_NATIVEHANDLER_TASK,
                                  &Int32Value(task as i32));
        obj.get()
    }
}

<|endoftext|>