<commit_before>use std::sync::{Arc, RWLock};
use std::collections::HashMap;
use std::io::{IoResult, File};
use std::io::util::copy;
use std::path::BytesContainer;
use serialize::Encodable;
use http;
use http::server::ResponseWriter;
use time;
use mimes::get_media_type;
use mustache;
use mustache::{Template, Encoder, Error};

///A container for the response
pub struct Response<'a, 'b: 'a> {
    ///the original `http::server::ResponseWriter`
    pub origin: &'a mut ResponseWriter<'b>,
    templates: Arc<RWLock<HashMap<&'static str, Template>>>
}

impl<'a, 'b> Response<'a, 'b> {
    pub fn from_internal<'c, 'd>(response: &'c mut ResponseWriter<'d>,
                                 templates: Arc<RWLock<HashMap<&'static str, Template>>>)
                                -> Response<'c, 'd> {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Sets the content type by it's short form.
    /// Returns the response for chaining.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.content_type("html");
    /// ```
    pub fn content_type(&mut self, text: &str) -> &mut Response<'a,'b> {
        self.origin.headers.content_type = get_media_type(text);
        self
    }

    /// Sets the status code and returns the response for chaining
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.status_code(http::status::NotFound);
    /// ```
    pub fn status_code(&mut self, status: http::status::Status) -> &mut Response<'a,'b> {
        self.origin.status = status;
        self
    }

    /// Writes a response
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send("hello world");
    /// ```
    pub fn send<T: BytesContainer> (&mut self, text: T) {
        // TODO: This needs to be more sophisticated to return the correct headers
        // not just "some headers" :)
        Response::set_headers(self.origin);
        let _ = self.origin.write(text.container_as_bytes());
    }

    fn set_headers(response_writer: &mut http::server::ResponseWriter) {
        let ref mut headers = response_writer.headers;
        headers.date = Some(time::now_utc());

        // we don't need to set this https://github.com/Ogeon/rustful/issues/3#issuecomment-44787613
        headers.content_length = None;
        if headers.content_type.is_none() {
            headers.content_type = get_media_type("txt");
        }

        headers.server = Some(String::from_str("Nickel"));
    }

    /// Writes a file to the output.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send_file(some_path);
    /// ```
    pub fn send_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        self.origin.headers.content_length = None;

        self.origin.headers.content_type = path.extension_str().and_then(get_media_type);
        self.origin.headers.server = Some(String::from_str("Nickel"));
        copy(&mut file, self.origin)
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// let mut data = HashMap::<&'static str, &'static str>::new();
    /// data.insert("name", "user");
    /// response.render("examples/assets/template.tpl", &data);
    /// ```
    pub fn render<'a, T: Encodable<Encoder<'a>, Error>>
        (&mut self, path: &'static str, data: &T) {
        // Fast path doesn't need writer lock
        let _ = match self.templates.read().find(&path) {
            Some(template) => template.render(self.origin, data),
            None => {
                // Search again incase there was a race to compile the template
                let mut templates = self.templates.write();
                let template = templates.find_or_insert_with(path, |_| {
                    let mut file = File::open(&Path::new(path));
                    let raw_template = file.read_to_string()
                                            .ok()
                                            .expect(format!("Couldn't open the template file: {}",
                                                            path).as_slice());
                    mustache::compile_str(raw_template.as_slice())
                });

                template.render(self.origin, data)
            }
        };
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_media_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>fix(response): Solve deadlock<commit_after>use std::sync::{Arc, RWLock};
use std::collections::HashMap;
use std::io::{IoResult, File};
use std::io::util::copy;
use std::path::BytesContainer;
use serialize::Encodable;
use http;
use http::server::ResponseWriter;
use time;
use mimes::get_media_type;
use mustache;
use mustache::{Template, Encoder, Error};

///A container for the response
pub struct Response<'a, 'b: 'a> {
    ///the original `http::server::ResponseWriter`
    pub origin: &'a mut ResponseWriter<'b>,
    templates: Arc<RWLock<HashMap<&'static str, Template>>>
}

impl<'a, 'b> Response<'a, 'b> {
    pub fn from_internal<'c, 'd>(response: &'c mut ResponseWriter<'d>,
                                 templates: Arc<RWLock<HashMap<&'static str, Template>>>)
                                -> Response<'c, 'd> {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Sets the content type by it's short form.
    /// Returns the response for chaining.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.content_type("html");
    /// ```
    pub fn content_type(&mut self, text: &str) -> &mut Response<'a,'b> {
        self.origin.headers.content_type = get_media_type(text);
        self
    }

    /// Sets the status code and returns the response for chaining
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.status_code(http::status::NotFound);
    /// ```
    pub fn status_code(&mut self, status: http::status::Status) -> &mut Response<'a,'b> {
        self.origin.status = status;
        self
    }

    /// Writes a response
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send("hello world");
    /// ```
    pub fn send<T: BytesContainer> (&mut self, text: T) {
        // TODO: This needs to be more sophisticated to return the correct headers
        // not just "some headers" :)
        Response::set_headers(self.origin);
        let _ = self.origin.write(text.container_as_bytes());
    }

    fn set_headers(response_writer: &mut http::server::ResponseWriter) {
        let ref mut headers = response_writer.headers;
        headers.date = Some(time::now_utc());

        // we don't need to set this https://github.com/Ogeon/rustful/issues/3#issuecomment-44787613
        headers.content_length = None;
        if headers.content_type.is_none() {
            headers.content_type = get_media_type("txt");
        }

        headers.server = Some(String::from_str("Nickel"));
    }

    /// Writes a file to the output.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send_file(some_path);
    /// ```
    pub fn send_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        self.origin.headers.content_length = None;

        self.origin.headers.content_type = path.extension_str().and_then(get_media_type);
        self.origin.headers.server = Some(String::from_str("Nickel"));
        copy(&mut file, self.origin)
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// let mut data = HashMap::<&'static str, &'static str>::new();
    /// data.insert("name", "user");
    /// response.render("examples/assets/template.tpl", &data);
    /// ```
    pub fn render<'a, T: Encodable<Encoder<'a>, Error>>
        (&mut self, path: &'static str, data: &T) {
            // Fast path doesn't need writer lock
            let _ = match self.templates.read().find(&path)
            {
                Some(t) =>
                {
                    let _ = t.render(self.origin, data);
                    return
                },
                None => {}
            };

            // We didn't find the template, get writers lock
            let mut templates = self.templates.write();
            // Search again incase there was a race to compile the template
            let template = templates.find_or_insert_with(path, |_| {
                let mut file = File::open(&Path::new(path));
                let raw_template = file.read_to_string()
                    .ok()
                    .expect(format!("Couldn't open the template file: {}",
                                    path).as_slice());
                mustache::compile_str(raw_template.as_slice())
            });

            let _ = template.render(self.origin, data);
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_media_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

pub fn define_math(context: &mut EvalContext) {
    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, false);

    define_numeric_binary_op(context, "add", |a, b| a + b, true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, true);
    define_numeric_binary_op(context, "div", |a, b| a / b, true);
    define_numeric_binary_op(context, "mod", |a, b| a % b, true);

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    apply_program(context, include_str!("math.lion"));

    //TODO: ge, le, gt, lt, neg, not, sin, cos, tan, etc.
}

fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> bool {

    define_binary_op(context, name,
        move |a, b| ContextItem::Boolean(operator(a, b)), coerce_first);
}

fn define_numeric_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> RichNumber {

    define_binary_op(context, name,
        move |a, b| ContextItem::Number(operator(a, b)), coerce_first);
}

fn define_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> ContextItem {

    const PARAMS_LENGTH: usize = 2;
    define_built_in(context, name, PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParams("Expected two numbers".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();

            if coerce_first {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(operator(lhs, rhs))
        })
    );
}

#[cfg(test)]
mod tests {
    use ast::*;
    use eval::eval_context::EvalContext;
    use math::rich_number::RichNumber;

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }
}

<commit_msg>Adding true and false to prelude<commit_after>use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

pub fn define_math(context: &mut EvalContext) {
    context.set_boolean("true", true);
    context.set_boolean("false", false);

    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, false);

    define_numeric_binary_op(context, "add", |a, b| a + b, true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, true);
    define_numeric_binary_op(context, "div", |a, b| a / b, true);
    define_numeric_binary_op(context, "mod", |a, b| a % b, true);

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    apply_program(context, include_str!("math.lion"));

    //TODO: ge, le, gt, lt, neg, not, sin, cos, tan, etc.
}

fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> bool {

    define_binary_op(context, name,
        move |a, b| ContextItem::Boolean(operator(a, b)), coerce_first);
}

fn define_numeric_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> RichNumber {

    define_binary_op(context, name,
        move |a, b| ContextItem::Number(operator(a, b)), coerce_first);
}

fn define_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(RichNumber, RichNumber) -> ContextItem {

    const PARAMS_LENGTH: usize = 2;
    define_built_in(context, name, PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParams("Expected two numbers".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();

            if coerce_first {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(operator(lhs, rhs))
        })
    );
}

#[cfg(test)]
mod tests {
    use ast::*;
    use eval::eval_context::EvalContext;
    use math::rich_number::RichNumber;

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }
}

<|endoftext|>


<commit_before>//! An adapter providing time-related services, such as the current
//! timestamp or the current time of day.

use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::api::{ Error, InternalError, Operation, User };
use foxbox_taxonomy::values::{ Duration as ValDuration, Range, TimeStamp, Type, Value };
use foxbox_taxonomy::services::*;

use transformable_channels::mpsc::*;

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };

use chrono;
use chrono::*;
use timer;

static ADAPTER_NAME: &'static str = "Clock adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

#[derive(Clone)]
enum Op {
    Enter(Id<Channel>, Value),
    Exit(Id<Channel>, Value),
}

enum Movement { Enter, Exit }

pub struct Clock {
    /// Timer used to dispatch `register_watch` requests.
    timer: Mutex<timer::Timer>,

    getter_timestamp_id: Id<Channel>,
    getter_time_of_day_id: Id<Channel>,
    getter_interval_id: Id<Channel>,
}

/// A guard used to cancel watching for values.
struct Guard(Vec<timer::Guard>);
impl AdapterWatchGuard for Guard {
}

impl Clock {
    pub fn id() -> Id<AdapterId> {
        Id::new("clock@link.mozilla.org")
    }
    pub fn service_clock_id() -> Id<ServiceId> {
        Id::new("service:clock@link.mozilla.org")
    }
    pub fn getter_timestamp_id() -> Id<Channel> {
        Id::new("getter:timestamp.clock@link.mozilla.org")
    }
    pub fn getter_time_of_day_id() -> Id<Channel> {
        Id::new("getter:timeofday.clock@link.mozilla.org")
    }
    pub fn getter_interval_id() -> Id<Channel> {
        Id::new("getter:interval.clock@link.mozilla.org")
    }
}
impl Adapter for Clock {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            if id == self.getter_timestamp_id {
                let date = TimeStamp::from_datetime(chrono::UTC::now());
                (id, Ok(Some(Value::TimeStamp(date))))
            } else if id == self.getter_time_of_day_id {
                use chrono::Timelike;
                let date = chrono::Local::now();
                let duration = chrono::Duration::seconds(date.num_seconds_from_midnight() as i64);
                (id, Ok(Some(Value::Duration(ValDuration::from(duration)))))
            } else {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            }
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, _)| {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            })
            .collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, filter, tx)| {
            let tx = tx.map(|msg| {
                match msg {
                    Op::Enter(id, value) => {
                        WatchEvent::Enter {
                            id: id,
                            value: value
                        }
                    },
                    Op::Exit(id, value) => {
                        WatchEvent::Exit {
                            id: id,
                            value: value
                        }
                    },
                }
            });
            (id.clone(), match filter {
                Some(Value::Range(range)) => self.aux_register_watch(&id, &*range, Box::new(tx.clone())),
                _ => Err(Error::GetterRequiresThresholdForWatching(id)),
            })
        }).collect()
    }
}

