<commit_before>use std::collections::HashMap;

use common::{Result, Id, Op2, Const};
use explicit;
use implicit;
use env;
use hindley_milner;

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Imm {
    Bool(bool),
    Int(i64),
    Var(Id),
    //    Fun(Id, Box<Expr>),
    //    Fix(Id, Box<Expr>),
    //    Star,
    //    V,
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Op {
    Op2(Op2, Box<Imm>, Box<Imm>),
    //    MkArray(Box<Imm>, Box<Imm>),
    //    GetArray(Box<Imm>, Box<Imm>),
    //    SetArray(Box<Imm>, Box<Imm>, Box<Imm>),
    Imm(Imm),
}
//    WellFormed(Imm), // Var-only

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Expr {
    //    If(Box<Imm>, Box<Imm>, Box<Imm>),
    //    App(Box<Imm>, Box<Imm>),
    Let(Id, Box<Expr>, Box<Expr>),
    Op(Op),
}

#[derive(PartialEq, Eq, Clone, Debug)]
struct ConvEnv {
    //env: HashMap<Id, explicit::Type>,
    next_id: i32,
}

impl ConvEnv {
    fn new() -> ConvEnv {
        ConvEnv {
            //env: HashMap::new(),
            next_id: 0,
        }
    }

    fn tmp(&self) -> (ConvEnv, Id) {
        let id = self.next_id;
        let c = ConvEnv{
            //env: self.env.clone(),
            next_id: self.next_id+1,
        };

        (c, format!("!tmp!{}", id))
    }
}

fn op(cenv: ConvEnv, e: &explicit::Expr) -> (ConvEnv, Op) {
    use typed::Expr as E;
    match *e {
        E::Const(Const::Bool(c)) => (cenv, Op::Imm(Imm::Bool(c))),
        E::Const(Const::Int(c)) => (cenv, Op::Imm(Imm::Int(c))),
        _ => {
            panic!("TODO: implement term for {:?}", e);
        }
    }
}

fn identity(cenv: ConvEnv, e: Expr) -> (ConvEnv, Expr) {
    (cenv, e)
}

// 1:1 translation -- can't fail
fn expr<F>(cenv: ConvEnv, e: &explicit::Expr, k: F) -> (ConvEnv, Expr)
    where F: FnOnce(ConvEnv, Expr) -> (ConvEnv, Expr) {

    use self::Imm as I;
    use typed::Expr as E;

    match *e {
        E::Const(_) => {
            let (cenv, eop) = op(cenv, e);
            (cenv, Expr::Op(eop))
        }
        E::Op2(op, ref l, ref r) => {
            let (cenv, l_tmp) = cenv.tmp();
            let (cenv, r_tmp) = cenv.tmp();

            expr(cenv, l, |cenv2: ConvEnv, ll: Expr| {
                expr(cenv2, r, |cenv3: ConvEnv, rr: Expr| {

                    // value to pass to the continuation
                    let val = Expr::Op(Op::Op2(op,
                                     box I::Var(l_tmp.clone()),
                                     box I::Var(r_tmp.clone())));

                    // our inner expression is whatever the
                    // continuation says it is.
                    let (cenv4, inner_expr) = k(cenv3, val);

                    let result = Expr::Let(l_tmp.clone(), box ll,
                                           box Expr::Let(r_tmp.clone(), box rr,
                                                         box inner_expr));

                    (cenv4, result)
                })
            })
        }
        _ => {
            panic!("TODO: implement expr for {:?}", e);
        }
    }
    // E::Op2(op, ref e1, ref e2) => {
    //     let (n, e1, _) = convert(n, env, renamed, e1);
    //     let (n, e2, _) = convert(n, env, renamed, e2);
    //     (n, I::Op2(op, box e1, box e2), explicit::opty(op))
    // }
    // E::If(ref e1, ref e2, ref e3) => {
    //     let (n, e1, _) = convert(n, env, renamed, e1);
    //     let (n, e2, t) = convert(n, env, renamed, e2);
    //     let (n, e3, _) = convert(n, env, renamed, e3);
    //     (n, I::If(box e1, box e2, box e3), t)
    // }
    // E::Var(ref x) => {
    //     let αx = renamed.get(x).unwrap();
    //     (n, I::Var(αx.clone()), env.get(αx).unwrap().clone())
    // }
    // E::Let(ref id, ref e1, ref e2) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     let (n, e1, t1) = convert(n, env, &renamed, e1);

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1);

    //     let (n, e2, t2) = convert(n, env, &renamed, e2);
    //     (n, I::Let(αid, box e1, box e2), t2)
    // }
    // E::Fun(ref id, ref t1, ref e) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1.clone());

    //     let (n, e, t2) = convert(n, env, &renamed, e);

    //     (n, I::Fun(αid, box e), Type::TFun(box t1.clone(), box t2))
    // }
    // E::Fix(ref id, ref t1, ref e) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1.clone());

    //     let (n, e, t2) = convert(n, env, &renamed, e);

    //     (n, I::Fix(αid, box e), t2)
    // }
    // E::App(ref e1, ref e2) => {
    //     let (n, e1, t1) = convert(n, env, renamed, e1);
    //     let (n, e2, _) = convert(n, env, renamed, e2);
    //     let t = match t1 {
    //         Type::TFun(_, t2) => *t2,
    //         _ => panic!("expected TFun, not {:?}", t1),
    //     };
    //     (n, I::App(box e1, box e2), t)
    // }
    // E::MkArray(ref sz, ref val) => {
    //     let (n, sz, _) = convert(n, env, renamed, sz);
    //     let (n, val, _) = convert(n, env, renamed, val);
    //     (n, I::MkArray(box sz, box val), Type::TIntArray)
    // }
    // E::GetArray(ref id, ref idx) => {
    //     let (n, id, _) = convert(n, env, renamed, id);
    //     let (n, idx, _) = convert(n, env, renamed, idx);
    //     (n, I::GetArray(box id, box idx), Type::TInt)
    // }
    // E::SetArray(ref id, ref idx, ref v) => {
    //     let (n, id, _) = convert(n, env, renamed, id);
    //     let (n, idx, _) = convert(n, env, renamed, idx);
    //     let (n, v, _) = convert(n, env, renamed, v);
    //     (n, I::SetArray(box id, box idx, box v), Type::TIntArray)
    // }
}

pub fn anf(implicit_expr: &implicit::Expr) -> Result<(Expr, HashMap<Id, explicit::Type>)> {

    let explicit_expr = hindley_milner::infer(implicit_expr)?;
    // alpha-renaming
    let (alpha_expr, env) = env::extract(&explicit_expr);

    // println!("α-implicit: {:?}\n", α_expr);
    // println!("Γ         : {:?}\n", env);

    let cenv = ConvEnv::new();
    let (_, expr) = expr(cenv, &alpha_expr, identity);

    Ok((expr, env))
    // step 1 -- arithmatic + let bindings
    // step 2 -- conds + boolean vals
    // step 3 -- arrays
    // step 4 -- closures

    // (define (Value? M)
    //   (match M
    //     [`(quote ,_)         #t]
    //     [(? number?)         #t]
    //     [(? boolean?)        #t]
    //     [(? string?)         #t]
    //     [(? char?)           #t]
    //     [(? symbol?)         #t]
    //     [(or '+ '- '* '/ '=) #t]
    //     [else                #f]))

    // (define (normalize-term M) (normalize M (λ (x) x)))

    // (define (normalize M k)
    //   (match M
    //     [`(λ ,params ,body)
    //       (k `(λ ,params ,(normalize-term body)))]

    //     [`(let ([,x ,M1]) ,M2)
    //       (normalize M1 (λ (N1)
    //        `(let ([,x ,N1])
    //          ,(normalize M2 k))))]

    //     [`(if ,M1 ,M2 ,M3)
    //       (normalize-name M1 (λ (t)
    //        (k `(if ,t ,(normalize-term M2)
    //                   ,(normalize-term M3)))))]

    //     [`(,Fn . ,M*)
    //       (normalize-name Fn (λ (t)
    //        (normalize-name* M* (λ (t*)
    //         (k `(,t . ,t*))))))]

    //     [(? Value?)             (k M)]))

    // (define (normalize-name M k)
    //   (normalize M (λ (N)
    //     (if (Value? N) (k N)
    //         (let ([t (gensym)])
    //          `(let ([,t ,N]) ,(k t)))))))

    // (define (normalize-name* M* k)
    //   (if (null? M*)
    //       (k '())
    //       (normalize-name (car M*) (λ (t)
    //        (normalize-name* (cdr M*) (λ (t*)
    //         (k `(,t . ,t*))))))))

}

macro_rules! test_anf(
    ($s:expr, $ae:expr) => { {
        use implicit_parse;
        use tok::Tokenizer;
        use std;
        let s = $s;
        let tokenizer = Tokenizer::new(&s);
        let iexpr = match implicit_parse::parse_Program(&s, tokenizer) {
            Ok(iexpr) => iexpr,
            Err(e) => {
                die!("parse_Program({}): {:?}", $s, e);
            }
        };
        let anf_expr = match anf(&iexpr) {
            Ok((anf_expr, _)) => anf_expr,
            Err(e) => {
                die!("anf: {:?}", e);
            }
        };
        if anf_expr != $ae {
            die!("mismatch {:?} != {:?}", anf_expr, $ae);
        }
    } }
);