impl Clock {
    fn aux_register_watch(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        match () {
            _ if *id == self.getter_time_of_day_id => self.aux_register_watch_timeofday(id, range, tx),
            _ if *id == self.getter_timestamp_id => self.aux_register_watch_timestamp(id, range, tx),
            _ if *id == self.getter_interval_id => self.aux_register_watch_interval(id, range, tx),
            _ => Err(Error::OperationNotSupported(Operation::Watch, id.clone()))
        }
    }

    fn aux_register_watch_interval(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::Duration.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when to call the trigger.
        let duration = match *range {
            Eq (ref val) | Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into()
            }
            _ => return Err(Error::InvalidValue(Value::Range(Box::new(range.clone()))))
        };

        debug!(target: "clock@link.mozilla.org", "[clock@link.mozilla.org] Scheduling a repeating watch with a duration of {}", duration);

        let id = id.clone();
        let guard = self.timer.lock().unwrap().schedule_repeating(duration, move || {
            // Send Enter followed immediately by Exit, to make sure that Thinkerbell
            // rules reset themselves.
            let _ = tx.send(Op::Enter(id.clone(),
                Value::Duration(ValDuration::from(duration))));
            let _ = tx.send(Op::Exit(id.clone(),
                Value::Duration(ValDuration::from(duration))));
        });
        Ok(Box::new(Guard(vec![guard])))
    }

    fn aux_register_watch_timeofday(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::Duration.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when to call the trigger. Repeat duration is always one day.
        let mut thresholds = match *range {
            Leq (ref val) => {
                // Equivalent to BetweenEq { min: 0am, max: val }
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, chrono::Duration::seconds(0)), (Movement::Exit, ts)]
            }
            Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                let ts = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts), (Movement::Exit, Duration::days(1))]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
            OutOfStrict { ref min, ref max } => {
                // Equivalent to BetweenEq {min: 0am, max: min} and BetweenEq {min: max, max: 0am}
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            Eq (ref val) => {
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts.clone()), (Movement::Exit, ts)]
            }
        };

        // Determine when the next timers needs to launch.
        let now = chrono::Local::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, threshold)| {
            let date = match Self::get_next_date(&now, threshold) {
                Err(_) => return None,
                Ok(date) => date,
            };
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule(date, Some(Duration::days(1)), move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }

    fn get_next_date(now: &DateTime<Local>, time_of_day: Duration)
        -> Result<DateTime<Local>, Error>
    {
        match chrono::Local::today().and_time(NaiveTime::from_hms(0, 0, 0) + time_of_day) {
            None => Err(Error::InvalidValue(Value::Duration(ValDuration::from(time_of_day)))),
            Some(date) => {
                if date >= *now  {
                    Ok(date)
                } else {
                    // Otherwise, shift to tomorrow.
                    match date.checked_add(Duration::days(1)) {
                        None => Err(Error::InvalidValue(Value::Duration(ValDuration::from(time_of_day)))),
                        Some(date) => Ok(date)
                    }
                }
            }
        }
    }

    fn aux_register_watch_timestamp(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::TimeStamp.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when/if to call the trigger.
        let mut thresholds = match *range {
            Leq (_) => {
                // This variant doesn't make sense.
                return Ok(Box::new(Guard(vec![])))
            }
            Geq (ref val) | Eq (ref val) => {
                let ts = *try!(val.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts)]
            }
            OutOfStrict { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
        };

        // Determine when/if the next timers needs to launch.
        let now = chrono::UTC::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, date)| {
            if date < now {
                return None
            }
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule_with_date(date, move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }
}

impl Clock {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let getter_timestamp_id = Clock::getter_timestamp_id();
        let getter_time_of_day_id = Clock::getter_time_of_day_id();
        let getter_interval_id = Clock::getter_interval_id();
        let service_clock_id = Clock::service_clock_id();
        let adapter_id = Clock::id();
        let clock = Arc::new(Clock {
            timer: Mutex::new(timer::Timer::new()),
            getter_timestamp_id: getter_timestamp_id.clone(),
            getter_time_of_day_id: getter_time_of_day_id.clone(),
            getter_interval_id: getter_interval_id.clone(),
        });
        try!(adapt.add_adapter(clock));
        let mut service = Service::empty(&service_clock_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla clock v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            supports_watch: true,
            supports_fetch: true,
            kind: ChannelKind::CurrentTimeOfDay,
            ..Channel::empty(&getter_time_of_day_id, &service_clock_id, &adapter_id)
        }));
        try!(adapt.add_channel(Channel {
            supports_watch: true,
            supports_fetch: true,
            kind: ChannelKind::CurrentTime,
            ..Channel::empty(&getter_timestamp_id, &service_clock_id, &adapter_id)
        }));
        try!(adapt.add_channel(Channel {
            supports_watch: true,
            kind: ChannelKind::CountEveryInterval,
            ..Channel::empty(&getter_interval_id, &service_clock_id, &adapter_id)
        }));
        Ok(())
    }
}
<commit_msg>Clock: Decentralizing ChannelKind<commit_after>//! An adapter providing time-related services, such as the current
//! timestamp or the current time of day.

use foxbox_taxonomy::api::{ Error, InternalError, Operation, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ Duration as ValDuration, Range, TimeStamp, Type, Value };

use transformable_channels::mpsc::*;

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };

use chrono;
use chrono::*;
use timer;

static ADAPTER_NAME: &'static str = "Clock adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

#[derive(Clone)]
enum Op {
    Enter(Id<Channel>, Value),
    Exit(Id<Channel>, Value),
}

enum Movement { Enter, Exit }

pub struct Clock {
    /// Timer used to dispatch `register_watch` requests.
    timer: Mutex<timer::Timer>,

    getter_timestamp_id: Id<Channel>,
    getter_time_of_day_id: Id<Channel>,
    getter_interval_id: Id<Channel>,
}

/// A guard used to cancel watching for values.
struct Guard(Vec<timer::Guard>);
impl AdapterWatchGuard for Guard {
}

impl Clock {
    pub fn id() -> Id<AdapterId> {
        Id::new("clock@link.mozilla.org")
    }
    pub fn service_clock_id() -> Id<ServiceId> {
        Id::new("service:clock@link.mozilla.org")
    }
    pub fn getter_timestamp_id() -> Id<Channel> {
        Id::new("getter:timestamp.clock@link.mozilla.org")
    }
    pub fn getter_time_of_day_id() -> Id<Channel> {
        Id::new("getter:timeofday.clock@link.mozilla.org")
    }
    pub fn getter_interval_id() -> Id<Channel> {
        Id::new("getter:interval.clock@link.mozilla.org")
    }
}
impl Adapter for Clock {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            if id == self.getter_timestamp_id {
                let date = TimeStamp::from_datetime(chrono::UTC::now());
                (id, Ok(Some(Value::TimeStamp(date))))
            } else if id == self.getter_time_of_day_id {
                use chrono::Timelike;
                let date = chrono::Local::now();
                let duration = chrono::Duration::seconds(date.num_seconds_from_midnight() as i64);
                (id, Ok(Some(Value::Duration(ValDuration::from(duration)))))
            } else {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            }
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, _)| {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            })
            .collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, filter, tx)| {
            let tx = tx.map(|msg| {
                match msg {
                    Op::Enter(id, value) => {
                        WatchEvent::Enter {
                            id: id,
                            value: value
                        }
                    },
                    Op::Exit(id, value) => {
                        WatchEvent::Exit {
                            id: id,
                            value: value
                        }
                    },
                }
            });
            (id.clone(), match filter {
                Some(Value::Range(range)) => self.aux_register_watch(&id, &*range, Box::new(tx.clone())),
                _ => Err(Error::GetterRequiresThresholdForWatching(id)),
            })
        }).collect()
    }
}