#[test]
fn anf_transforms() {
    use common::Op2 as O;
    use self::Imm as I;
    use self::Op::*;
    use self::Expr::*;

    test_anf!(
        "-22",
        Op(Imm(I::Int(-22))));
    test_anf!(
        "2+3",
        Let(String::from("!tmp!0"),
            box Op(Op2(O::Add, box I::Int(2), box I::Int(3))),
            box Op(Imm(I::Var(String::from("!tmp!0"))))));
}
<commit_msg>more correct op2<commit_after>use std::collections::HashMap;

use common::{Result, Id, Op2, Const};
use explicit;
use implicit;
use env;
use hindley_milner;

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Imm {
    Bool(bool),
    Int(i64),
    Var(Id),
    //    Fun(Id, Box<Expr>),
    //    Fix(Id, Box<Expr>),
    //    Star,
    //    V,
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Op {
    Op2(Op2, Box<Imm>, Box<Imm>),
    //    MkArray(Box<Imm>, Box<Imm>),
    //    GetArray(Box<Imm>, Box<Imm>),
    //    SetArray(Box<Imm>, Box<Imm>, Box<Imm>),
    Imm(Imm),
}
//    WellFormed(Imm), // Var-only

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Expr {
    //    If(Box<Imm>, Box<Imm>, Box<Imm>),
    //    App(Box<Imm>, Box<Imm>),
    Let(Id, Box<Expr>, Box<Expr>),
    Op(Op),
}

#[derive(PartialEq, Eq, Clone, Debug)]
struct ConvEnv {
    //env: HashMap<Id, explicit::Type>,
    next_id: i32,
}

impl ConvEnv {
    fn new() -> ConvEnv {
        ConvEnv {
            //env: HashMap::new(),
            next_id: 0,
        }
    }

    fn tmp(&self) -> (ConvEnv, Id) {
        let id = self.next_id;
        let c = ConvEnv{
            //env: self.env.clone(),
            next_id: self.next_id+1,
        };

        (c, format!("!tmp!{}", id))
    }
}

fn op(cenv: ConvEnv, e: &explicit::Expr) -> (ConvEnv, Op) {
    use typed::Expr as E;
    match *e {
        E::Const(Const::Bool(c)) => (cenv, Op::Imm(Imm::Bool(c))),
        E::Const(Const::Int(c)) => (cenv, Op::Imm(Imm::Int(c))),
        _ => {
            panic!("TODO: implement term for {:?}", e);
        }
    }
}

fn identity(cenv: ConvEnv, e: Expr) -> (ConvEnv, Expr) {
    (cenv, e)
}

// 1:1 translation -- can't fail
fn expr<F>(cenv: ConvEnv, e: &explicit::Expr, k: F) -> (ConvEnv, Expr)
    where F: FnOnce(ConvEnv, Expr) -> (ConvEnv, Expr) {

    use self::Imm as I;
    use typed::Expr as E;

    match *e {
        E::Const(_) => {
            let (cenv, eop) = op(cenv, e);
            (cenv, Expr::Op(eop))
        }
        E::Op2(op, ref l, ref r) => {
            expr(cenv, l, |cenv: ConvEnv, ll: Expr| -> (ConvEnv, Expr) {
                expr(cenv, r, |cenv: ConvEnv, rr: Expr| -> (ConvEnv, Expr) {
                    // allocate vars
                    let (cenv, l_tmp) = cenv.tmp();
                    let (cenv, r_tmp) = cenv.tmp();

                    // value to pass to the continuation
                    let val = Expr::Op(Op::Op2(op,
                                               box I::Var(l_tmp.clone()),
                                               box I::Var(r_tmp.clone())));

                    // our inner expression is whatever the
                    // continuation says it is.
                    let (cenv, inner_expr) = k(cenv, val);

                    let result = Expr::Let(l_tmp.clone(), box ll.clone(),
                                           box Expr::Let(r_tmp.clone(), box rr,
                                                         box inner_expr));

                    (cenv, result)
                })
            })
        }
        _ => {
            panic!("TODO: implement expr for {:?}", e);
        }
    }
    // E::Op2(op, ref e1, ref e2) => {
    //     let (n, e1, _) = convert(n, env, renamed, e1);
    //     let (n, e2, _) = convert(n, env, renamed, e2);
    //     (n, I::Op2(op, box e1, box e2), explicit::opty(op))
    // }
    // E::If(ref e1, ref e2, ref e3) => {
    //     let (n, e1, _) = convert(n, env, renamed, e1);
    //     let (n, e2, t) = convert(n, env, renamed, e2);
    //     let (n, e3, _) = convert(n, env, renamed, e3);
    //     (n, I::If(box e1, box e2, box e3), t)
    // }
    // E::Var(ref x) => {
    //     let αx = renamed.get(x).unwrap();
    //     (n, I::Var(αx.clone()), env.get(αx).unwrap().clone())
    // }
    // E::Let(ref id, ref e1, ref e2) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     let (n, e1, t1) = convert(n, env, &renamed, e1);

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1);

    //     let (n, e2, t2) = convert(n, env, &renamed, e2);
    //     (n, I::Let(αid, box e1, box e2), t2)
    // }
    // E::Fun(ref id, ref t1, ref e) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1.clone());

    //     let (n, e, t2) = convert(n, env, &renamed, e);

    //     (n, I::Fun(αid, box e), Type::TFun(box t1.clone(), box t2))
    // }
    // E::Fix(ref id, ref t1, ref e) => {
    //     let mut renamed = renamed.clone();
    //     let (n, αid) = (n+1, format!("{}!a{}", id, n));

    //     renamed.insert(id.clone(), αid.clone());
    //     env.insert(αid.clone(), t1.clone());

    //     let (n, e, t2) = convert(n, env, &renamed, e);

    //     (n, I::Fix(αid, box e), t2)
    // }
    // E::App(ref e1, ref e2) => {
    //     let (n, e1, t1) = convert(n, env, renamed, e1);
    //     let (n, e2, _) = convert(n, env, renamed, e2);
    //     let t = match t1 {
    //         Type::TFun(_, t2) => *t2,
    //         _ => panic!("expected TFun, not {:?}", t1),
    //     };
    //     (n, I::App(box e1, box e2), t)
    // }
    // E::MkArray(ref sz, ref val) => {
    //     let (n, sz, _) = convert(n, env, renamed, sz);
    //     let (n, val, _) = convert(n, env, renamed, val);
    //     (n, I::MkArray(box sz, box val), Type::TIntArray)
    // }
    // E::GetArray(ref id, ref idx) => {
    //     let (n, id, _) = convert(n, env, renamed, id);
    //     let (n, idx, _) = convert(n, env, renamed, idx);
    //     (n, I::GetArray(box id, box idx), Type::TInt)
    // }
    // E::SetArray(ref id, ref idx, ref v) => {
    //     let (n, id, _) = convert(n, env, renamed, id);
    //     let (n, idx, _) = convert(n, env, renamed, idx);
    //     let (n, v, _) = convert(n, env, renamed, v);
    //     (n, I::SetArray(box id, box idx, box v), Type::TIntArray)
    // }
}

pub fn anf(implicit_expr: &implicit::Expr) -> Result<(Expr, HashMap<Id, explicit::Type>)> {

    let explicit_expr = hindley_milner::infer(implicit_expr)?;
    // alpha-renaming
    let (alpha_expr, env) = env::extract(&explicit_expr);

    // println!("α-implicit: {:?}\n", α_expr);
    // println!("Γ         : {:?}\n", env);

    let cenv = ConvEnv::new();
    let (_, expr) = expr(cenv, &alpha_expr, identity);

    Ok((expr, env))
    // step 1 -- arithmatic + let bindings
    // step 2 -- conds + boolean vals
    // step 3 -- arrays
    // step 4 -- closures

    // (define (Value? M)
    //   (match M
    //     [`(quote ,_)         #t]
    //     [(? number?)         #t]
    //     [(? boolean?)        #t]
    //     [(? string?)         #t]
    //     [(? char?)           #t]
    //     [(? symbol?)         #t]
    //     [(or '+ '- '* '/ '=) #t]
    //     [else                #f]))

    // (define (normalize-term M) (normalize M (λ (x) x)))

    // (define (normalize M k)
    //   (match M
    //     [`(λ ,params ,body)
    //       (k `(λ ,params ,(normalize-term body)))]

    //     [`(let ([,x ,M1]) ,M2)
    //       (normalize M1 (λ (N1)
    //        `(let ([,x ,N1])
    //          ,(normalize M2 k))))]

    //     [`(if ,M1 ,M2 ,M3)
    //       (normalize-name M1 (λ (t)
    //        (k `(if ,t ,(normalize-term M2)
    //                   ,(normalize-term M3)))))]

    //     [`(,Fn . ,M*)
    //       (normalize-name Fn (λ (t)
    //        (normalize-name* M* (λ (t*)
    //         (k `(,t . ,t*))))))]

    //     [(? Value?)             (k M)]))

    // (define (normalize-name M k)
    //   (normalize M (λ (N)
    //     (if (Value? N) (k N)
    //         (let ([t (gensym)])
    //          `(let ([,t ,N]) ,(k t)))))))

    // (define (normalize-name* M* k)
    //   (if (null? M*)
    //       (k '())
    //       (normalize-name (car M*) (λ (t)
    //        (normalize-name* (cdr M*) (λ (t*)
    //         (k `(,t . ,t*))))))))

}

macro_rules! test_anf(
    ($s:expr, $ae:expr) => { {
        use implicit_parse;
        use tok::Tokenizer;
        use std;
        let s = $s;
        let tokenizer = Tokenizer::new(&s);
        let iexpr = match implicit_parse::parse_Program(&s, tokenizer) {
            Ok(iexpr) => iexpr,
            Err(e) => {
                die!("parse_Program({}): {:?}", $s, e);
            }
        };
        let anf_expr = match anf(&iexpr) {
            Ok((anf_expr, _)) => anf_expr,
            Err(e) => {
                die!("anf: {:?}", e);
            }
        };
        if anf_expr != $ae {
            die!("mismatch {:?} != {:?}", anf_expr, $ae);
        }
    } }
);

#[test]
fn anf_transforms() {
    use common::Op2 as O;
    use self::Imm as I;
    use self::Op::*;
    use self::Expr::*;

    test_anf!(
        "-22",
        Op(Imm(I::Int(-22))));
    test_anf!(
        "2+3",
        Let(String::from("!tmp!0"),
            box Op(Op2(O::Add, box I::Int(2), box I::Int(3))),
            box Op(Imm(I::Var(String::from("!tmp!0"))))));
}
<|endoftext|>


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

//! Servo, the mighty web browser engine from the future.
//!
//! This is a very simple library that wires all of Servo's components
//! together as type `Browser`, along with a generic client
//! implementing the `WindowMethods` trait, to create a working web
//! browser.
//!
//! The `Browser` type is responsible for configuring a
//! `Constellation`, which does the heavy lifting of coordinating all
//! of Servo's internal subsystems, including the `ScriptThread` and the
//! `LayoutThread`, as well maintains the navigation context.
//!
//! The `Browser` is fed events from a generic type that implements the
//! `WindowMethods` trait.

extern crate gaol;
#[macro_use]
extern crate util as _util;

mod export {
    extern crate canvas;
    extern crate canvas_traits;
    extern crate compositing;
    extern crate devtools;
    extern crate devtools_traits;
    extern crate euclid;
    extern crate gfx;
    extern crate gleam;
    extern crate ipc_channel;
    extern crate layers;
    extern crate layout;
    extern crate msg;
    extern crate net;
    extern crate net_traits;
    extern crate profile;
    extern crate profile_traits;
    extern crate script;
    extern crate script_traits;
    extern crate style;
    extern crate url;
}

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use compositing::CompositorEventListener;
use compositing::CompositorMsg as ConstellationMsg;
use compositing::compositor_thread::InitialCompositorState;
use compositing::constellation::InitialConstellationState;
use compositing::pipeline::UnprivilegedPipelineContent;
use compositing::sandboxing;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use compositing::{CompositorProxy, CompositorThread, Constellation};
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcSender};
use net::image_cache_thread::new_image_cache_thread;
use net::resource_thread::new_resource_thread;
use net::storage_thread::StorageThreadFactory;
use net_traits::storage_thread::StorageThread;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use std::borrow::Borrow;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use util::opts;

pub use _util as util;
pub use export::canvas;
pub use export::canvas_traits;
pub use export::compositing;
pub use export::devtools;
pub use export::devtools_traits;
pub use export::euclid;
pub use export::gfx;
pub use export::gleam::gl;
pub use export::ipc_channel;
pub use export::layers;
pub use export::layout;
pub use export::msg;
pub use export::net;
pub use export::net_traits;
pub use export::profile;
pub use export::profile_traits;
pub use export::script;
pub use export::script_traits;
pub use export::style;
pub use export::url;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Browser {
    compositor: Box<CompositorEventListener + 'static>,
}

impl Browser {
    pub fn new<Window>(window: Option<Rc<Window>>) -> Browser
                       where Window: WindowMethods + 'static {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        script::init();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            WindowMethods::create_compositor_channel(&window);
        let supports_clipboard = match window {
            Some(ref win_rc) => {
                let win: &Window = win_rc.borrow();
                win.supports_clipboard()
            }
            None => false
        };
        let time_profiler_chan = profile_time::Profiler::create(opts.time_profiler_period);
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let constellation_chan = create_constellation(opts.clone(),
                                                      compositor_proxy.clone_compositor_proxy(),
                                                      time_profiler_chan.clone(),
                                                      mem_profiler_chan.clone(),
                                                      devtools_chan,
                                                      supports_clipboard);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = CompositorThread::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
        });

        Browser {
            compositor: compositor,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }
}

fn create_constellation(opts: opts::Opts,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool) -> Sender<ConstellationMsg> {
    let resource_thread = new_resource_thread(opts.user_agent.clone(), devtools_chan.clone());

    let image_cache_thread = new_image_cache_thread(resource_thread.clone());
    let font_cache_thread = FontCacheThread::new(resource_thread.clone());
    let storage_thread: StorageThread = StorageThreadFactory::new();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        devtools_chan: devtools_chan,
        image_cache_thread: image_cache_thread,
        font_cache_thread: font_cache_thread,
        resource_thread: resource_thread,
        storage_thread: storage_thread,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
    };
    let constellation_chan =
        Constellation::<layout::layout_thread::LayoutThread,
                        script::script_thread::ScriptThread>::start(initial_state);

    // Send the URL command to the constellation.
    match opts.url {
        Some(url) => {
            constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
        },
        None => ()
    };

    constellation_chan
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
        ChildSandbox::new(sandboxing::content_process_sandbox_profile()).activate().unwrap();
    }

    script::init();

    unprivileged_content.start_all::<layout::layout_thread::LayoutThread,
                                     script::script_thread::ScriptThread>(true);
}

<commit_msg>Workaround to land the rustup while we wait for rust-errno<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Servo, the mighty web browser engine from the future.
//!
//! This is a very simple library that wires all of Servo's components
//! together as type `Browser`, along with a generic client
//! implementing the `WindowMethods` trait, to create a working web
//! browser.
//!
//! The `Browser` type is responsible for configuring a
//! `Constellation`, which does the heavy lifting of coordinating all
//! of Servo's internal subsystems, including the `ScriptThread` and the
//! `LayoutThread`, as well maintains the navigation context.
//!
//! The `Browser` is fed events from a generic type that implements the
//! `WindowMethods` trait.

extern crate gaol;
#[macro_use]
extern crate util as _util;

mod export {
    extern crate canvas;
    extern crate canvas_traits;
    extern crate compositing;
    extern crate devtools;
    extern crate devtools_traits;
    extern crate euclid;
    extern crate gfx;
    extern crate gleam;
    extern crate ipc_channel;
    extern crate layers;
    extern crate layout;
    extern crate msg;
    extern crate net;
    extern crate net_traits;
    extern crate profile;
    extern crate profile_traits;
    extern crate script;
    extern crate script_traits;
    extern crate style;
    extern crate url;
}

#[cfg(feature = "webdriver")]
extern crate webdriver_server;

#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
    webdriver_server::start_server(port, constellation);
}

#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) { }

use compositing::CompositorEventListener;
use compositing::CompositorMsg as ConstellationMsg;
use compositing::compositor_thread::InitialCompositorState;
use compositing::constellation::InitialConstellationState;
use compositing::pipeline::UnprivilegedPipelineContent;
use compositing::sandboxing;
use compositing::windowing::WindowEvent;
use compositing::windowing::WindowMethods;
use compositing::{CompositorProxy, CompositorThread, Constellation};
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
use ipc_channel::ipc::{self, IpcSender};
use net::image_cache_thread::new_image_cache_thread;
use net::resource_thread::new_resource_thread;
use net::storage_thread::StorageThreadFactory;
use net_traits::storage_thread::StorageThread;
use profile::mem as profile_mem;
use profile::time as profile_time;
use profile_traits::mem;
use profile_traits::time;
use std::borrow::Borrow;
use std::rc::Rc;
use std::sync::mpsc::Sender;
use util::opts;

pub use _util as util;
pub use export::canvas;
pub use export::canvas_traits;
pub use export::compositing;
pub use export::devtools;
pub use export::devtools_traits;
pub use export::euclid;
pub use export::gfx;
pub use export::gleam::gl;
pub use export::ipc_channel;
pub use export::layers;
pub use export::layout;
pub use export::msg;
pub use export::net;
pub use export::net_traits;
pub use export::profile;
pub use export::profile_traits;
pub use export::script;
pub use export::script_traits;
pub use export::style;
pub use export::url;