impl Clock {
    fn aux_register_watch(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        match () {
            _ if *id == self.getter_time_of_day_id => self.aux_register_watch_timeofday(id, range, tx),
            _ if *id == self.getter_timestamp_id => self.aux_register_watch_timestamp(id, range, tx),
            _ if *id == self.getter_interval_id => self.aux_register_watch_interval(id, range, tx),
            _ => Err(Error::OperationNotSupported(Operation::Watch, id.clone()))
        }
    }

    fn aux_register_watch_interval(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::Duration.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when to call the trigger.
        let duration = match *range {
            Eq (ref val) | Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into()
            }
            _ => return Err(Error::InvalidValue(Value::Range(Box::new(range.clone()))))
        };

        debug!(target: "clock@link.mozilla.org", "[clock@link.mozilla.org] Scheduling a repeating watch with a duration of {}", duration);

        let id = id.clone();
        let guard = self.timer.lock().unwrap().schedule_repeating(duration, move || {
            // Send Enter followed immediately by Exit, to make sure that Thinkerbell
            // rules reset themselves.
            let _ = tx.send(Op::Enter(id.clone(),
                Value::Duration(ValDuration::from(duration))));
            let _ = tx.send(Op::Exit(id.clone(),
                Value::Duration(ValDuration::from(duration))));
        });
        Ok(Box::new(Guard(vec![guard])))
    }

    fn aux_register_watch_timeofday(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::Duration.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when to call the trigger. Repeat duration is always one day.
        let mut thresholds = match *range {
            Leq (ref val) => {
                // Equivalent to BetweenEq { min: 0am, max: val }
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, chrono::Duration::seconds(0)), (Movement::Exit, ts)]
            }
            Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                let ts = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts), (Movement::Exit, Duration::days(1))]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
            OutOfStrict { ref min, ref max } => {
                // Equivalent to BetweenEq {min: 0am, max: min} and BetweenEq {min: max, max: 0am}
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            Eq (ref val) => {
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts.clone()), (Movement::Exit, ts)]
            }
        };

        // Determine when the next timers needs to launch.
        let now = chrono::Local::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, threshold)| {
            let date = match Self::get_next_date(&now, threshold) {
                Err(_) => return None,
                Ok(date) => date,
            };
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule(date, Some(Duration::days(1)), move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }

    fn get_next_date(now: &DateTime<Local>, time_of_day: Duration)
        -> Result<DateTime<Local>, Error>
    {
        match chrono::Local::today().and_time(NaiveTime::from_hms(0, 0, 0) + time_of_day) {
            None => Err(Error::InvalidValue(Value::Duration(ValDuration::from(time_of_day)))),
            Some(date) => {
                if date >= *now  {
                    Ok(date)
                } else {
                    // Otherwise, shift to tomorrow.
                    match date.checked_add(Duration::days(1)) {
                        None => Err(Error::InvalidValue(Value::Duration(ValDuration::from(time_of_day)))),
                        Some(date) => Ok(date)
                    }
                }
            }
        }
    }

    fn aux_register_watch_timestamp(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Sanity checks
        let typ = try!(range.get_type().map_err(Error::TypeError));
        try!(Type::TimeStamp.ensure_eq(&typ).map_err(Error::TypeError));

        // Now determine when/if to call the trigger.
        let mut thresholds = match *range {
            Leq (_) => {
                // This variant doesn't make sense.
                return Ok(Box::new(Guard(vec![])))
            }
            Geq (ref val) | Eq (ref val) => {
                let ts = *try!(val.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts)]
            }
            OutOfStrict { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
        };

        // Determine when/if the next timers needs to launch.
        let now = chrono::UTC::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, date)| {
            if date < now {
                return None
            }
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule_with_date(date, move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }
}

impl Clock {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let getter_timestamp_id = Clock::getter_timestamp_id();
        let getter_time_of_day_id = Clock::getter_time_of_day_id();
        let getter_interval_id = Clock::getter_interval_id();
        let service_clock_id = Clock::service_clock_id();
        let adapter_id = Clock::id();
        let clock = Arc::new(Clock {
            timer: Mutex::new(timer::Timer::new()),
            getter_timestamp_id: getter_timestamp_id.clone(),
            getter_time_of_day_id: getter_time_of_day_id.clone(),
            getter_interval_id: getter_interval_id.clone(),
        });
        try!(adapt.add_adapter(clock));
        let mut service = Service::empty(&service_clock_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla clock v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-of-day-seconds"),
            supports_fetch: Some(Signature::returns(Maybe::Required(Type::Duration))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(Type::Duration),
                returns: Maybe::Required(Type::Duration)
            }),
            id: getter_time_of_day_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-timestamp-rfc-3339"),
            supports_fetch: Some(Signature::returns(Maybe::Required(Type::TimeStamp))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(Type::TimeStamp),
                returns: Maybe::Required(Type::TimeStamp)
            }),
            id: getter_timestamp_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-interval-seconds"),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(Type::Duration),
                returns: Maybe::Required(Type::TimeStamp)
            }),
            id: getter_interval_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = use_by_ref(type_, direction);
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}