/// The in-process interface to Servo.
///
/// It does everything necessary to render the web, primarily
/// orchestrating the interaction between JavaScript, CSS layout,
/// rendering, and the client window.
///
/// Clients create a `Browser` for a given reference-counted type
/// implementing `WindowMethods`, which is the bridge to whatever
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Browser {
    compositor: Box<CompositorEventListener + 'static>,
}

impl Browser {
    pub fn new<Window>(window: Option<Rc<Window>>) -> Browser
                       where Window: WindowMethods + 'static {
        // Global configuration options, parsed from the command line.
        let opts = opts::get();

        script::init();

        // Get both endpoints of a special channel for communication between
        // the client window and the compositor. This channel is unique because
        // messages to client may need to pump a platform-specific event loop
        // to deliver the message.
        let (compositor_proxy, compositor_receiver) =
            WindowMethods::create_compositor_channel(&window);
        let supports_clipboard = match window {
            Some(ref win_rc) => {
                let win: &Window = win_rc.borrow();
                win.supports_clipboard()
            }
            None => false
        };
        let time_profiler_chan = profile_time::Profiler::create(opts.time_profiler_period);
        let mem_profiler_chan = profile_mem::Profiler::create(opts.mem_profiler_period);
        let devtools_chan = opts.devtools_port.map(|port| {
            devtools::start_server(port)
        });

        // Create the constellation, which maintains the engine
        // pipelines, including the script and layout threads, as well
        // as the navigation context.
        let constellation_chan = create_constellation(opts.clone(),
                                                      compositor_proxy.clone_compositor_proxy(),
                                                      time_profiler_chan.clone(),
                                                      mem_profiler_chan.clone(),
                                                      devtools_chan,
                                                      supports_clipboard);

        if cfg!(feature = "webdriver") {
            if let Some(port) = opts.webdriver_port {
                webdriver(port, constellation_chan.clone());
            }
        }

        // The compositor coordinates with the client window to create the final
        // rendered page and display it somewhere.
        let compositor = CompositorThread::create(window, InitialCompositorState {
            sender: compositor_proxy,
            receiver: compositor_receiver,
            constellation_chan: constellation_chan,
            time_profiler_chan: time_profiler_chan,
            mem_profiler_chan: mem_profiler_chan,
        });

        Browser {
            compositor: compositor,
        }
    }

    pub fn handle_events(&mut self, events: Vec<WindowEvent>) -> bool {
        self.compositor.handle_events(events)
    }

    pub fn repaint_synchronously(&mut self) {
        self.compositor.repaint_synchronously()
    }

    pub fn pinch_zoom_level(&self) -> f32 {
        self.compositor.pinch_zoom_level()
    }

    pub fn request_title_for_main_frame(&self) {
        self.compositor.title_for_main_frame()
    }
}

fn create_constellation(opts: opts::Opts,
                        compositor_proxy: Box<CompositorProxy + Send>,
                        time_profiler_chan: time::ProfilerChan,
                        mem_profiler_chan: mem::ProfilerChan,
                        devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                        supports_clipboard: bool) -> Sender<ConstellationMsg> {
    let resource_thread = new_resource_thread(opts.user_agent.clone(), devtools_chan.clone());

    let image_cache_thread = new_image_cache_thread(resource_thread.clone());
    let font_cache_thread = FontCacheThread::new(resource_thread.clone());
    let storage_thread: StorageThread = StorageThreadFactory::new();

    let initial_state = InitialConstellationState {
        compositor_proxy: compositor_proxy,
        devtools_chan: devtools_chan,
        image_cache_thread: image_cache_thread,
        font_cache_thread: font_cache_thread,
        resource_thread: resource_thread,
        storage_thread: storage_thread,
        time_profiler_chan: time_profiler_chan,
        mem_profiler_chan: mem_profiler_chan,
        supports_clipboard: supports_clipboard,
    };
    let constellation_chan =
        Constellation::<layout::layout_thread::LayoutThread,
                        script::script_thread::ScriptThread>::start(initial_state);

    // Send the URL command to the constellation.
    match opts.url {
        Some(url) => {
            constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
        },
        None => ()
    };

    constellation_chan
}

/// Content process entry point.
pub fn run_content_process(token: String) {
    let (unprivileged_content_sender, unprivileged_content_receiver) =
        ipc::channel::<UnprivilegedPipelineContent>().unwrap();
    let connection_bootstrap: IpcSender<IpcSender<UnprivilegedPipelineContent>> =
        IpcSender::connect(token).unwrap();
    connection_bootstrap.send(unprivileged_content_sender).unwrap();

    let unprivileged_content = unprivileged_content_receiver.recv().unwrap();
    opts::set_defaults(unprivileged_content.opts());

    // Enter the sandbox if necessary.
    if opts::get().sandbox {
        ChildSandbox::new(sandboxing::content_process_sandbox_profile()).activate().unwrap();
    }

    script::init();

    unprivileged_content.start_all::<layout::layout_thread::LayoutThread,
                                     script::script_thread::ScriptThread>(true);
}

// This is a workaround for https://github.com/rust-lang/rust/pull/30175 until
// https://github.com/lfairy/rust-errno/pull/5 lands, and should be removed once
// we update Servo with the rust-errno crate.
#[cfg(target_os = "android")]
#[no_mangle]
pub unsafe extern fn __errno_location() -> *mut i32 {
    extern { fn __errno() -> *mut i32; }
    __errno()
}
<|endoftext|>


<commit_before>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */

//! Logging configuration
//!
//! This module provides a `Logger` struct which can be used for logging
//! messages from Cymbalum. One and only one of the initilialization function
//! should be called, and it should only be called once. Any call to
//! initializing function after the first one will be ignored.
//!
//! Other [log](https://github.com/rust-lang/log) compatible crates can be used
//! for logging if you whant.

use std::io;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::path::Path;
use std::sync::{Arc, Mutex};

extern crate log;
use log::{Log, LogRecord, LogMetadata, set_logger};
pub use log::LogLevel;

/// Logger with capacity to write to the standard output stream, the standard
/// error stream or a file.
 pub struct Logger {
     level: LogLevel,
     writer: Arc<Mutex<Box<Write + Send + Sync>>>,
 }

 impl Logger{
     fn new<T>(level: LogLevel, handle: T) -> Logger where T: Write + Send + Sync + 'static {
         Logger{
             level: level,
             writer: Arc::new(Mutex::new(Box::new(handle))),
         }
     }

     fn init(logger: Logger) {
         let res = set_logger(|max_log_level| {
             max_log_level.set(logger.level.to_log_level_filter());
             Box::new(logger)
         });

         if let Err(_) = res {
             warn!("Logger was initialized more than once! This call is ignored.");
         }
     }

     /// Initialize the global logger to use the standard output stream.
     pub fn stdout() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard output stream, with
     /// the maximum log level of `level`
     pub fn stdout_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream.
     pub fn stderr() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream, with
     /// the maximum log level of `level`
     pub fn stderr_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed.
     pub fn file<P: AsRef<Path>>(path: P) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(levels::DEFAULT_LEVEL, file);
         Logger::init(logger);
         Ok(())
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed. The
     /// maximum log level is set to `level`.
     pub fn file_at_level<P: AsRef<Path>>(path: P, level: LogLevel) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(level, file);
         Logger::init(logger);
         Ok(())
     }
 }

#[cfg(not(debug_assertions))]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
 }

#[cfg(debug_assertions)]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Debug;
 }

/// Implements Log trait for Logger
impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let mut out = self.writer.lock().ok().expect("Could not lock the logger.");
        let write_res = write!(
            &mut out, "{}: {} at {}:{}",
            record.level(), record.args(),
            record.location().file(), record.location().line()
        );

        if let Err(err) = write_res {
            let error_res = write!(& mut io::stderr(),
                "Error while writing log: {:?}\nMessage was: {} at {}:{} ({})",
                err, record.args(),
                record.location().file(), record.location().line(),
                record.level()
            );
            if let Err(err2) = error_res {
                panic!(format!(
                    "Error while logging logger error, aborting. Error chain: {:?} -> {:?} from {}:{}",
                    err, err2,
                    record.location().file(), record.location().line(),
                ))
            }
        }
    }
}
<commit_msg>Add a new line in log reports<commit_after>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */

//! Logging configuration
//!
//! This module provides a `Logger` struct which can be used for logging
//! messages from Cymbalum. One and only one of the initilialization function
//! should be called, and it should only be called once. Any call to
//! initializing function after the first one will be ignored.
//!
//! Other [log](https://github.com/rust-lang/log) compatible crates can be used
//! for logging if you whant.

use std::io;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::path::Path;
use std::sync::{Arc, Mutex};

extern crate log;
use log::{Log, LogRecord, LogMetadata, set_logger};
pub use log::LogLevel;