#[inline]
fn use_by_ref(type_: &library::Type, direction: library::ParameterDirection) -> bool {
    use library::Type::*;
    match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    }
}
<commit_msg>Auto merge of #166 - gkoz:pr, r=gkoz<commit_after>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use super::conversion_type::ConversionType;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Record(ref record) => Ok(record.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer => rust_type = rust_type.map_any(|s| format!("Option<{}>", s)),
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = use_by_ref(type_, direction);
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Record(..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        Class(..) => {
            match direction {
                _ if env.type_status(&type_id.full_name(&env.library)).ignored() => {
                    Err(format!("/*Ignored*/{}", rust_type.as_str()))
                }
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}

#[inline]
fn use_by_ref(type_: &library::Type, direction: library::ParameterDirection) -> bool {
    use library::Type::*;
    match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Record(..) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#[macro_export]
#[doc(hidden)]
macro_rules! check_recursion {
    ($widget:ident) => {
        if $widget.try_borrow_mut().is_err() {
            panic!("An event to the same widget was emitted in the update() method, which would cause an infinite \
                   recursion.\nThis can be caused by calling a gtk+ function that is connected to send a message \
                   to the same widget.\nInspect the stack trace to determine which call it is.\nThen you can either \
                   refactor your code to avoid a cyclic event dependency or block events from being emitted by doing \
                   the following:\n{\n    let _lock = self.model.relm.stream().lock();\n    // Your panicking call.\n}\
                   \nSee this example: \
                   https://github.com/antoyo/relm/blob/feature/futures-glib/examples/checkboxes.rs#L88\
                   This issue can also happen when emitting a signal to the same widget, in which case you need to\
                   refactor your code to avoid this cyclic event dependency.");
        }
    };
}

/// Connect events to sending a message.
///
/// Rule #1:
/// Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// Rule #2:
/// Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// Rule #3:
/// Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// Rule #4:
/// Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        let stream = $other_component.stream().clone();
        $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event where the return value is retrieved asynchronously.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), async $msg:ident $( ( $( $arg:expr ),*) )* ) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let cx = ::futures_glib::MainContext::default(|cx| cx.clone());
            let lp = ::relm::MainLoop::new(Some(&cx));
            let (resolver, rx) = ::relm::Resolver::channel(lp.clone());
            let msg: Option<_> = $crate::IntoOption::into_option($msg($($($arg,)*)* resolver));
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            lp.run();
            // TODO: remove unwrap().
            ::futures::Stream::wait(rx).next().unwrap().unwrap()
        });
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        let stream = $dst_component.stream().clone();
        $src_component.stream().observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}
<commit_msg>Add connect_stream! macro<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/// Connect events to sending a message.
///
/// Rule #1:
/// Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// Rule #2:
/// Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// Rule #3:
/// Send `$msg` with a Resolver and block the callback into it resolves to a value.
/// This is useful in case you need an access to the model to decide whether you want to inhibit
/// the event or not.
///
/// Rule #4:
/// Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// Rule #5:
/// Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        connect_stream!($widget, $event($($args),*), $other_component.stream(), $msg);
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event where the return value is retrieved asynchronously.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), async $msg:ident $( ( $( $arg:expr ),*) )* ) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let cx = ::futures_glib::MainContext::default(|cx| cx.clone());
            let lp = ::relm::MainLoop::new(Some(&cx));
            let (resolver, rx) = ::relm::Resolver::channel(lp.clone());
            let msg: Option<_> = $crate::IntoOption::into_option($msg($($($arg,)*)* resolver));
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            lp.run();
            // TODO: remove unwrap().
            ::futures::Stream::wait(rx).next().unwrap().unwrap()
        });
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        connect_stream!($src_component@$message, $dst_component.stream(), $msg);
    };
}

/// Connect events to sending a message.
/// Similar to `connect!` but wants a stream instead of a component.
///
/// Rule #1:
/// Send `$msg` to `$other_stream` when the GTK+ `$event` is emitted on `$widget`.
///
/// Rule #2:
/// Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect_stream {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_stream:expr, $msg:expr) => {
        let stream = $other_stream.clone();
        $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_stream:expr, $msg:expr) => {
        let stream = $dst_stream.clone();
        $src_component.stream().observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::collections::vec_deque::VecDeque;
use std::vec::Vec;
use js_types::js_type::{JsT, JsType, Marking};
use uuid::Uuid;

/// A map from uuids to references to objects. The interface to the garbage
/// collector, this map gets updated whenever a GC cycle is performed. References
/// that are no longer live may be dropped from the map.
pub struct UuidMap<'r> {
    inner: HashMap<Uuid, &'r RefCell<JsT>>,
}