/// Logger with capacity to write to the standard output stream, the standard
/// error stream or a file.
 pub struct Logger {
     level: LogLevel,
     writer: Arc<Mutex<Box<Write + Send + Sync>>>,
 }

 impl Logger{
     fn new<T>(level: LogLevel, handle: T) -> Logger where T: Write + Send + Sync + 'static {
         Logger{
             level: level,
             writer: Arc::new(Mutex::new(Box::new(handle))),
         }
     }

     fn init(logger: Logger) {
         let res = set_logger(|max_log_level| {
             max_log_level.set(logger.level.to_log_level_filter());
             Box::new(logger)
         });

         if let Err(_) = res {
             warn!("Logger was initialized more than once! This call is ignored.");
         }
     }

     /// Initialize the global logger to use the standard output stream.
     pub fn stdout() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard output stream, with
     /// the maximum log level of `level`
     pub fn stdout_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream.
     pub fn stderr() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream, with
     /// the maximum log level of `level`
     pub fn stderr_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed.
     pub fn file<P: AsRef<Path>>(path: P) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(levels::DEFAULT_LEVEL, file);
         Logger::init(logger);
         Ok(())
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed. The
     /// maximum log level is set to `level`.
     pub fn file_at_level<P: AsRef<Path>>(path: P, level: LogLevel) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(level, file);
         Logger::init(logger);
         Ok(())
     }
 }

#[cfg(not(debug_assertions))]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
 }

#[cfg(debug_assertions)]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Debug;
 }

/// Implements Log trait for Logger
impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let mut out = self.writer.lock().ok().expect("Could not lock the logger.");
        let write_res = write!(
            &mut out, "{}: {} at {}:{}\n",
            record.level(), record.args(),
            record.location().file(), record.location().line()
        );

        if let Err(err) = write_res {
            let error_res = write!(& mut io::stderr(),
                "Error while writing log: {:?}\nMessage was: {} at {}:{} ({})\n",
                err, record.args(),
                record.location().file(), record.location().line(),
                record.level()
            );
            if let Err(err2) = error_res {
                panic!(format!(
                    "Error while logging logger error, aborting. Error chain: {:?} -> {:?} from {}:{}",
                    err, err2,
                    record.location().file(), record.location().line(),
                ))
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError};
use hlua::any::AnyLuaValue;

use std::thread;

use std::fs::{File};
use std::path::Path;
use std::io::Write;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };

    /// Receives data back from the lua thread
    /// This should only be accessed by the lua thread itself.
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
    /// Unused send type
    Unused,
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        trace!("thread: Inside thread!");
        let sender = answer_tx;
        let receiver = query_rx;
        let mut lua = Lua::new();
        trace!("thread: Loading libraries...");
        lua.openlibs();
        trace!("thread: Libraries loaded");
        trace!("thread: Testing hello world...");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        trace!("thread: Done!");
        trace!("thread: Entering loop...");
        thread_main_loop(sender, receiver, &mut lua);
    });
    trace!("Created thread. Init finished.");
}

fn thread_main_loop(sender: Sender<LuaResponse>, receiver: Receiver<LuaQuery>,
                    lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(&sender, message, lua);
            }
        }
    }
}

fn thread_handle_message(sender: &Sender<LuaResponse>, request: LuaQuery, lua: &mut Lua) {
    match request {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");

            info!("thread: Lua thread terminating!");
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");

            error!("thread: Lua thread restart not supported!");
            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {:?}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    let response = LuaResponse::Error(error);

                    thread_send(&sender, response);
                }
                Ok(_) => {
                    // This is gonna be really spammy one day
                    trace!("thread: Code executed okay.");
                }
            }
        },

        LuaQuery::ExecuteFile(name) => {
            trace!("thread: Received request to execute file {}", name);
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(&sender, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(&sender, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetVariable(varname) => {
            trace!("thread: Received request to get variable {}", varname);
            let var_result = lua.get(varname.as_str());

            match var_result {
                Some(var) => {
                    thread_send(&sender, LuaResponse::Variable(Some(var)));
                }
                None => {
                    warn!("thread: Unable to get variable {}", varname);

                    thread_send(&sender, LuaResponse::Variable(None));
                }
            }
        },

        LuaQuery::SetValue { name: name, val: val } => {

        },

        LuaQuery::EmptyArray(name) => {

        },

        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: &Sender<LuaResponse>, response: LuaResponse)
               -> Result<(), String> {
    trace!("Called thread_send");
    match sender.send(response) {
        Ok(()) => Ok(()),
        Err(e) => {
            error!("thread: Unable to broadcast response!");
            error!("thread: Shutting down in response to inability to continue!");
            panic!("Lua thread unable to communicate with main thread, shutting down!");
        }
    }
}
<commit_msg>Add basic lua::send function for sending LuaQueries<commit_after>//! Lua functionality

use hlua;
use hlua::{Lua, LuaError};
use hlua::any::AnyLuaValue;

use std::thread;

use std::fs::{File};
use std::path::Path;
use std::io::Write;

use std::sync::{Mutex, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

lazy_static! {
    /// Sends requests to the lua thread
    static ref SENDER: Mutex<Sender<LuaQuery>> = {
        let (tx, rx) = channel::<LuaQuery>();
        Mutex::new(tx)
    };

    /// Receives data back from the lua thread
    /// This should only be accessed by the lua thread itself.
    static ref RECEIVER: Mutex<Receiver<LuaResponse>> = {
        let (tx, rx) = channel::<LuaResponse>();
        Mutex::new(rx)
    };

    /// Whether the lua thread is currently running
    pub static ref RUNNING: RwLock<bool> = RwLock::new(false);
}

/// Messages sent to the lua thread
pub enum LuaQuery {
    /// Halt the lua thread
    Terminate,
    // Restart the lua thread
    Restart,
    /// Execute a string
    Execute(String),
    /// Execute a file
    ExecuteFile(String),
    /// Get a variable
    GetVariable(String),
    /// Set a value
    SetValue {
        name: Box<::std::borrow::Borrow<str> + Sized>,
        val: Box<hlua::Push<&'static mut Lua<'static>> + Sized>
    },
    /// Create a new array
    EmptyArray(String),
    /// Unused send type
    Unused,
}

/// Messages received from lua thread
pub enum LuaResponse {
    /// Lua variable obtained
    Variable(Option<AnyLuaValue>),
    /// Lua error
    Error(hlua::LuaError),
    /// A function is returned
    Function(hlua::functions_read::LuaFunction<String>)
}

unsafe impl Send for LuaQuery { }
unsafe impl Send for LuaResponse { }
unsafe impl Sync for LuaQuery { }
unsafe impl Sync for LuaResponse { }

/// Send a value to the lua thread
pub fn send(query: LuaQuery) {
    SENDER.lock().unwrap().send(query).unwrap();
}

/// Initialize the lua thread
pub fn init() {
    trace!("Initializing...");
    let (query_tx, query_rx) = channel::<LuaQuery>();
    let (answer_tx, answer_rx) = channel::<LuaResponse>();
    {
        let mut sender = SENDER.lock().unwrap();
        let mut receiver = RECEIVER.lock().unwrap();

        *sender = query_tx;
        *receiver = answer_rx;
    }

    thread::spawn(move || {
        trace!("thread: Inside thread!");
        let sender = answer_tx;
        let receiver = query_rx;
        let mut lua = Lua::new();
        trace!("thread: Loading libraries...");
        lua.openlibs();
        trace!("thread: Libraries loaded");
        trace!("thread: Testing hello world...");
        let mut file = File::create("/tmp/init.lua").unwrap();
        file.write(b"print('Hello world!')").unwrap();
        lua.execute_from_reader::<(), File>(File::open("/tmp/init.lua")
                                            .unwrap()).unwrap();
        trace!("thread: Done!");
        trace!("thread: Entering loop...");
        thread_main_loop(sender, receiver, &mut lua);
    });
    trace!("Created thread. Init finished.");
}

fn thread_main_loop(sender: Sender<LuaResponse>, receiver: Receiver<LuaQuery>,
                    lua: &mut Lua) {
    loop {
        let request = receiver.recv();
        match request {
            Err(e) => {
                error!("Lua thread: unable to receive message: {}", e);
                error!("Lua thread: now panicking!");

                panic!("Lua thread: lost contact with host, exiting!");
            }
            Ok(message) => {
                trace!("Handling a request");
                thread_handle_message(&sender, message, lua);
            }
        }
    }
}

fn thread_handle_message(sender: &Sender<LuaResponse>, request: LuaQuery, lua: &mut Lua) {
    match request {
        LuaQuery::Terminate => {
            trace!("thread: Received terminate signal");

            info!("thread: Lua thread terminating!");
            return;
        },

        LuaQuery::Restart => {
            trace!("thread: Received restart signal!");

            error!("thread: Lua thread restart not supported!");
            panic!("Lua thread: Restart not supported!");
        },

        LuaQuery::Execute(code) => {
            trace!("thread: Received request to execute code");
            trace!("thread: Executing {:?}", code);

            match lua.execute::<()>(&code) {
                Err(error) => {
                    warn!("thread: Error executing code: {:?}", error);
                    let response = LuaResponse::Error(error);

                    thread_send(&sender, response);
                }
                Ok(_) => {
                    // This is gonna be really spammy one day
                    trace!("thread: Code executed okay.");
                }
            }
        },

        LuaQuery::ExecuteFile(name) => {
            trace!("thread: Received request to execute file {}", name);
            info!("thread: Executing {}", name);

            let path = Path::new(&name);
            let try_file = File::open(path);

            if let Ok(file) = try_file {
                let result = lua.execute_from_reader::<(), File>(file);
                if let Err(err) = result {
                    warn!("thread: Error executing {}!", name);

                    thread_send(&sender, LuaResponse::Error(err));
                }
                else {
                    trace!("thread: Execution of {} successful.", name);
                }
            }
            else { // Could not open file
                // Unwrap_err is used because we're in the else of let
                let read_error =
                    LuaError::ReadError(try_file.unwrap_err());

                thread_send(&sender, LuaResponse::Error(read_error));
            }
        },

        LuaQuery::GetVariable(varname) => {
            trace!("thread: Received request to get variable {}", varname);
            let var_result = lua.get(varname.as_str());

            match var_result {
                Some(var) => {
                    thread_send(&sender, LuaResponse::Variable(Some(var)));
                }
                None => {
                    warn!("thread: Unable to get variable {}", varname);

                    thread_send(&sender, LuaResponse::Variable(None));
                }
            }
        },

        LuaQuery::SetValue { name: name, val: val } => {

        },

        LuaQuery::EmptyArray(name) => {

        },

        _ => {
            panic!("Unimplemented send type for lua thread!");
        }
    }
}

fn thread_send(sender: &Sender<LuaResponse>, response: LuaResponse)
               -> Result<(), String> {
    trace!("Called thread_send");
    match sender.send(response) {
        Ok(()) => Ok(()),
        Err(e) => {
            error!("thread: Unable to broadcast response!");
            error!("thread: Shutting down in response to inability to continue!");
            panic!("Lua thread unable to communicate with main thread, shutting down!");
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Fedor Gogolev <knsd@knsd.net>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::{Read, BufRead, Error as IoError, ErrorKind};
use std::string::{FromUtf8Error};
use std::cell::{RefCell};
use std::rc::{Rc};

use num::{Zero};
use num::bigint::{BigInt, ToBigInt, Sign};
use byteorder::{ReadBytesExt, LittleEndian, BigEndian, Error as ByteorderError};
use from_ascii::{FromAscii, ParseIntError, ParseFloatError};

use string::{unescape, Error as UnescapeError};
use value::{Value};

use opcodes::*;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        EmptyMarker
        StackTooSmall
        EmptyStack
        InvalidValueOnStack
        InvalidGetValue
        InvalidPutValue
        NotImplemented

        Read(err: ByteorderError) {
            from()
        }
        Io(err: IoError) {
            from()
        }
        UnknownOpcode(opcode: u8) {}

        InvalidInt {
            from(ParseIntError)
        }
        InvalidLong
        InvalidFloat {
            from(ParseFloatError)
        }

        InvalidString {
            from(FromUtf8Error)
        }
        UnescapeError(err: UnescapeError) {
            from()
        }

        InvalidProto(proto: u8)
        NegativeLength {}

        #[doc(hidden)]
        __Nonexhaustive
    }
}

#[derive(Debug, PartialEq)]
pub enum BooleanOrInt {
    Boolean(bool),
    Int(i64),
}

macro_rules! rc {
    ($term: expr) => (Rc::new(RefCell::new($term)))
}

fn read_exact<R>(rd: &mut R, mut buf: &mut [u8]) -> Result<(), IoError> where R: Read {
    while !buf.is_empty() {
        match rd.read(buf) {
            Ok(0) => break,
            Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; }
            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    if !buf.is_empty() {
        Err(IoError::new(ErrorKind::Other,
                       "failed to fill whole buffer"))
    } else {
        Ok(())
    }
}

fn read_until_newline<R>(rd: &mut R) -> Result<Vec<u8>, Error> where R: Read + BufRead {
    let mut buf = Vec::new();
    try!(rd.read_until('\n' as u8, &mut buf));

    // Skip last symbol — \n
    match buf.split_last() {
        Some((&b'\n', init)) => Ok(init.to_vec()),
        _ => return Err(Error::InvalidString),
    }
}

fn read_decimal_int<R>(rd: &mut R) -> Result<BooleanOrInt, Error> where R: Read + BufRead {
    let s = try!(read_until_newline(rd));
    let val = match &s[..] {
        b"00" => BooleanOrInt::Boolean(false),
        b"01" => BooleanOrInt::Boolean(true),
        _ => BooleanOrInt::Int(try!(i64::from_ascii(&s)))
    };
    Ok(val)
}

fn read_decimal_long<R>(rd: &mut R) -> Result<BigInt, Error> where R: Read + BufRead {
    let s = try!(read_until_newline(rd));
    let init = match s.split_last() {
        None => return Err(Error::InvalidString),
        Some((&b'L', init)) => init,
        Some(_) => &s[..],
    };

    match BigInt::parse_bytes(&init, 10) {
        Some(i) => Ok(i),
        None => Err(Error::InvalidLong)
    }
}

fn read_long<R>(rd: &mut R, length: usize) -> Result<BigInt, Error> where R: Read + BufRead {
    let mut buf = vec![0; length];
    try!(read_exact(rd, buf.as_mut()));

    let mut n = BigInt::from_bytes_le(Sign::Plus, &buf);

    let last = match buf.last_mut() {
        None => return Err(Error::InvalidLong),
        Some(last) => last,
    };

    if *last > 127 {
        n = n - (1.to_bigint().unwrap() << (length * 8))
    }

    Ok(n)
}

pub struct Machine {
    stack: Vec<Value>,
    memo: Vec<Value>,
    marker: Option<usize>,
}

impl Machine {
    pub fn new() -> Self {
        Machine {
            stack: Vec::new(),
            memo: vec![Value::None],
            marker: None,
        }
    }

    fn split_off(&mut self) -> Result<Vec<Value>, Error> {
        let at = match self.marker {
            None => return Err(Error::EmptyMarker),
            Some(marker) => marker,
        };

        if at > self.stack.len() {
            return Err(Error::StackTooSmall);
        }

        Ok(self.stack.split_off(at))
    }

    pub fn pop(&mut self) -> Result<Value, Error> {
        match self.stack.pop() {
            None => return Err(Error::EmptyStack),
            Some(value) => Ok(value),
        }
    }

    fn handle_get(&mut self, i: usize) -> Result<(), Error> {
        let value = match self.memo.get(i) {
            None => return Err(Error::InvalidGetValue),
            Some(ref v) => (*v).clone(),
        };
        self.stack.push(value);
        Ok(())
    }

    fn handle_put(&mut self, i: usize) -> Result<(), Error> {
        let value = match self.stack.last() {
            None => return Err(Error::EmptyStack),
            Some(ref v) => (*v).clone(),
        };
        let len = self.memo.len();
        if len != i {
            return Err(Error::InvalidPutValue)
        }
        self.memo.push(value);
        Ok(())
    }

    pub fn execute<R>(&mut self, rd: &mut R) -> Result<bool, Error> where R: Read + BufRead {
        macro_rules! ensure_not_negative {
            ($n: expr) => ({
                if $n < Zero::zero() {
                    return Err(Error::NegativeLength)
                }
            })
        }

        let marker = try!(rd.read_u8());
        match marker {
            PROTO => {
                let version = try!(rd.read_u8());
                if version < 2 {
                    return Err(Error::InvalidProto(version))
                }
            },
            STOP => return Ok(true),

            INT => {
                self.stack.push(match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Boolean(v) => Value::Bool(v),
                    BooleanOrInt::Int(v) => Value::Long(BigInt::from(v)),
                })
            },
            BININT => self.stack.push(Value::Int(try!(rd.read_i32::<LittleEndian>()) as isize)),
            BININT1 => self.stack.push(Value::Int(try!(rd.read_u8()) as isize)),
            BININT2 => self.stack.push(Value::Int(try!(rd.read_u16::<LittleEndian>()) as isize)),
            LONG => self.stack.push(Value::Long(BigInt::from(try!(read_decimal_long(rd))))),
            LONG1 => {
                let length = try!(rd.read_u8());
                self.stack.push(Value::Long(BigInt::from(try!(read_long(rd, length as usize)))))
            }
            LONG4 => {
                let length = try!(rd.read_i32::<LittleEndian>());
                self.stack.push(Value::Long(BigInt::from(try!(read_long(rd, length as usize)))))
            }

            STRING => self.stack.push(Value::String(try!(unescape(&try!(read_until_newline(rd)), false)))),
            BINSTRING => {
                let length = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(length);

                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, &mut buf));
                self.stack.push(Value::String(buf))
            },
            SHORT_BINSTRING => {
                let length = try!(rd.read_u8());
                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, &mut buf));
                self.stack.push(Value::String(buf))
            },

            NONE => self.stack.push(Value::None),
            NEWTRUE => self.stack.push(Value::Bool(true)),
            NEWFALSE => self.stack.push(Value::Bool(false)),

            UNICODE => {
                let buf = try!(unescape(&try!(read_until_newline(rd)), true));
                self.stack.push(Value::Unicode(try!(String::from_utf8(buf))))
            },
            BINUNICODE => {
                let length = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(length);
                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, buf.as_mut()));
                self.stack.push(Value::Unicode(try!(String::from_utf8(buf))))
            },

            FLOAT => {
                let s = try!(read_until_newline(rd));
                self.stack.push(Value::Float(try!(f64::from_ascii(&s))))
            },
            BINFLOAT => {
                self.stack.push(Value::Float(try!(rd.read_f64::<BigEndian>())))
            },

            EMPTY_LIST => {
                self.stack.push(Value::List(rc!(Vec::new())))
            },
            APPEND => {
                let v = try!(self.pop());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::List(ref mut list)) => (*list.borrow_mut()).push(v),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            APPENDS => {
                let values = try!(self.split_off());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::List(ref mut list)) => (*list.borrow_mut()).extend(values),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            LIST => {
                let values = try!(self.split_off());
                self.stack.push(Value::List(rc!(values)));
            },

            EMPTY_TUPLE => self.stack.push(Value::Tuple(rc!(Vec::new()))),
            TUPLE => {
                let values = try!(self.split_off());
                self.stack.push(Value::Tuple(rc!(values)));
            },
            TUPLE1 => {
                let v1 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1])))
            },
            TUPLE2 => {
                let v1 = try!(self.pop());
                let v2 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1, v2])))
            },
            TUPLE3 => {
                let v1 = try!(self.pop());
                let v2 = try!(self.pop());
                let v3 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1, v2, v3])))
            }

            EMPTY_DICT => self.stack.push(Value::Dict(rc!(Vec::new()))),
            DICT => {
                let mut values = try!(self.split_off());
                let mut dict = Vec::new();

                for i in 0 .. values.len() / 2 { // TODO: Check panic
                    let key = values.remove(2 * i);
                    let value = values.remove(2 * i + 1);
                    dict.push((key, value));
                }
                self.stack.push(Value::Dict(rc!(dict)));
            },
            SETITEM => {
                let value = try!(self.pop());
                let key = try!(self.pop());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::Dict(ref mut dict)) => (*dict.borrow_mut()).push((key, value)),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            SETITEMS => {
                let mut values = try!(self.split_off());

                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::Dict(ref mut dict_ref)) => {
                        for i in 0 .. values.len() / 2 { // TODO: Check panic
                                let key = values.remove(2 * i);
                                let value = values.remove(2 * i + 1);
                                (*dict_ref.borrow_mut()).push((key, value));
                            }
                    },
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },

            POP => {
                try!(self.pop());
            },
            DUP => {
                let value = match self.stack.last() {
                    None => return Err(Error::EmptyStack),
                    Some(ref v) => (*v).clone(),
                };
                self.stack.push(value)
            },
            MARK => {
                self.marker = Some(self.stack.len())
            },
            POP_MARK => {
                try!(self.split_off());
            },

            GET => {
                let n = match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Int(n) => n,
                    BooleanOrInt::Boolean(false) => 0,
                    BooleanOrInt::Boolean(true) => 1,
                };
                ensure_not_negative!(n);
                try!(self.handle_get(n as usize))
            }
            BINGET => {
                try!(self.handle_get(try!(rd.read_u8()) as usize))
            }
            LONG_BINGET => {
                let n = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(n);
                try!(self.handle_get(n as usize))
            }

            PUT => {
                let n = match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Int(n) => n,
                    BooleanOrInt::Boolean(false) => 0,
                    BooleanOrInt::Boolean(true) => 1,
                };
                ensure_not_negative!(n);
                try!(self.handle_put(n as usize))
            }
            BINPUT => {
                try!(self.handle_put(try!(rd.read_u8()) as usize))
            }
            LONG_BINPUT => {
                let n = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(n);
                try!(self.handle_put(n as usize))
            }

            c => return Err(Error::UnknownOpcode(c)),
        }
        Ok(false)
    }
}
<commit_msg>Use hashmap for memo<commit_after>// Copyright (c) 2016 Fedor Gogolev <knsd@knsd.net>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::{Read, BufRead, Error as IoError, ErrorKind};
use std::string::{FromUtf8Error};
use std::collections::{HashMap};
use std::cell::{RefCell};
use std::rc::{Rc};