impl<'r> UuidMap<'r> {
    pub fn new() -> UuidMap<'r> {
        UuidMap {
            inner: HashMap::new(),
        }
    }

    /*pub fn from_jsts(jsts: Vec<RefCell<JsT>>) -> UuidMap<'r> {
        let mut tmp_map = HashMap::new();
        jsts.iter().map(|j| tmp_map.insert(j.borrow.uuid, j));
        UuidMap {
            inner: tmp_map,
        }
    }*/

    pub fn insert_ref(&mut self, jst: &'r RefCell<JsT>) {
        self.inner.insert(jst.borrow().uuid, jst);
    }

    pub fn drop_uuid(&mut self, uuid: Uuid) {
        self.inner.remove(&uuid);
    }

    pub fn drop_ref(&mut self, jst: RefCell<JsT>) {
        self.inner.remove(&jst.borrow().uuid);
    }

    pub fn mark_uuid(&mut self, uuid: Uuid, marking: Marking) {
        if let Some(jst) = self.inner.get_mut(&uuid) {
            jst.borrow_mut().gc_flag = marking;
        }
    }
}

/// A graph node that stores sets of object uuids, as well as a list of
/// pointers to nodes it can flow to.
/// A variable v is live on edge e if there is:
///     - a node n in the CFG s.t. use[n] contains v
///     - a directed path from e -> n s.t. for every statement s on the path,
///       def[s] does not contain v
#[derive(Clone)]
struct LivenessNode {
    defs: HashSet<Uuid>,
    uses: HashSet<Uuid>,
    ins: HashSet<Uuid>,
    outs: HashSet<Uuid>,
    succ: Vec<Box<LivenessNode>>,
    pred: Vec<Box<LivenessNode>>,
}

struct LivenessGraph {
    nodes: Vec<LivenessNode>,
}

type Statement = i32; // TODO, placeholder

impl LivenessNode {
    fn new() -> LivenessNode {
        LivenessNode {
            defs: HashSet::new(),
            uses: HashSet::new(),
            ins: HashSet::new(),
            outs: HashSet::new(),
            succ: Vec::new(),
            pred: Vec::new(),
        }
    }

    fn node_flow(&mut self) {
        self.ins = self.uses.union(&self.outs.difference(&self.defs).cloned().collect())
                            .cloned().collect();

        self.outs = self.succ.clone().into_iter().flat_map(|s| s.ins).collect();
    }

    // TODO Given a list of AST instructions, parse out what UUIDs are used
    fn compute_uses(&mut self, statements: Vec<Statement>) {

    }

    fn compute_defs(&mut self, statements: Vec<Statement>) {

    }

}

impl LivenessGraph {
    fn new() -> LivenessGraph {
        LivenessGraph {
            nodes: Vec::new(),
        }
    }

    fn graph_flow(&mut self) {
        let mut node_queue: VecDeque<&mut LivenessNode> = self.nodes.iter_mut().collect();
        while let Some(n) = node_queue.pop_front() {
            let old_ins = n.ins.clone();
            let old_outs = n.outs.clone();
            n.node_flow();
            if old_outs != n.outs {
                n.succ.iter_mut().map(|s| node_queue.push_back(s));
            }
        }
    }
}

pub struct StackFrame<'m> {
    members: Vec<&'m JsT>,
}

impl<'m> StackFrame<'m> {
    pub fn new() -> StackFrame<'m> {
        StackFrame { members: Vec::new(), }
    }

    pub fn alloc_ref(&mut self, refce: &'m mut JsT)  {
        self.members.push(refce);
    }
}
<commit_msg>Rename and refactor several methods within ref_manip<commit_after>use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::collections::vec_deque::VecDeque;
use std::vec::Vec;
use js_types::js_type::{JsT, JsType, Marking};
use uuid::Uuid;

/// A map from uuids to references to objects. The interface to the garbage
/// collector, this map gets updated whenever a GC cycle is performed. References
/// that are no longer live may be dropped from the map.
pub struct UuidMap<'r> {
    inner: HashMap<Uuid, &'r RefCell<JsT>>,
}

impl<'r> UuidMap<'r> {
    pub fn new() -> UuidMap<'r> {
        UuidMap {
            inner: HashMap::new(),
        }
    }

    pub fn insert_by_refcell(&mut self, jst: &'r RefCell<JsT>) {
        self.inner.insert(jst.borrow().uuid, jst);
    }

    pub fn insert_by_val(&mut self, jst: JsT) -> bool {
        if let Some(ref refcell) = self.inner.get(&jst.uuid) {
            let mut inner = refcell.borrow_mut();
            *inner = jst;
            true
        } else {
            false
        }
    }

    pub fn remove_by_uuid(&mut self, uuid: Uuid) {
        self.inner.remove(&uuid);
    }

    pub fn remove_by_ref(&mut self, jst: &'r RefCell<JsT>) {
        self.inner.remove(&jst.borrow().uuid);
    }

    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<JsT> {
        if let Some(ref refcell) = self.inner.get(&uuid) {
            Some(refcell.borrow().clone())
        } else {
            None
        }
    }

    pub fn mark_uuid(&mut self, uuid: Uuid, marking: Marking) {
        if let Some(jst) = self.inner.get_mut(&uuid) {
            jst.borrow_mut().gc_flag = marking;
        }
    }
}

/// A graph node that stores sets of object uuids, as well as a list of
/// pointers to nodes it can flow to.
/// A variable v is live on edge e if there is:
///     - a node n in the CFG s.t. use[n] contains v
///     - a directed path from e -> n s.t. for every statement s on the path,
///       def[s] does not contain v
#[derive(Clone)]
struct LivenessNode {
    defs: HashSet<Uuid>,
    uses: HashSet<Uuid>,
    ins: HashSet<Uuid>,
    outs: HashSet<Uuid>,
    succ: Vec<Box<LivenessNode>>,
    pred: Vec<Box<LivenessNode>>,
}

struct LivenessGraph {
    nodes: Vec<LivenessNode>,
}

type Statement = i32; // TODO, placeholder

impl LivenessNode {
    fn new() -> LivenessNode {
        LivenessNode {
            defs: HashSet::new(),
            uses: HashSet::new(),
            ins: HashSet::new(),
            outs: HashSet::new(),
            succ: Vec::new(),
            pred: Vec::new(),
        }
    }

    fn node_flow(&mut self) {
        self.ins = self.uses.union(&self.outs.difference(&self.defs).cloned().collect())
                            .cloned().collect();

        self.outs = self.succ.clone().into_iter().flat_map(|s| s.ins).collect();
    }