use num::{Zero};
use num::bigint::{BigInt, ToBigInt, Sign};
use byteorder::{ReadBytesExt, LittleEndian, BigEndian, Error as ByteorderError};
use from_ascii::{FromAscii, ParseIntError, ParseFloatError};

use string::{unescape, Error as UnescapeError};
use value::{Value};

use opcodes::*;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        EmptyMarker
        StackTooSmall
        EmptyStack
        InvalidValueOnStack
        InvalidGetValue
        InvalidPutValue
        NotImplemented

        Read(err: ByteorderError) {
            from()
        }
        Io(err: IoError) {
            from()
        }
        UnknownOpcode(opcode: u8) {}

        InvalidInt {
            from(ParseIntError)
        }
        InvalidLong
        InvalidFloat {
            from(ParseFloatError)
        }

        InvalidString {
            from(FromUtf8Error)
        }
        UnescapeError(err: UnescapeError) {
            from()
        }

        InvalidProto(proto: u8)
        NegativeLength {}

        #[doc(hidden)]
        __Nonexhaustive
    }
}

#[derive(Debug, PartialEq)]
pub enum BooleanOrInt {
    Boolean(bool),
    Int(i64),
}

macro_rules! rc {
    ($term: expr) => (Rc::new(RefCell::new($term)))
}

fn read_exact<R>(rd: &mut R, mut buf: &mut [u8]) -> Result<(), IoError> where R: Read {
    while !buf.is_empty() {
        match rd.read(buf) {
            Ok(0) => break,
            Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; }
            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }
    if !buf.is_empty() {
        Err(IoError::new(ErrorKind::Other,
                       "failed to fill whole buffer"))
    } else {
        Ok(())
    }
}

fn read_until_newline<R>(rd: &mut R) -> Result<Vec<u8>, Error> where R: Read + BufRead {
    let mut buf = Vec::new();
    try!(rd.read_until('\n' as u8, &mut buf));

    // Skip last symbol — \n
    match buf.split_last() {
        Some((&b'\n', init)) => Ok(init.to_vec()),
        _ => return Err(Error::InvalidString),
    }
}

fn read_decimal_int<R>(rd: &mut R) -> Result<BooleanOrInt, Error> where R: Read + BufRead {
    let s = try!(read_until_newline(rd));
    let val = match &s[..] {
        b"00" => BooleanOrInt::Boolean(false),
        b"01" => BooleanOrInt::Boolean(true),
        _ => BooleanOrInt::Int(try!(i64::from_ascii(&s)))
    };
    Ok(val)
}

fn read_decimal_long<R>(rd: &mut R) -> Result<BigInt, Error> where R: Read + BufRead {
    let s = try!(read_until_newline(rd));
    let init = match s.split_last() {
        None => return Err(Error::InvalidString),
        Some((&b'L', init)) => init,
        Some(_) => &s[..],
    };

    match BigInt::parse_bytes(&init, 10) {
        Some(i) => Ok(i),
        None => Err(Error::InvalidLong)
    }
}

fn read_long<R>(rd: &mut R, length: usize) -> Result<BigInt, Error> where R: Read + BufRead {
    let mut buf = vec![0; length];
    try!(read_exact(rd, buf.as_mut()));

    let mut n = BigInt::from_bytes_le(Sign::Plus, &buf);

    let last = match buf.last_mut() {
        None => return Err(Error::InvalidLong),
        Some(last) => last,
    };

    if *last > 127 {
        n = n - (1.to_bigint().unwrap() << (length * 8))
    }

    Ok(n)
}

pub struct Machine {
    stack: Vec<Value>,
    memo: HashMap<usize, Value>,
    marker: Option<usize>,
}

impl Machine {
    pub fn new() -> Self {
        Machine {
            stack: Vec::new(),
            memo: HashMap::new(),
            marker: None,
        }
    }

    fn split_off(&mut self) -> Result<Vec<Value>, Error> {
        let at = match self.marker {
            None => return Err(Error::EmptyMarker),
            Some(marker) => marker,
        };

        if at > self.stack.len() {
            return Err(Error::StackTooSmall);
        }

        Ok(self.stack.split_off(at))
    }

    pub fn pop(&mut self) -> Result<Value, Error> {
        match self.stack.pop() {
            None => return Err(Error::EmptyStack),
            Some(value) => Ok(value),
        }
    }

    fn handle_get(&mut self, i: usize) -> Result<(), Error> {
        let value = match self.memo.get(&i) {
            None => return Err(Error::InvalidGetValue),
            Some(ref v) => (*v).clone(),
        };
        self.stack.push(value);
        Ok(())
    }

    fn handle_put(&mut self, i: usize) -> Result<(), Error> {
        let value = match self.stack.last() {
            None => return Err(Error::EmptyStack),
            Some(ref v) => (*v).clone(),
        };
        self.memo.insert(i, value);
        Ok(())
    }

    pub fn execute<R>(&mut self, rd: &mut R) -> Result<bool, Error> where R: Read + BufRead {
        macro_rules! ensure_not_negative {
            ($n: expr) => ({
                if $n < Zero::zero() {
                    return Err(Error::NegativeLength)
                }
            })
        }

        let marker = try!(rd.read_u8());
        match marker {
            PROTO => {
                let version = try!(rd.read_u8());
                if version < 2 {
                    return Err(Error::InvalidProto(version))
                }
            },
            STOP => return Ok(true),

            INT => {
                self.stack.push(match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Boolean(v) => Value::Bool(v),
                    BooleanOrInt::Int(v) => Value::Long(BigInt::from(v)),
                })
            },
            BININT => self.stack.push(Value::Int(try!(rd.read_i32::<LittleEndian>()) as isize)),
            BININT1 => self.stack.push(Value::Int(try!(rd.read_u8()) as isize)),
            BININT2 => self.stack.push(Value::Int(try!(rd.read_u16::<LittleEndian>()) as isize)),
            LONG => self.stack.push(Value::Long(BigInt::from(try!(read_decimal_long(rd))))),
            LONG1 => {
                let length = try!(rd.read_u8());
                self.stack.push(Value::Long(BigInt::from(try!(read_long(rd, length as usize)))))
            }
            LONG4 => {
                let length = try!(rd.read_i32::<LittleEndian>());
                self.stack.push(Value::Long(BigInt::from(try!(read_long(rd, length as usize)))))
            }

            STRING => self.stack.push(Value::String(try!(unescape(&try!(read_until_newline(rd)), false)))),
            BINSTRING => {
                let length = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(length);

                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, &mut buf));
                self.stack.push(Value::String(buf))
            },
            SHORT_BINSTRING => {
                let length = try!(rd.read_u8());
                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, &mut buf));
                self.stack.push(Value::String(buf))
            },

            NONE => self.stack.push(Value::None),
            NEWTRUE => self.stack.push(Value::Bool(true)),
            NEWFALSE => self.stack.push(Value::Bool(false)),

            UNICODE => {
                let buf = try!(unescape(&try!(read_until_newline(rd)), true));
                self.stack.push(Value::Unicode(try!(String::from_utf8(buf))))
            },
            BINUNICODE => {
                let length = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(length);
                let mut buf = vec![0; length as usize];
                try!(read_exact(rd, buf.as_mut()));
                self.stack.push(Value::Unicode(try!(String::from_utf8(buf))))
            },

            FLOAT => {
                let s = try!(read_until_newline(rd));
                self.stack.push(Value::Float(try!(f64::from_ascii(&s))))
            },
            BINFLOAT => {
                self.stack.push(Value::Float(try!(rd.read_f64::<BigEndian>())))
            },

            EMPTY_LIST => {
                self.stack.push(Value::List(rc!(Vec::new())))
            },
            APPEND => {
                let v = try!(self.pop());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::List(ref mut list)) => (*list.borrow_mut()).push(v),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            APPENDS => {
                let values = try!(self.split_off());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::List(ref mut list)) => (*list.borrow_mut()).extend(values),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            LIST => {
                let values = try!(self.split_off());
                self.stack.push(Value::List(rc!(values)));
            },

            EMPTY_TUPLE => self.stack.push(Value::Tuple(rc!(Vec::new()))),
            TUPLE => {
                let values = try!(self.split_off());
                self.stack.push(Value::Tuple(rc!(values)));
            },
            TUPLE1 => {
                let v1 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1])))
            },
            TUPLE2 => {
                let v1 = try!(self.pop());
                let v2 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1, v2])))
            },
            TUPLE3 => {
                let v1 = try!(self.pop());
                let v2 = try!(self.pop());
                let v3 = try!(self.pop());
                self.stack.push(Value::Tuple(rc!(vec![v1, v2, v3])))
            }

            EMPTY_DICT => self.stack.push(Value::Dict(rc!(Vec::new()))),
            DICT => {
                let mut values = try!(self.split_off());
                let mut dict = Vec::new();

                for i in 0 .. values.len() / 2 { // TODO: Check panic
                    let key = values.remove(2 * i);
                    let value = values.remove(2 * i + 1);
                    dict.push((key, value));
                }
                self.stack.push(Value::Dict(rc!(dict)));
            },
            SETITEM => {
                let value = try!(self.pop());
                let key = try!(self.pop());
                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::Dict(ref mut dict)) => (*dict.borrow_mut()).push((key, value)),
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },
            SETITEMS => {
                let mut values = try!(self.split_off());

                match self.stack.last_mut() {
                    None => return Err(Error::EmptyStack),
                    Some(&mut Value::Dict(ref mut dict_ref)) => {
                        for i in 0 .. values.len() / 2 { // TODO: Check panic
                                let key = values.remove(2 * i);
                                let value = values.remove(2 * i + 1);
                                (*dict_ref.borrow_mut()).push((key, value));
                            }
                    },
                    _ => return Err(Error::InvalidValueOnStack),
                }
            },

            POP => {
                try!(self.pop());
            },
            DUP => {
                let value = match self.stack.last() {
                    None => return Err(Error::EmptyStack),
                    Some(ref v) => (*v).clone(),
                };
                self.stack.push(value)
            },
            MARK => {
                self.marker = Some(self.stack.len())
            },
            POP_MARK => {
                try!(self.split_off());
            },

            GET => {
                let n = match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Int(n) => n,
                    BooleanOrInt::Boolean(false) => 0,
                    BooleanOrInt::Boolean(true) => 1,
                };
                ensure_not_negative!(n);
                try!(self.handle_get(n as usize))
            }
            BINGET => {
                try!(self.handle_get(try!(rd.read_u8()) as usize))
            }
            LONG_BINGET => {
                let n = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(n);
                try!(self.handle_get(n as usize))
            }

            PUT => {
                let n = match try!(read_decimal_int(rd)) {
                    BooleanOrInt::Int(n) => n,
                    BooleanOrInt::Boolean(false) => 0,
                    BooleanOrInt::Boolean(true) => 1,
                };
                ensure_not_negative!(n);
                try!(self.handle_put(n as usize))
            }
            BINPUT => {
                try!(self.handle_put(try!(rd.read_u8()) as usize))
            }
            LONG_BINPUT => {
                let n = try!(rd.read_i32::<LittleEndian>());
                ensure_not_negative!(n);
                try!(self.handle_put(n as usize))
            }

            c => return Err(Error::UnknownOpcode(c)),
        }
        Ok(false)
    }
}
<|endoftext|>


<commit_before>use std::io;

use consts::PARAMETER_SIZE;
use platform::PlatformManager;

pub struct U2FManager {
    platform: PlatformManager
}

impl U2FManager {
    pub fn new() -> Self {
        Self { platform: PlatformManager::new() }
    }

    pub fn register(&mut self, timeout: u64, challenge: Vec<u8>, application: Vec<u8>) -> io::Result<Vec<u8>>
    {
        if challenge.len() != PARAMETER_SIZE ||
           application.len() != PARAMETER_SIZE {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid parameter sizes"));
        }

        self.platform.register(timeout, challenge, application)
    }

    pub fn sign(&mut self, timeout: u64, challenge: Vec<u8>, application: Vec<u8>, key_handle: Vec<u8>) -> io::Result<Vec<u8>>
    {
        if challenge.len() != PARAMETER_SIZE ||
           application.len() != PARAMETER_SIZE {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid parameter sizes"));
        }

        if key_handle.len() > 256 {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Key handle too large"));
        }

        self.platform.sign(timeout, challenge, application, key_handle)
    }

    pub fn cancel<F>(&mut self) {
        self.platform.cancel();
    }
}
<commit_msg>Remove unused generic type param<commit_after>use std::io;

use consts::PARAMETER_SIZE;
use platform::PlatformManager;

pub struct U2FManager {
    platform: PlatformManager
}

impl U2FManager {
    pub fn new() -> Self {
        Self { platform: PlatformManager::new() }
    }

    pub fn register(&mut self, timeout: u64, challenge: Vec<u8>, application: Vec<u8>) -> io::Result<Vec<u8>>
    {
        if challenge.len() != PARAMETER_SIZE ||
           application.len() != PARAMETER_SIZE {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid parameter sizes"));
        }

        self.platform.register(timeout, challenge, application)
    }

    pub fn sign(&mut self, timeout: u64, challenge: Vec<u8>, application: Vec<u8>, key_handle: Vec<u8>) -> io::Result<Vec<u8>>
    {
        if challenge.len() != PARAMETER_SIZE ||
           application.len() != PARAMETER_SIZE {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid parameter sizes"));
        }

        if key_handle.len() > 256 {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "Key handle too large"));
        }

        self.platform.sign(timeout, challenge, application, key_handle)
    }

    pub fn cancel(&mut self) {
        self.platform.cancel();
    }
}
<|endoftext|>