    // TODO Given a list of AST instructions, parse out what UUIDs are used
    fn compute_uses(&mut self, statements: Vec<Statement>) {

    }

    fn compute_defs(&mut self, statements: Vec<Statement>) {

    }

}

impl LivenessGraph {
    fn new() -> LivenessGraph {
        LivenessGraph {
            nodes: Vec::new(),
        }
    }

    fn graph_flow(&mut self) {
        let mut node_queue: VecDeque<&mut LivenessNode> = self.nodes.iter_mut().collect();
        while let Some(n) = node_queue.pop_front() {
            let old_ins = n.ins.clone();
            let old_outs = n.outs.clone();
            n.node_flow();
            if old_outs != n.outs {
                n.succ.iter_mut().map(|s| node_queue.push_back(s));
            }
        }
    }
}

pub struct StackFrame<'m> {
    members: Vec<&'m JsT>,
}

impl<'m> StackFrame<'m> {
    pub fn new() -> StackFrame<'m> {
        StackFrame { members: Vec::new(), }
    }

    pub fn alloc_ref(&mut self, refce: &'m mut JsT)  {
        self.members.push(refce);
    }
}
<|endoftext|>


<commit_before>//! This module implements the logic needed to apply the proper analysis passes to simplify the IR.

use petgraph::algo::tarjan_scc;
use petgraph::Graph;

use rayon::prelude::*;

use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;

use analysis::analyzer;
use analysis::analyzer::{
    Action, AnalyzerInfo, AnalyzerKind, Change, FuncAnalyzer, ModuleAnalyzer,
};
use analysis::arithmetic::{ArithChange, Arithmetic};
use analysis::copy_propagation::CopyPropagation;
use analysis::cse::cse::CSE;
use analysis::cse::ssasort::Sorter;
use analysis::dce::DCE;
use analysis::functions::fix_ssa_opcalls::CallSiteFixer;
use analysis::functions::infer_regusage::Inferer;
use analysis::inst_combine::Combiner;
use analysis::interproc::fixcall::CallFixer;
use analysis::sccp::SCCP;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::regfile::SubRegisterFile;

fn sort_by_requires(analyzers: &Vec<AnalyzerKind>) -> impl Iterator<Item = AnalyzerKind> {
    // Build the dependency graph.
    let mut graph = Graph::<AnalyzerKind, ()>::new();
    let mut kind2id = HashMap::new();

    for analyzer in analyzers {
        let n = kind2id
            .entry(*analyzer)
            .or_insert_with(|| graph.add_node(*analyzer))
            .clone();
        let info: &'static AnalyzerInfo = From::from(*analyzer);

        for dep in info.requires {
            let d = kind2id
                .entry(*dep)
                .or_insert_with(|| graph.add_node(*dep))
                .clone();
            graph.add_edge(d, n, ());
        }
    }

    // Compute the reverse topological sorting using the Tarjan's algorithm. If SCCs are present
    // we can safely run the analyzers within the same SCC in arbitrary order because the `requires`
    // property is not garanteed to be respected.
    let sccs = tarjan_scc(&graph);

    sccs.into_iter().flatten().map(move |id| graph[id]).rev()
}

pub trait EngineResult: Any + Debug {}

pub trait Engine: Any + Debug {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>>;
    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>>;
}

/// Radeco's default engine.
#[derive(Debug)]
pub struct RadecoEngine {
    max_iteration: u32,
}

impl RadecoEngine {
    pub fn new(max_iteration: u32) -> Self {
        RadecoEngine {
            max_iteration: max_iteration,
        }
    }
}

impl Engine for RadecoEngine {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>> {
        // Analyze preserved for all functions.
        {
            let bp_name = regfile.get_name_by_alias(&"BP".to_string());
            let bp_name = bp_name.map(|s| s.to_owned());
            let sp_name = regfile.get_name_by_alias(&"SP".to_string());
            let sp_name = sp_name.map(|s| s.to_owned());
            let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
            callfixer.rounded_analysis();
        }

        // Fix call sites
        let mut call_site_fixer = CallSiteFixer::new();
        call_site_fixer.analyze(rmod, None::<fn(_) -> _>);

        // Infer calling conventions
        let mut inferer = Inferer::new((*regfile).clone());
        inferer.analyze(rmod, None::<fn(_) -> _>);

        rmod.functions.par_iter_mut().for_each(|(_, rfn)| {
            self.run_func(rfn);
        });

        None
    }

    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>> {
        // Try to convert the condition codes to relational operators. This should be done before
        // all the other passes.
        let mut arithmetic = Arithmetic::new();
        arithmetic.analyze(
            rfn,
            Some(|change: Box<Change>| {
                let change = change.as_any().downcast_ref::<ArithChange>().unwrap();
                if change.new_expr.contains("OpEq")
                    || change.new_expr.contains("OpGt")
                    || change.new_expr.contains("OpLt")
                {
                    Action::Apply
                } else {
                    Action::Skip
                }
            }),
        );

        {
            // Sort the IR.
            let mut sorter = Sorter::new(rfn.ssa_mut());
            sorter.run();
        }

        let mut analyzers = sort_by_requires(&analyzer::all_func_analyzers());

        // Run iteratively all the available analyzers until a stable point or the maximum
        // number of iterations is reached.
        for _ in 0..self.max_iteration {
            let mut stable = true;

            // Build and run the analyzers.
            while let Some(analyzer) = analyzers.next() {
                // If the policy is called then there is still something to change, thus this is
                // not a stable point.
                let policy = |_| {
                    stable = false;
                    Action::Apply
                };

                match analyzer {
                    AnalyzerKind::Arithmetic => {
                        let mut arithmetic = Arithmetic::new();
                        arithmetic.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::Combiner => {
                        let mut combiner = Combiner::new();
                        combiner.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CopyPropagation => {
                        let mut copy_propagation = CopyPropagation::new();
                        copy_propagation.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CSE => {
                        let mut cse = CSE::new();
                        cse.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::DCE => {
                        let mut dce = DCE::new();
                        dce.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::SCCP => {
                        let mut sccp = SCCP::new();
                        sccp.analyze(rfn, Some(policy));
                    }
                    _ => (),
                }
            }

            if stable {
                break;
            }
        }

        None
    }
}
<commit_msg>Add some logging to `RadecoEngine`<commit_after>//! This module implements the logic needed to apply the proper analysis passes to simplify the IR.

use petgraph::algo::tarjan_scc;
use petgraph::Graph;

use rayon::prelude::*;

use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;

use analysis::analyzer;
use analysis::analyzer::{
    Action, AnalyzerInfo, AnalyzerKind, Change, FuncAnalyzer, ModuleAnalyzer,
};
use analysis::arithmetic::{ArithChange, Arithmetic};
use analysis::copy_propagation::CopyPropagation;
use analysis::cse::cse::CSE;
use analysis::cse::ssasort::Sorter;
use analysis::dce::DCE;
use analysis::functions::fix_ssa_opcalls::CallSiteFixer;
use analysis::functions::infer_regusage::Inferer;
use analysis::inst_combine::Combiner;
use analysis::interproc::fixcall::CallFixer;
use analysis::sccp::SCCP;
use frontend::radeco_containers::{RadecoFunction, RadecoModule};
use middle::regfile::SubRegisterFile;

fn sort_by_requires(analyzers: &Vec<AnalyzerKind>) -> impl Iterator<Item = AnalyzerKind> {
    // Build the dependency graph.
    let mut graph = Graph::<AnalyzerKind, ()>::new();
    let mut kind2id = HashMap::new();

    for analyzer in analyzers {
        let n = kind2id
            .entry(*analyzer)
            .or_insert_with(|| graph.add_node(*analyzer))
            .clone();
        let info: &'static AnalyzerInfo = From::from(*analyzer);

        for dep in info.requires {
            let d = kind2id
                .entry(*dep)
                .or_insert_with(|| graph.add_node(*dep))
                .clone();
            graph.add_edge(d, n, ());
        }
    }

    // Compute the reverse topological sorting using the Tarjan's algorithm. If SCCs are present
    // we can safely run the analyzers within the same SCC in arbitrary order because the `requires`
    // property is not garanteed to be respected.
    let sccs = tarjan_scc(&graph);

    sccs.into_iter().flatten().map(move |id| graph[id]).rev()
}

pub trait EngineResult: Any + Debug {}

pub trait Engine: Any + Debug {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>>;
    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>>;
}

/// Radeco's default engine.
#[derive(Debug)]
pub struct RadecoEngine {
    max_iteration: u32,
}

impl RadecoEngine {
    pub fn new(max_iteration: u32) -> Self {
        RadecoEngine {
            max_iteration: max_iteration,
        }
    }
}

impl Engine for RadecoEngine {
    fn run_module(
        &self,
        rmod: &mut RadecoModule,
        regfile: &SubRegisterFile,
    ) -> Option<Box<EngineResult>> {
        radeco_trace!("run_module");

        // Analyze preserved for all functions.
        {
            let bp_name = regfile.get_name_by_alias(&"BP".to_string());
            let bp_name = bp_name.map(|s| s.to_owned());
            let sp_name = regfile.get_name_by_alias(&"SP".to_string());
            let sp_name = sp_name.map(|s| s.to_owned());
            let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
            callfixer.rounded_analysis();
        }

        // Fix call sites
        let mut call_site_fixer = CallSiteFixer::new();
        call_site_fixer.analyze(rmod, None::<fn(_) -> _>);

        // Infer calling conventions
        let mut inferer = Inferer::new((*regfile).clone());
        inferer.analyze(rmod, None::<fn(_) -> _>);

        rmod.functions.par_iter_mut().for_each(|(_, rfn)| {
            self.run_func(rfn);
        });

        None
    }

    fn run_func(&self, rfn: &mut RadecoFunction) -> Option<Box<EngineResult>> {
        radeco_trace!("run_func: {}", rfn.name);

        // Try to convert the condition codes to relational operators. This should be done before
        // all the other passes.
        let mut arithmetic = Arithmetic::new();
        arithmetic.analyze(
            rfn,
            Some(|change: Box<Change>| {
                let change = change.as_any().downcast_ref::<ArithChange>().unwrap();
                if change.new_expr.contains("OpEq")
                    || change.new_expr.contains("OpGt")
                    || change.new_expr.contains("OpLt")
                {
                    Action::Apply
                } else {
                    Action::Skip
                }
            }),
        );

        {
            // Sort the IR.
            let mut sorter = Sorter::new(rfn.ssa_mut());
            sorter.run();
        }

        let mut analyzers = sort_by_requires(&analyzer::all_func_analyzers());

        // Run iteratively all the available analyzers until a stable point or the maximum
        // number of iterations is reached.
        for _ in 0..self.max_iteration {
            let mut stable = true;

            // Build and run the analyzers.
            while let Some(analyzer) = analyzers.next() {
                radeco_trace!("running analyzer: {:?}", analyzer);
                // If the policy is called then there is still something to change, thus this is
                // not a stable point.
                let policy = |_| {
                    stable = false;
                    Action::Apply
                };

                match analyzer {
                    AnalyzerKind::Arithmetic => {
                        let mut arithmetic = Arithmetic::new();
                        arithmetic.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::Combiner => {
                        let mut combiner = Combiner::new();
                        combiner.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CopyPropagation => {
                        let mut copy_propagation = CopyPropagation::new();
                        copy_propagation.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::CSE => {
                        let mut cse = CSE::new();
                        cse.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::DCE => {
                        let mut dce = DCE::new();
                        dce.analyze(rfn, Some(policy));
                    }
                    AnalyzerKind::SCCP => {
                        let mut sccp = SCCP::new();
                        sccp.analyze(rfn, Some(policy));
                    }
                    _ => (),
                }
            }

            if stable {
                break;
            }
        }

        None
    }
}
<|endoftext|>