<commit_before>//! Test Various helpers for Actix applications to use during testing.

use actix_http::http::header::{Header, IntoHeaderValue};
use actix_http::http::{HeaderName, HttpTryFrom, Version};
use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes;
#[cfg(feature = "cookies")]
use cookie::{Cookie, CookieJar};

use crate::ClientResponse;

/// Test `ClientResponse` builder
///
/// ```rust,ignore
/// # extern crate http;
/// # extern crate actix_web;
/// # use http::{header, StatusCode};
/// # use actix_web::*;
/// use actix_web::test::TestRequest;
///
/// fn index(req: &HttpRequest) -> Response {
///     if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
///         Response::Ok().into()
///     } else {
///         Response::BadRequest().into()
///     }
/// }
///
/// fn main() {
///     let resp = TestRequest::with_header("content-type", "text/plain")
///         .run(&index)
///         .unwrap();
///     assert_eq!(resp.status(), StatusCode::OK);
///
///     let resp = TestRequest::default().run(&index).unwrap();
///     assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestResponse(Option<Inner>);

struct Inner {
    head: ResponseHead,
    #[cfg(feature = "cookies")]
    cookies: CookieJar,
    payload: Option<Payload>,
}

impl Default for TestResponse {
    fn default() -> TestResponse {
        TestResponse(Some(Inner {
            head: ResponseHead::default(),
            #[cfg(feature = "cookies")]
            cookies: CookieJar::new(),
            payload: None,
        }))
    }
}

impl TestResponse {
    /// Create TestRequest and set header
    pub fn with_header<K, V>(key: K, value: V) -> Self
    where
        HeaderName: HttpTryFrom<K>,
        V: IntoHeaderValue,
    {
        Self::default().header(key, value).take()
    }

    /// Set HTTP version of this request
    pub fn version(&mut self, ver: Version) -> &mut Self {
        parts(&mut self.0).head.version = ver;
        self
    }

    /// Set a header
    pub fn set<H: Header>(&mut self, hdr: H) -> &mut Self {
        if let Ok(value) = hdr.try_into() {
            parts(&mut self.0).head.headers.append(H::name(), value);
            return self;
        }
        panic!("Can not set header");
    }

    /// Set a header
    pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
    where
        HeaderName: HttpTryFrom<K>,
        V: IntoHeaderValue,
    {
        if let Ok(key) = HeaderName::try_from(key) {
            if let Ok(value) = value.try_into() {
                parts(&mut self.0).head.headers.append(key, value);
                return self;
            }
        }
        panic!("Can not create header");
    }

    /// Set cookie for this request
    #[cfg(feature = "cookies")]
    pub fn cookie<'a>(&mut self, cookie: Cookie<'a>) -> &mut Self {
        parts(&mut self.0).cookies.add(cookie.into_owned());
        self
    }

    /// Set request payload
    pub fn set_payload<B: Into<Bytes>>(&mut self, data: B) -> &mut Self {
        let mut payload = h1::Payload::empty();
        payload.unread_data(data.into());
        parts(&mut self.0).payload = Some(payload.into());
        self
    }

    pub fn take(&mut self) -> Self {
        Self(self.0.take())
    }

    /// Complete request creation and generate `Request` instance
    pub fn finish(&mut self) -> ClientResponse {
        let inner = self.0.take().expect("cannot reuse test request builder");;
        let mut head = inner.head;

        #[cfg(feature = "cookies")]
        {
            use std::fmt::Write as FmtWrite;

            use actix_http::http::header::{self, HeaderValue};
            use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};

            let mut cookie = String::new();
            for c in inner.cookies.delta() {
                let name = percent_encode(c.name().as_bytes(), USERINFO_ENCODE_SET);
                let value = percent_encode(c.value().as_bytes(), USERINFO_ENCODE_SET);
                let _ = write!(&mut cookie, "; {}={}", name, value);
            }
            if !cookie.is_empty() {
                head.headers.insert(
                    header::SET_COOKIE,
                    HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
                );
            }
        }

        if let Some(pl) = inner.payload {
            ClientResponse::new(head, pl)
        } else {
            ClientResponse::new(head, h1::Payload::empty().into())
        }
    }
}

#[inline]
fn parts<'a>(parts: &'a mut Option<Inner>) -> &'a mut Inner {
    parts.as_mut().expect("cannot reuse test request builder")
}
<commit_msg>simplify TestResponse builder<commit_after>//! Test Various helpers for Actix applications to use during testing.

use actix_http::http::header::{Header, IntoHeaderValue};
use actix_http::http::{HeaderName, HttpTryFrom, Version};
use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes;
#[cfg(feature = "cookies")]
use cookie::{Cookie, CookieJar};

use crate::ClientResponse;

/// Test `ClientResponse` builder
pub struct TestResponse {
    head: ResponseHead,
    #[cfg(feature = "cookies")]
    cookies: CookieJar,
    payload: Option<Payload>,
}

impl Default for TestResponse {
    fn default() -> TestResponse {
        TestResponse {
            head: ResponseHead::default(),
            #[cfg(feature = "cookies")]
            cookies: CookieJar::new(),
            payload: None,
        }
    }
}

impl TestResponse {
    /// Create TestResponse and set header
    pub fn with_header<K, V>(key: K, value: V) -> Self
    where
        HeaderName: HttpTryFrom<K>,
        V: IntoHeaderValue,
    {
        Self::default().header(key, value)
    }

    /// Set HTTP version of this response
    pub fn version(mut self, ver: Version) -> Self {
        self.head.version = ver;
        self
    }

    /// Set a header
    pub fn set<H: Header>(mut self, hdr: H) -> Self {
        if let Ok(value) = hdr.try_into() {
            self.head.headers.append(H::name(), value);
            return self;
        }
        panic!("Can not set header");
    }

    /// Append a header
    pub fn header<K, V>(mut self, key: K, value: V) -> Self
    where
        HeaderName: HttpTryFrom<K>,
        V: IntoHeaderValue,
    {
        if let Ok(key) = HeaderName::try_from(key) {
            if let Ok(value) = value.try_into() {
                self.head.headers.append(key, value);
                return self;
            }
        }
        panic!("Can not create header");
    }

    /// Set cookie for this response
    #[cfg(feature = "cookies")]
    pub fn cookie<'a>(mut self, cookie: Cookie<'a>) -> Self {
        self.cookies.add(cookie.into_owned());
        self
    }

    /// Set response's payload
    pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
        let mut payload = h1::Payload::empty();
        payload.unread_data(data.into());
        self.payload = Some(payload.into());
        self
    }

    /// Complete response creation and generate `ClientResponse` instance
    pub fn finish(self) -> ClientResponse {
        let mut head = self.head;

        #[cfg(feature = "cookies")]
        {
            use std::fmt::Write as FmtWrite;

            use actix_http::http::header::{self, HeaderValue};
            use percent_encoding::{percent_encode, USERINFO_ENCODE_SET};

            let mut cookie = String::new();
            for c in self.cookies.delta() {
                let name = percent_encode(c.name().as_bytes(), USERINFO_ENCODE_SET);
                let value = percent_encode(c.value().as_bytes(), USERINFO_ENCODE_SET);
                let _ = write!(&mut cookie, "; {}={}", name, value);
            }
            if !cookie.is_empty() {
                head.headers.insert(
                    header::SET_COOKIE,
                    HeaderValue::from_str(&cookie.as_str()[2..]).unwrap(),
                );
            }
        }

        if let Some(pl) = self.payload {
            ClientResponse::new(head, pl)
        } else {
            ClientResponse::new(head, h1::Payload::empty().into())
        }
    }
}
<|endoftext|>


<commit_before>pub mod demosaic;
pub mod whitebalance;
pub mod level;
pub mod gamma;
pub mod curves;
pub mod colorspaces;

use decoders::Image;

pub fn fcol (img: &Image, row: usize, col: usize) -> usize {
  let filter: [usize; 256] = [
    2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0,
    0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2,
    2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3,
    0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1,
    3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0,
    2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1,
    2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1,
    1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3,
    2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3,
    1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2,
    0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2,
    2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0,
    1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0,
    0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1,
    2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2,
    0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3
  ];

  match img.dcraw_filters {
    1 => filter[(row&15)*img.width+(col&15)] as usize,
    //9 => img.xtrans_filters[((row+600) % 6)*width + ((col+600) % 6)],
    _ => (img.dcraw_filters >> (((row << 1 & 14) + (col & 1) ) << 1) & 3) as usize,
  }
}

pub fn simple_decode (img: &Image) -> Vec<f32> {
  let leveled = level::level(img);
  let wbed = whitebalance::whitebalance(img, &leveled);
  let demosaic = demosaic::ppg(img, &wbed);
  let rgbed = colorspaces::camera_to_rec709(img, &demosaic);
  let curved = curves::base(img, &rgbed);
  let gamma = gamma::gamma(img, &curved);

  gamma
}
<commit_msg>curves: disable the basic curve for now<commit_after>pub mod demosaic;
pub mod whitebalance;
pub mod level;
pub mod gamma;
pub mod curves;
pub mod colorspaces;

use decoders::Image;

pub fn fcol (img: &Image, row: usize, col: usize) -> usize {
  let filter: [usize; 256] = [
    2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0,
    0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2,
    2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3,
    0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1,
    3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0,
    2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1,
    2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1,
    1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3,
    2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3,
    1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2,
    0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2,
    2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0,
    1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0,
    0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1,
    2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2,
    0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3
  ];

  match img.dcraw_filters {
    1 => filter[(row&15)*img.width+(col&15)] as usize,
    //9 => img.xtrans_filters[((row+600) % 6)*width + ((col+600) % 6)],
    _ => (img.dcraw_filters >> (((row << 1 & 14) + (col & 1) ) << 1) & 3) as usize,
  }
}

pub fn simple_decode (img: &Image) -> Vec<f32> {
  let leveled = level::level(img);
  let wbed = whitebalance::whitebalance(img, &leveled);
  let demosaic = demosaic::ppg(img, &wbed);
  let rgbed = colorspaces::camera_to_rec709(img, &demosaic);
  //let curved = curves::base(img, &rgbed);
  let gamma = gamma::gamma(img, &rgbed);

  gamma
}
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
use std::borrow::Cow;
use syntax_pos::Span;
use syntax::symbol::{sym, Symbol};
use syntax::attr;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().kind {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, def_id, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
    for ty in ty.walk() {
        match ty.kind {
            ty::Ref(_, _, hir::Mutability::Mutable) => {
                if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
                    return Err((
                        span,
                        "mutable references in const fn are unstable".into(),
                    ))
                }
            }
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()))
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, place, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, operand, span, def_id, body),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, def_id, body)?;
            check_operand(tcx, rhs, span, def_id, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, def_id, body)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, def_id, body)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box(place, rval)) => {
            check_place(tcx, place, span, def_id, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        | StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _)
        if !feature_allowed(tcx, def_id, sym::const_if_match)
        => {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),

        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, place, span, def_id, body)
        }
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(
    tcx: TyCtxt<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>
) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let &[ref proj_base @ .., elem] = cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match)
                => return Err((span, "`match` or `if let` in `const fn` is unstable".into())),
            ProjectionElem::Downcast(_symbol, _variant_index) => {}

            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        if !feature_allowed(tcx, def_id, sym::const_fn_union) {
                            return Err((span, "accessing union fields is unstable".into()));
                        }
                    }
                }
            }
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {}
        }
    }

    Ok(())
}

/// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
fn feature_allowed(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    feature_gate: Symbol,
) -> bool {
    attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
        .map_or(false, |mut features| features.any(|name| name == feature_gate))
}

fn check_terminator(
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    def_id: DefId,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, location, span, def_id, body)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, location, span, def_id, body)?;
            check_operand(tcx, value, span, def_id, body)
        },

        | TerminatorKind::FalseEdges { .. }
        | TerminatorKind::SwitchInt { .. }
        if !feature_allowed(tcx, def_id, sym::const_if_match)
        => Err((
            span,
            "loops and conditional expressions are not stable in const fn".into(),
        )),

        TerminatorKind::FalseEdges { .. } => Ok(()),
        TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
            check_operand(tcx, discr, span, def_id, body)
        }

        // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally.
        TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()),

        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.kind {
                if !tcx.is_min_const_fn(def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                check_operand(tcx, func, span, def_id, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, def_id, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, def_id, body),

        TerminatorKind::FalseUnwind { .. } if !tcx.features().const_loop => {
            Err((span, "loops are not allowed in const fn".into()))
        },

        TerminatorKind::FalseUnwind { .. } => Ok(()),
    }
}
<commit_msg>Correctly mark things as `min_const_fn`<commit_after>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, Ty, TyCtxt, adjustment::{PointerCast}};
use std::borrow::Cow;
use syntax_pos::Span;
use syntax::symbol::{sym, Symbol};
use syntax::attr;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate {
                | Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                | Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().kind {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, def_id, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
    for ty in ty.walk() {
        match ty.kind {
            ty::Ref(_, _, hir::Mutability::Mutable) => {
                if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
                    return Err((
                        span,
                        "mutable references in const fn are unstable".into(),
                    ))
                }
            }
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()))
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        | ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ))
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, place, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => Err((
                    span,
                    "casting pointers to ints is unstable in const fn".into(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, operand, span, def_id, body),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _) |
        Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => Err((
            span,
            "unsizing casts are not allowed in const fn".into(),
        )),
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, def_id, body)?;
            check_operand(tcx, rhs, span, def_id, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((
                    span,
                    "only int, `bool` and `char` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => Err((
            span,
            "heap allocations are not allowed in const fn".into(),
        )),
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, def_id, body)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, def_id, body)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box(place, rval)) => {
            check_place(tcx, place, span, def_id, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        | StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _)
        if !feature_allowed(tcx, def_id, sym::const_if_match)
        => {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),

        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body),

        | StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        | StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, place, span, def_id, body)
        }
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(
    tcx: TyCtxt<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>
) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let &[ref proj_base @ .., elem] = cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match)
                => return Err((span, "`match` or `if let` in `const fn` is unstable".into())),
            ProjectionElem::Downcast(_symbol, _variant_index) => {}

            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(&place.base, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        if !feature_allowed(tcx, def_id, sym::const_fn_union) {
                            return Err((span, "accessing union fields is unstable".into()));
                        }
                    }
                }
            }
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {}
        }
    }

    Ok(())
}

/// Returns whether `allow_internal_unstable(..., <feature_gate>, ...)` is present.
fn feature_allowed(
    tcx: TyCtxt<'tcx>,
    def_id: DefId,
    feature_gate: Symbol,
) -> bool {
    attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
        .map_or(false, |mut features| features.any(|name| name == feature_gate))
}

fn check_terminator(
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    def_id: DefId,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => {
            check_place(tcx, location, span, def_id, body)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, location, span, def_id, body)?;
            check_operand(tcx, value, span, def_id, body)
        },

        | TerminatorKind::FalseEdges { .. }
        | TerminatorKind::SwitchInt { .. }
        if !feature_allowed(tcx, def_id, sym::const_if_match)
        => Err((
            span,
            "loops and conditional expressions are not stable in const fn".into(),
        )),

        TerminatorKind::FalseEdges { .. } => Ok(()),
        TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
            check_operand(tcx, discr, span, def_id, body)
        }

        // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally.
        TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()),

        | TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call {
            func,
            args,
            from_hir_call: _,
            destination: _,
            cleanup: _,
        } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.kind {
                if !tcx.is_min_const_fn(def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                check_operand(tcx, func, span, def_id, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, def_id, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert {
            cond,
            expected: _,
            msg: _,
            target: _,
            cleanup: _,
        } => check_operand(tcx, cond, span, def_id, body),

        | TerminatorKind::FalseUnwind { .. }
        if feature_allowed(tcx, def_id, sym::const_loop)
        => Ok(()),

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        }
    }
}
<|endoftext|>


<commit_before>use super::{ Message, Element, Pressed, Released, MouseMoved };
use entities::{ EntitiesState, EntitiesHelper, EntityID, ComponentID, NativeComponentType };
use nalgebra::na;
use nalgebra::na::Inv;
use script;

pub struct InputSystem {
    logger: Box<::log::Logger>,
    current_hover: Option<EntityID>,
    last_known_mouse_position: na::Vec2<f64>
}

impl InputSystem {
    pub fn new<L: ::log::Logger + 'static>(_: &EntitiesState, logger: L)
        -> InputSystem
    {
        InputSystem {
            logger: box logger,
            current_hover: None,
            last_known_mouse_position: na::Vec2::new(0.0, 0.0)
        }
    }

    pub fn process(&mut self, state: &mut EntitiesState, elapsed: &f64, messages: &[Message])
    {
        self.process_hover(state, elapsed, messages);

        let mut filteredMessagesIter = messages.iter().filter_map(|msg| match msg {
                                                                            &Pressed(ref e) => Some((e.clone(), true)),
                                                                            &Released(ref e) => Some((e.clone(), false)),
                                                                            _ => None
                                                                        });

        for (element, pressed) in filteredMessagesIter {
            let elementStr = format!("{}", element);

            for (component, script) in state
                                .get_visible_native_components("inputHandler")
                                .move_iter()
                                // filter if they have the right element
                                .filter(|c| match state.get_as_string(c, "element") { Some(s) => s == elementStr, _ => false })
                                // obtain the script and the component id
                                .filter_map(|c| match state.get_as_string(&c, "script") { Some(s) => Some((c.clone(), s)), _ => None })

                                .collect::<Vec<(ComponentID, String)>>().move_iter()
            {
                script::execute_mut(state, &component, &script.as_slice()).unwrap();
            }

            for (component, entity) in state
                                .get_visible_native_components("inputHandler")
                                .move_iter()
                                // filter if they have the right element
                                .filter(|c| match state.get_as_string(c, "element") { Some(s) => s == elementStr, _ => false })
                                // obtain the script and the component id
                                .filter_map(|c| match state.get_as_entity(&c, "prototypeWhilePressed") { Some(id) => Some((c.clone(), id)), _ => None })
                                .collect::<Vec<(ComponentID, EntityID)>>().move_iter()
            {
                let children = state.get_component_children(&component).unwrap();

                match (children.len() != 0, pressed) {
                    (false, true) =>
                        {
                            let owner = match state.get_owner(&component) { Ok(o) => o, _ => continue };
                            let newCmp = state.create_component_from_entity(&owner, &entity, ::std::collections::HashMap::new()).unwrap();
                            state.set_component_parent(&newCmp, &component);
                        },

                    (true, false) =>
                        {
                            for c in children.iter() {
                                state.destroy_component(c);
                            }
                        },

                    _ => ()
                }
            }
        }
    }

    fn process_hover(&mut self, state: &mut EntitiesState, _: &f64, messages: &[Message])
    {
        // getting the mouse position between (-1, -1) and (1, 1)
        let mouse_position = messages
            .iter().rev()
            .filter_map(|msg| match msg {
                &MouseMoved(x, y) => Some(na::Vec2::new(x, y)),
                _ => None
            })
            .next().unwrap_or(self.last_known_mouse_position);
        self.last_known_mouse_position = mouse_position;

        // getting the mouse position in camera units
        let mouse_position = {
            let mousePosVector = na::Vec4::new(mouse_position.x as f32, mouse_position.y as f32, 0.0, 1.0);
            let matrix = { let mut m = ::display::DisplaySystem::get_camera(state).unwrap(); m.inv(); m };
            let result = mousePosVector * matrix;
            na::Vec2::new(result.x / result.w, result.y / result.w)
        };

        // getting which entity is being hovered
        let hovered_entity: Option<EntityID> = state
            .get_visible_native_components("clickBox")
            .move_iter()
            .filter_map(|component| {
                let entity = match state.get_owner(&component) { Ok(t) => t, _ => return None };
                let entity_position = ::physics::PhysicsSystem::get_entity_position(state, &entity);

                let coord1 = match (state.get_as_number(&component, "leftX"), state.get_as_number(&component, "bottomY"))
                {
                    (Some(x), Some(y)) => na::Vec2::new(x as f32 + entity_position.x, y as f32 + entity_position.y),
                    _ => return None
                };

                let coord2 = match (state.get_as_number(&component, "rightX"), state.get_as_number(&component, "topY"))
                {
                    (Some(x), Some(y)) => na::Vec2::new(x as f32 + entity_position.x, y as f32 + entity_position.y),
                    _ => return None
                };

                if (coord1.x < coord2.x) && coord2.x < mouse_position.x { return None }
                if (coord2.x < coord1.x) && coord1.x < mouse_position.x { return None }
                if (coord1.x > coord2.x) && coord2.x > mouse_position.x { return None }
                if (coord2.x > coord1.x) && coord1.x > mouse_position.x { return None }
                if (coord1.y < coord2.y) && coord2.y < mouse_position.y { return None }
                if (coord2.y < coord1.y) && coord1.y < mouse_position.y { return None }
                if (coord1.y > coord2.y) && coord2.y > mouse_position.y { return None }
                if (coord2.y > coord1.y) && coord1.y > mouse_position.y { return None }

                Some(entity)
            })
            .next();

        // if the hovered entity has not changed, we have finished
        if hovered_entity == self.current_hover {
            return
        }

        // we have left the current hovered entity, executing script and removing prototype
        if self.current_hover.is_some() {
            // looping through each "hoverHandler" of the current_hover entity
            for cmp in state
                .get_components_iter()
                .filter(|c| state.is_component_visible(*c).unwrap())
                .filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "hoverHandler", _ => false })
                .filter(|c| state.get_owner(*c).ok() == self.current_hover)
                .map(|c| c.clone())
                .collect::<Vec<ComponentID>>().move_iter()
            {
                // removing all its children (ie. the prototype)
                for c in state.get_component_children(&cmp).unwrap().move_iter() {
                    state.destroy_component(&c).ok();
                }

                // executing onLeave script
                match state.get_as_string(&cmp, "scriptOnLeave") {
                    None => (),
                    Some(script) => { script::execute_mut(state, &cmp, &script.as_slice()).unwrap(); }
                };
            }
        }

        // updating self
        self.current_hover = hovered_entity;

        // processing the new entity
        match hovered_entity {
            None => (),
            Some(hovered_entity) => {
                // looping through each "hoverHandler" of the new entity
                for cmp in state
                    .get_visible_native_components("hoverHandler")
                    .move_iter()
                    .filter(|c| state.get_owner(c).ok() == Some(hovered_entity))
                    .collect::<Vec<ComponentID>>().move_iter()
                {
                    // adding prototype
                    match state.get_as_entity(&cmp, "prototype") {
                        Some(prototype) => {
                            let newCmp = state.create_component_from_entity(&hovered_entity, &prototype, ::std::collections::HashMap::new()).unwrap();
                            state.set_component_parent(&newCmp, &cmp);
                        },
                        None => ()
                    };

                    // executing onLeave script
                    match state.get_as_string(&cmp, "scriptOnEnter") {
                        None => (),
                        Some(script) => { script::execute_mut(state, &cmp, &script.as_slice()).unwrap(); }
                    };
                }
            }
        }
    }
}
<commit_msg>Cleanup of input system<commit_after>use super::{ Message, Element, Pressed, Released, MouseMoved };
use entities::{ EntitiesState, EntitiesHelper, EntityID, ComponentID, NativeComponentType };
use nalgebra::na;
use nalgebra::na::Inv;
use script;

pub struct InputSystem {
    logger: Box<::log::Logger>,
    current_hover: Option<EntityID>,
    last_known_mouse_position: na::Vec2<f64>
}

impl InputSystem {
    pub fn new<L: ::log::Logger + 'static>(_: &EntitiesState, logger: L)
        -> InputSystem
    {
        InputSystem {
            logger: box logger,
            current_hover: None,
            last_known_mouse_position: na::Vec2::new(0.0, 0.0)
        }
    }

    pub fn process(&mut self, state: &mut EntitiesState, elapsed: &f64, messages: &[Message])
    {
        self.process_hover(state, elapsed, messages);
        self.process_input_handlers(state, elapsed, messages);
    }

    /// Processes all "inputHandler" components.
    pub fn process_input_handlers(&mut self, state: &mut EntitiesState,
                                  elapsed: &f64, messages: &[Message])
    {
        // creating an iterator to have only the "pressed" and "release" messages
        let mut filteredMessagesIter = messages
            .iter()
            .filter_map(|msg| match msg {
                &Pressed(ref e) => Some((e, true)),
                &Released(ref e) => Some((e, false)),
                _ => None
            });

        for (element, pressed) in filteredMessagesIter {
            let element = format!("{}", element);

            // getting all "inputHandler" components that match the element
            for component in state
                .get_visible_native_components("inputHandler")
                .move_iter()
                .filter(|c|
                    match state.get_as_string(c, "element") {
                        Some(s) => s == element,
                        _ => false
                    }
                )
                .collect::<Vec<ComponentID>>().move_iter()
            {
                // this component **may** need an update
                // we delegate this to a subfunction
                self.update_input_handler(state, &component, pressed)
            }
        }
    }

    /// Processes all "hoverHandler" components.
    fn process_hover(&mut self, state: &mut EntitiesState, _: &f64, messages: &[Message])
    {
        // getting the mouse position between (-1, -1) and (1, 1)
        let mouse_position = messages
            .iter().rev()
            .filter_map(|msg| match msg {
                &MouseMoved(x, y) => Some(na::Vec2::new(x, y)),
                _ => None
            })
            .next().unwrap_or(self.last_known_mouse_position);
        self.last_known_mouse_position = mouse_position;

        // getting the mouse position in camera units
        let mouse_position = {
            let mousePosVector = na::Vec4::new(mouse_position.x as f32, mouse_position.y as f32, 0.0, 1.0);
            let matrix = { let mut m = ::display::DisplaySystem::get_camera(state).unwrap(); m.inv(); m };
            let result = mousePosVector * matrix;
            na::Vec2::new(result.x / result.w, result.y / result.w)
        };

        // getting which entity is being hovered
        let hovered_entity: Option<EntityID> = state
            .get_visible_native_components("clickBox")
            .move_iter()
            .filter_map(|component| {
                let entity = match state.get_owner(&component) { Ok(t) => t, _ => return None };
                let entity_position = ::physics::PhysicsSystem::get_entity_position(state, &entity);

                let coord1 = match (state.get_as_number(&component, "leftX"), state.get_as_number(&component, "bottomY"))
                {
                    (Some(x), Some(y)) => na::Vec2::new(x as f32 + entity_position.x, y as f32 + entity_position.y),
                    _ => return None
                };

                let coord2 = match (state.get_as_number(&component, "rightX"), state.get_as_number(&component, "topY"))
                {
                    (Some(x), Some(y)) => na::Vec2::new(x as f32 + entity_position.x, y as f32 + entity_position.y),
                    _ => return None
                };

                if (coord1.x < coord2.x) && coord2.x < mouse_position.x { return None }
                if (coord2.x < coord1.x) && coord1.x < mouse_position.x { return None }
                if (coord1.x > coord2.x) && coord2.x > mouse_position.x { return None }
                if (coord2.x > coord1.x) && coord1.x > mouse_position.x { return None }
                if (coord1.y < coord2.y) && coord2.y < mouse_position.y { return None }
                if (coord2.y < coord1.y) && coord1.y < mouse_position.y { return None }
                if (coord1.y > coord2.y) && coord2.y > mouse_position.y { return None }
                if (coord2.y > coord1.y) && coord1.y > mouse_position.y { return None }

                Some(entity)
            })
            .next();

        // if the hovered entity has not changed, we have finished
        if hovered_entity == self.current_hover {
            return
        }

        // we have left the current hovered entity, executing script and removing prototype
        if self.current_hover.is_some() {
            // looping through each "hoverHandler" of the current_hover entity
            for cmp in state
                .get_components_iter()
                .filter(|c| state.is_component_visible(*c).unwrap())
                .filter(|c| match state.get_type(*c) { Ok(NativeComponentType(t)) => t.as_slice() == "hoverHandler", _ => false })
                .filter(|c| state.get_owner(*c).ok() == self.current_hover)
                .map(|c| c.clone())
                .collect::<Vec<ComponentID>>().move_iter()
            {
                // removing all its children (ie. the prototype)
                for c in state.get_component_children(&cmp).unwrap().move_iter() {
                    state.destroy_component(&c).ok();
                }

                // executing onLeave script
                match state.get_as_string(&cmp, "scriptOnLeave") {
                    None => (),
                    Some(script) => { script::execute_mut(state, &cmp, &script.as_slice()).unwrap(); }
                };
            }
        }

        // updating self
        self.current_hover = hovered_entity;

        // processing the new entity
        match hovered_entity {
            None => (),
            Some(hovered_entity) => {
                // looping through each "hoverHandler" of the new entity
                for cmp in state
                    .get_visible_native_components("hoverHandler")
                    .move_iter()
                    .filter(|c| state.get_owner(c).ok() == Some(hovered_entity))
                    .collect::<Vec<ComponentID>>().move_iter()
                {
                    // adding prototype
                    match state.get_as_entity(&cmp, "prototype") {
                        Some(prototype) => {
                            let newCmp = state.create_component_from_entity(&hovered_entity, &prototype, ::std::collections::HashMap::new()).unwrap();
                            state.set_component_parent(&newCmp, &cmp);
                        },
                        None => ()
                    };

                    // executing onLeave script
                    match state.get_as_string(&cmp, "scriptOnEnter") {
                        None => (),
                        Some(script) => { script::execute_mut(state, &cmp, &script.as_slice()).unwrap(); }
                    };
                }
            }
        }
    }

    /// Updates a single "inputHandler" component.
    /// This function checks whether the component has its state matching
    /// the pressed/release state of the input.
    fn update_input_handler(&mut self, state: &mut EntitiesState,
                            component: &ComponentID, pressed: bool)
    {
        // executing the script if the component has one
        match state.get_as_string(component, "script") {
            Some(script) => {
                script::execute_mut(state, component, &script.as_slice()).unwrap();
            },
            None => ()
        };

        // handling prototype
        match state.get_as_entity(component, "prototypeWhilePressed") {
            None => (),
            Some(prototype) => {
                let children = state.get_component_children(component).unwrap();

                match (children.len() != 0, pressed) {
                    (false, true) =>
                        {
                            let owner = match state.get_owner(component) { Ok(o) => o, _ => return };
                            let newCmp = state.create_component_from_entity(&owner, &prototype, ::std::collections::HashMap::new()).unwrap();
                            state.set_component_parent(&newCmp, component);
                        },

                    (true, false) =>
                        {
                            for c in children.iter() {
                                state.destroy_component(c);
                            }
                        },

                    _ => ()
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/// Data load location information for an opcode
pub enum Load {

    /// Use contained constant as argument.
    Const(i32),

    /// Load argument from the contained memory address.
    Addr(u32),

    /// Pop argument off the stack.
    Pop,

    /// Load argument from the contained stack frame address.
    Frame(u32),

    /// Load argument from the contained RAM address.
    Ram(u32),
}

/// Data save location information for an opcode
pub enum Save {

    /// Discard result.
    Null,

    /// Save result at the contained memory address.
    Addr(u32),

    /// Push result onto the stack.
    Push,

    /// Save result at the contained stack frame address.
    Frame(u32),

    /// Save result at the contained RAM address.
    Ram(u32),
}

/// Glulx opcodes
pub enum Opcode {

    /// Does nothing.
    NOP,

    /// Add together two `i32`.
    ADD(Load, Load, Save),

    /// Substract second `i32` from first `i32`.
    SUB(Load, Load, Save),

    /// Multiply together two `i32`.
    MUL(Load, Load, Save),

    /// Divide first `i32` by a second `i32`.
    DIV(Load, Load, Save),

    /// Mod together two `i32`.
    MOD(Load, Load, Save),

    /// Negate an `i32`.
    NEG(Load, Save),

    /// Bitwise AND together two `i32`.
    BITAND(Load, Load, Save),

    /// Bitwise OR together two `i32`.
    BITOR(Load, Load, Save),

    /// Bitwise XOR together two `i32`.
    BITXOR(Load, Load, Save),

    /// Bitwise NOT an `i32`.
    BITNOT(Load, Save),
    SHIFTL(Load, Load, Save),
    SSHIFTR(Load, Load, Save),
    USHIFTR(Load, Load, Save),

    /// Jump to a memory address.
    JUMP(Load),

    /// Jump if the first `i32` is zero.
    JZ(Load, Load),

    /// Jump if the first `i32` is not zero.
    JNZ(Load, Load),

    /// Jump if the first two `i32`s are equal.
    JEQ(Load, Load, Load),

    /// Jump if the first two `i32`s are not equal.
    JNE(Load, Load, Load),

    /// Jump if the first `i32` is less than the second `i32`.
    JLT(Load, Load, Load),

    /// Jump if the first `i32` is greater than or equal to the second `i32`.
    JGE(Load, Load, Load),

    /// Jump if the first `i32` is greater than to the second `i32`.
    JGT(Load, Load, Load),

    /// Jump if the first `i32` is less than or equal to the second `i32`.
    JLE(Load, Load, Load),

    /// Jump if the first `u32` is less than the second `u32`.
    JLTU(Load, Load, Load),

    /// Jump if the first `u32` is greater than or equal to the second `u32`.
    JGEU(Load, Load, Load),

    /// Jump if the first `u32` is greater than to the second `u32`.
    JGTU(Load, Load, Load),

    /// Jump if the first `u32` is less than or equal to the second `u32`.
    JLEU(Load, Load, Load),
    CALL(Load, Load, Save),
    RETURN(Load),
    CATCH(Save, Load),
    THROW(Load, Load),
    TAILCALL(Load, Load),
    COPY(Load, Save),
    COPYS(Load, Save),
    COPYB(Load, Save),
    SEXS(Load, Save),
    SEXB(Load, Save),
    ALOAD(Load, Load, Save),
    ALOADS(Load, Load, Save),
    ALOADB(Load, Load, Save),
    ALOADBIT(Load, Load, Save),
    ASTORE(Load, Load, Load),
    ASTORES(Load, Load, Load),
    ASTOREB(Load, Load, Load),
    ASTOREBIT(Load, Load, Load),
    STKCOUNT(Save),
    STKPEEK(Load, Save),
    STKSWAP,
    STKROLL(Load, Load),
    STKCOPY(Load),
    STREAMCHAR(Load),
    STREAMNUM(Load),
    STREAMSTR(Load),
    STREAMUNICHAR(Load),
    GESTALT(Load, Load, Save),
    DEBUGTRAP(Load),
    GETMEMSIZE(Save),
    SETMEMSIZE(Load, Save),
    JUMPABS(Load, Load, Load),
    RANDOM(Load, Save),
    SETRANDOM(Save),
    QUIT,
    VERIFY(Save),
    RESTART,
    SAVE(Load, Save),
    RESTORE(Load, Save),
    SAVEUNDO(Save),
    RESTOREUNDO(Save),
    PROTECT(Load, Load),
    GLK(Load, Load, Save),
    GETSTRINGTBL(Save),
    SETSTRINGTBL(Load),
    GETIOSYS(Save, Save),
    SETIOSYS(Load, Load),
    LINEARSEARCH(Load, Load, Load, Load, Load, Load, Load, Save),
    BINARYSEARCH(Load, Load, Load, Load, Load, Load, Load, Save),
    LINKEDSEARCH(Load, Load, Load, Load, Load, Load, Save),
    CALLF(Load, Save),
    CALLFI(Load, Load, Save),
    CALLFII(Load, Load, Load, Save),
    CALLFIII(Load, Load, Load, Load, Save),

    /// Zero `l1: u32` points in memory starting at the second `u32`.
    MZERO(Load, Load),

    /// Copy `l1: u32` points in memory from `l2: u32` to `l3: u32`.
    MCOPY(Load, Load, Load),
    MALLOC(Load, Save),
    MFREE(Load),
    ACCELFUNC(Load, Load),
    ACCELPARAM(Load, Load),

    /// Convert `i32` to `f32`.
    NUMTOF(Load, Save),

    /// Truncate `f32` to `i32`.
    FTONUMZ(Load, Save),

    /// Round `f32` to `i32`.
    FTONUMN(Load, Save),

    /// Ceil of `f32` as `f32`.
    CEIL(Load, Save),

    /// floor of `f32` as `f32`.
    FLOOR(Load, Save),

    /// Add together two `f32`s.
    FADD(Load, Load, Save),

    /// Subtract the second `f32` from the first `f32`.
    FSUB(Load, Load, Save),

    /// Multiply together two `f32`s.
    FMUL(Load, Load, Save),

    /// Divide the first `f32` by the second `f32`.
    FDIV(Load, Load, Save),
    FMOD(Load, Load, Save, Save),

    /// Return square root of `f32`.
    SQRT(Load, Save),

    /// Return natural exponent of `f32`.
    EXP(Load, Save),

    /// Return natural log of `f32`.
    LOG(Load, Save),

    /// Return power of first `f32` to second `f32`.
    POW(Load, Load, Save),

    /// Return sin of `f32`.
    SIN(Load, Save),

    /// Return cos of `f32`.
    COS(Load, Save),

    /// Return tan of `f32`.
    TAN(Load, Save),

    /// Return arcsin of `f32`.
    ASIN(Load, Save),

    /// Return arccos of `f32`.
    ACOS(Load, Save),

    /// Return arctan of `f32`.
    ATAN(Load, Save),
    ATAN2(Load, Load, Save),

    /// Jump if the second `f32` is in the range of the first `f32`
    /// += the third `f32`
    JFEQ(Load, Load, Load, Load),

    /// Jump if the second `f32` is not in the range of the first `f32`
    /// += the third `f32`
    JFNE(Load, Load, Load, Load),

    /// Jump if the first `f32` is less than the second `f32`.
    JFLT(Load, Load, Load),

    /// Jump if the first `f32` is less than or equal to the second `f32`.
    JFLE(Load, Load, Load),

    /// Jump if the first `f32` is greater than the second `f32`.
    JFGT(Load, Load, Load),

    /// Jump if the first `f32` is greater than or equal to the second `f32`.
    JFGE(Load, Load, Load),

    /// Jump if `f32` is `f32::NAN`.
    JISNAN(Load, Load),

    /// Jump if `f32` is `f32::INFINITY`.
    JISINF(Load, Load),
}
<commit_msg>added doc strings to bit shift instructions<commit_after>/// Data load location information for an opcode
pub enum Load {

    /// Use contained constant as argument.
    Const(i32),

    /// Load argument from the contained memory address.
    Addr(u32),

    /// Pop argument off the stack.
    Pop,

    /// Load argument from the contained stack frame address.
    Frame(u32),

    /// Load argument from the contained RAM address.
    Ram(u32),
}

/// Data save location information for an opcode
pub enum Save {

    /// Discard result.
    Null,

    /// Save result at the contained memory address.
    Addr(u32),

    /// Push result onto the stack.
    Push,

    /// Save result at the contained stack frame address.
    Frame(u32),

    /// Save result at the contained RAM address.
    Ram(u32),
}

/// Glulx opcodes
pub enum Opcode {

    /// Does nothing.
    NOP,

    /// Add together two `i32`.
    ADD(Load, Load, Save),

    /// Substract second `i32` from first `i32`.
    SUB(Load, Load, Save),

    /// Multiply together two `i32`.
    MUL(Load, Load, Save),

    /// Divide first `i32` by a second `i32`.
    DIV(Load, Load, Save),

    /// Mod together two `i32`.
    MOD(Load, Load, Save),

    /// Negate an `i32`.
    NEG(Load, Save),

    /// Bitwise AND together two `i32`.
    BITAND(Load, Load, Save),

    /// Bitwise OR together two `i32`.
    BITOR(Load, Load, Save),

    /// Bitwise XOR together two `i32`.
    BITXOR(Load, Load, Save),

    /// Bitwise NOT an `i32`.
    BITNOT(Load, Save),

    /// Bit shift left
    SHIFTL(Load, Load, Save),

    /// Signed bit shift right
    SSHIFTR(Load, Load, Save),

    /// Unsigned bit shift right
    USHIFTR(Load, Load, Save),

    /// Jump to a memory address.
    JUMP(Load),

    /// Jump if the first `i32` is zero.
    JZ(Load, Load),

    /// Jump if the first `i32` is not zero.
    JNZ(Load, Load),

    /// Jump if the first two `i32`s are equal.
    JEQ(Load, Load, Load),

    /// Jump if the first two `i32`s are not equal.
    JNE(Load, Load, Load),

    /// Jump if the first `i32` is less than the second `i32`.
    JLT(Load, Load, Load),

    /// Jump if the first `i32` is greater than or equal to the second `i32`.
    JGE(Load, Load, Load),

    /// Jump if the first `i32` is greater than to the second `i32`.
    JGT(Load, Load, Load),

    /// Jump if the first `i32` is less than or equal to the second `i32`.
    JLE(Load, Load, Load),

    /// Jump if the first `u32` is less than the second `u32`.
    JLTU(Load, Load, Load),

    /// Jump if the first `u32` is greater than or equal to the second `u32`.
    JGEU(Load, Load, Load),

    /// Jump if the first `u32` is greater than to the second `u32`.
    JGTU(Load, Load, Load),

    /// Jump if the first `u32` is less than or equal to the second `u32`.
    JLEU(Load, Load, Load),
    CALL(Load, Load, Save),
    RETURN(Load),
    CATCH(Save, Load),
    THROW(Load, Load),
    TAILCALL(Load, Load),
    COPY(Load, Save),
    COPYS(Load, Save),
    COPYB(Load, Save),
    SEXS(Load, Save),
    SEXB(Load, Save),
    ALOAD(Load, Load, Save),
    ALOADS(Load, Load, Save),
    ALOADB(Load, Load, Save),
    ALOADBIT(Load, Load, Save),
    ASTORE(Load, Load, Load),
    ASTORES(Load, Load, Load),
    ASTOREB(Load, Load, Load),
    ASTOREBIT(Load, Load, Load),
    STKCOUNT(Save),
    STKPEEK(Load, Save),
    STKSWAP,
    STKROLL(Load, Load),
    STKCOPY(Load),
    STREAMCHAR(Load),
    STREAMNUM(Load),
    STREAMSTR(Load),
    STREAMUNICHAR(Load),
    GESTALT(Load, Load, Save),
    DEBUGTRAP(Load),
    GETMEMSIZE(Save),
    SETMEMSIZE(Load, Save),
    JUMPABS(Load, Load, Load),
    RANDOM(Load, Save),
    SETRANDOM(Save),
    QUIT,
    VERIFY(Save),
    RESTART,
    SAVE(Load, Save),
    RESTORE(Load, Save),
    SAVEUNDO(Save),
    RESTOREUNDO(Save),
    PROTECT(Load, Load),
    GLK(Load, Load, Save),
    GETSTRINGTBL(Save),
    SETSTRINGTBL(Load),
    GETIOSYS(Save, Save),
    SETIOSYS(Load, Load),
    LINEARSEARCH(Load, Load, Load, Load, Load, Load, Load, Save),
    BINARYSEARCH(Load, Load, Load, Load, Load, Load, Load, Save),
    LINKEDSEARCH(Load, Load, Load, Load, Load, Load, Save),
    CALLF(Load, Save),
    CALLFI(Load, Load, Save),
    CALLFII(Load, Load, Load, Save),
    CALLFIII(Load, Load, Load, Load, Save),

    /// Zero `l1: u32` points in memory starting at the second `u32`.
    MZERO(Load, Load),

    /// Copy `l1: u32` points in memory from `l2: u32` to `l3: u32`.
    MCOPY(Load, Load, Load),
    MALLOC(Load, Save),
    MFREE(Load),
    ACCELFUNC(Load, Load),
    ACCELPARAM(Load, Load),

    /// Convert `i32` to `f32`.
    NUMTOF(Load, Save),

    /// Truncate `f32` to `i32`.
    FTONUMZ(Load, Save),

    /// Round `f32` to `i32`.
    FTONUMN(Load, Save),

    /// Ceil of `f32` as `f32`.
    CEIL(Load, Save),

    /// floor of `f32` as `f32`.
    FLOOR(Load, Save),

    /// Add together two `f32`s.
    FADD(Load, Load, Save),

    /// Subtract the second `f32` from the first `f32`.
    FSUB(Load, Load, Save),

    /// Multiply together two `f32`s.
    FMUL(Load, Load, Save),

    /// Divide the first `f32` by the second `f32`.
    FDIV(Load, Load, Save),
    FMOD(Load, Load, Save, Save),

    /// Return square root of `f32`.
    SQRT(Load, Save),

    /// Return natural exponent of `f32`.
    EXP(Load, Save),

    /// Return natural log of `f32`.
    LOG(Load, Save),

    /// Return power of first `f32` to second `f32`.
    POW(Load, Load, Save),

    /// Return sin of `f32`.
    SIN(Load, Save),

    /// Return cos of `f32`.
    COS(Load, Save),

    /// Return tan of `f32`.
    TAN(Load, Save),

    /// Return arcsin of `f32`.
    ASIN(Load, Save),

    /// Return arccos of `f32`.
    ACOS(Load, Save),

    /// Return arctan of `f32`.
    ATAN(Load, Save),
    ATAN2(Load, Load, Save),

    /// Jump if the second `f32` is in the range of the first `f32`
    /// += the third `f32`
    JFEQ(Load, Load, Load, Load),

    /// Jump if the second `f32` is not in the range of the first `f32`
    /// += the third `f32`
    JFNE(Load, Load, Load, Load),

    /// Jump if the first `f32` is less than the second `f32`.
    JFLT(Load, Load, Load),

    /// Jump if the first `f32` is less than or equal to the second `f32`.
    JFLE(Load, Load, Load),

    /// Jump if the first `f32` is greater than the second `f32`.
    JFGT(Load, Load, Load),

    /// Jump if the first `f32` is greater than or equal to the second `f32`.
    JFGE(Load, Load, Load),

    /// Jump if `f32` is `f32::NAN`.
    JISNAN(Load, Load),

    /// Jump if `f32` is `f32::INFINITY`.
    JISINF(Load, Load),
}
<|endoftext|>


<commit_before>use dynasmrt::x64::Assembler;
use dynasmrt::{DynasmApi, DynasmLabelApi};

use super::*;
use peephole::{self, Count};

dynasm!(asm
    ; .alias pointer, r12
    ; .alias mem_start, r13
    ; .alias mem_limit, r14
    ; .alias rts, r15
);

/// Compiles peephole-optimized AST to x64 machine code.
///
/// Uses the `dynasmrt` assembler
pub fn compile(program: &peephole::Program, checked: bool) -> Program {
    let mut asm = dynasmrt::x64::Assembler::new();
    let start = asm.offset();

    dynasm!(asm
        ; push r12
        ; push r13
        ; push r14
        ; push r15
        ; mov pointer, rcx
        ; mov mem_start, rcx
        ; mov mem_limit, rcx
        ; add mem_limit, rdx
        ; mov rts, r8
    );

    compile_sequence(&mut asm, program, checked);

    dynasm!(asm
        ; mov rax, rts::OKAY as i32
        ; jmp ->finish

        ; ->underflow:
        ; mov rax, rts::UNDERFLOW as i32
        ; jmp ->finish

        ; ->overflow:
        ; mov rax, rts::OVERFLOW as i32

        ; ->finish:
        ; pop r15
        ; pop r14
        ; pop r13
        ; pop r12
        ; ret
    );

    let code = asm.finalize().unwrap();

    Program {
        code: code,
        start: start,
    }
}

fn compile_sequence(asm: &mut Assembler, program: &[peephole::Instruction], checked: bool) {
    for instruction in program {
        compile_instruction(asm, instruction, checked);
    }
}

fn compile_instruction(asm: &mut Assembler, instruction: &peephole::Instruction, checked: bool) {
    use peephole::Instruction::*;

    match *instruction {
        Right(count) => {
            dynasm!(asm
                ;; load_pos_offset(asm, count, checked)
                ; add pointer, rax
            );
        }

        Left(count) => {
            dynasm!(asm
                ;; load_neg_offset(asm, count, checked)
                ; sub pointer, rax
            );
        }

        Change(count) => {
            dynasm!(asm
                ; add [pointer], BYTE u8_to_i8(count)
            );
        }

        In => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::read as _
                ; mov rcx, rts
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
                ; mov [pointer], al
            );
        }

        Out => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::write as _
                ; mov rcx, rts
                ; xor rdx, rdx
                ; mov dl, [pointer]
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
            );
        }

        SetZero => {
            dynasm!(asm
                ; mov BYTE [pointer], 0
            )
        }

        FindZeroRight(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_pos_offset(asm, skip, checked)
                ; add pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        FindZeroLeft(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_neg_offset(asm, skip, checked)
                ; sub pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        OffsetAddRight(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_pos_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        OffsetAddLeft(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_neg_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; neg rax
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        Loop(ref body) => {
            let begin_label = asm.new_dynamic_label();
            let end_label   = asm.new_dynamic_label();

            dynasm!(asm
                ; jmp =>end_label
                ; =>begin_label
            );

            compile_sequence(asm, body, checked);

            dynasm!(asm
                ; =>end_label
                ; cmp BYTE [pointer], 0
                ; jnz =>begin_label
            );
        }
    }
}

#[inline]
fn load_offset(asm: &mut Assembler, offset: Count) {
    if offset as i8 as Count == offset {
        dynasm!(asm
            ; mov rax, BYTE offset as i32
        );
    } else if offset as i16 as Count == offset {
        dynasm!(asm
            ; mov rax, WORD offset as i32
        );
    } else if offset as i32 as Count == offset {
        dynasm!(asm
            ; mov rax, DWORD offset as i32
        );
    } else {
        dynasm!(asm
            ; mov rax, QWORD offset as i64
        );
    }
}

fn load_pos_offset(asm: &mut Assembler, offset: Count, checked: bool) {
    load_offset(asm, offset);

    if checked {
        dynasm!(asm
            ; mov rcx, mem_limit
            ; sub rcx, pointer
            ; cmp rcx, rax
            ; jle ->overflow
        );
    }
}

fn load_neg_offset(asm: &mut Assembler, offset: Count, checked: bool) {
    load_offset(asm, offset);

    if checked {
        dynasm!(asm
            ; mov rcx, pointer
            ; sub rcx, mem_start
            ; cmp rcx, rax
            ; jl ->underflow
        );
    }
}

fn u8_to_i8(n: u8) -> i8 {
    let mut n = n as isize % 256;
    if n > 127 { n -= 256 };
    n as i8
}
<commit_msg>Don't need as many cases for constant load.<commit_after>use dynasmrt::x64::Assembler;
use dynasmrt::{DynasmApi, DynasmLabelApi};

use super::*;
use peephole::{self, Count};

dynasm!(asm
    ; .alias pointer, r12
    ; .alias mem_start, r13
    ; .alias mem_limit, r14
    ; .alias rts, r15
);

/// Compiles peephole-optimized AST to x64 machine code.
///
/// Uses the `dynasmrt` assembler
pub fn compile(program: &peephole::Program, checked: bool) -> Program {
    let mut asm = dynasmrt::x64::Assembler::new();
    let start = asm.offset();

    dynasm!(asm
        ; push r12
        ; push r13
        ; push r14
        ; push r15
        ; mov pointer, rcx
        ; mov mem_start, rcx
        ; mov mem_limit, rcx
        ; add mem_limit, rdx
        ; mov rts, r8
    );

    compile_sequence(&mut asm, program, checked);

    dynasm!(asm
        ; mov rax, rts::OKAY as i32
        ; jmp ->finish

        ; ->underflow:
        ; mov rax, rts::UNDERFLOW as i32
        ; jmp ->finish

        ; ->overflow:
        ; mov rax, rts::OVERFLOW as i32

        ; ->finish:
        ; pop r15
        ; pop r14
        ; pop r13
        ; pop r12
        ; ret
    );

    let code = asm.finalize().unwrap();

    Program {
        code: code,
        start: start,
    }
}

fn compile_sequence(asm: &mut Assembler, program: &[peephole::Instruction], checked: bool) {
    for instruction in program {
        compile_instruction(asm, instruction, checked);
    }
}

fn compile_instruction(asm: &mut Assembler, instruction: &peephole::Instruction, checked: bool) {
    use peephole::Instruction::*;

    match *instruction {
        Right(count) => {
            dynasm!(asm
                ;; load_pos_offset(asm, count, checked)
                ; add pointer, rax
            );
        }

        Left(count) => {
            dynasm!(asm
                ;; load_neg_offset(asm, count, checked)
                ; sub pointer, rax
            );
        }

        Change(count) => {
            dynasm!(asm
                ; add [pointer], BYTE u8_to_i8(count)
            );
        }

        In => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::read as _
                ; mov rcx, rts
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
                ; mov [pointer], al
            );
        }

        Out => {
            dynasm!(asm
                ; mov rax, QWORD rts::RtsState::write as _
                ; mov rcx, rts
                ; xor rdx, rdx
                ; mov dl, [pointer]
                ; sub rsp, BYTE 0x28
                ; call rax
                ; add rsp, BYTE 0x28
            );
        }

        SetZero => {
            dynasm!(asm
                ; mov BYTE [pointer], 0
            )
        }

        FindZeroRight(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_pos_offset(asm, skip, checked)
                ; add pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        FindZeroLeft(skip) => {
            dynasm!(asm
                ; jmp >end_loop
                ; begin_loop:
                ;; load_neg_offset(asm, skip, checked)
                ; sub pointer, rax
                ; end_loop:
                ; cmp BYTE [pointer], 0
                ; jnz <begin_loop
            )
        }

        OffsetAddRight(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_pos_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        OffsetAddLeft(offset) => {
            dynasm!(asm
                ; cmp BYTE [pointer], 0
                ; jz >skip
                ;; load_neg_offset(asm, offset, checked)
                ; mov cl, BYTE [pointer]
                ; mov BYTE [pointer], 0
                ; neg rax
                ; add BYTE [pointer + rax], cl
                ; skip:
            );
        }

        Loop(ref body) => {
            let begin_label = asm.new_dynamic_label();
            let end_label   = asm.new_dynamic_label();

            dynasm!(asm
                ; jmp =>end_label
                ; =>begin_label
            );

            compile_sequence(asm, body, checked);

            dynasm!(asm
                ; =>end_label
                ; cmp BYTE [pointer], 0
                ; jnz =>begin_label
            );
        }
    }
}

#[inline]
fn load_offset(asm: &mut Assembler, offset: Count) {
    if offset as i32 as Count == offset {
        dynasm!(asm
            ; mov rax, DWORD offset as i32
        );
    } else {
        dynasm!(asm
            ; mov rax, QWORD offset as i64
        );
    }
}

fn load_pos_offset(asm: &mut Assembler, offset: Count, checked: bool) {
    load_offset(asm, offset);

    if checked {
        dynasm!(asm
            ; mov rcx, mem_limit
            ; sub rcx, pointer
            ; cmp rcx, rax
            ; jle ->overflow
        );
    }
}

fn load_neg_offset(asm: &mut Assembler, offset: Count, checked: bool) {
    load_offset(asm, offset);

    if checked {
        dynasm!(asm
            ; mov rcx, pointer
            ; sub rcx, mem_start
            ; cmp rcx, rax
            ; jl ->underflow
        );
    }
}

fn u8_to_i8(n: u8) -> i8 {
    let mut n = n as isize % 256;
    if n > 127 { n -= 256 };
    n as i8
}
<|endoftext|>


<commit_before>fn main() {
    // Assert `Iterator` methods are feature gated
    assert!([1, 2, 2, 9].iter().is_sorted());
    //^ ERROR: use of unstable library feature 'is_sorted'
    assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs()));
    //^ ERROR: use of unstable library feature 'is_sorted'

    // Assert `[T]` methods are feature gated
    assert!([1, 2, 2, 9].is_sorted());
    //^ ERROR: use of unstable library feature 'is_sorted'
    assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
    //^ ERROR: use of unstable library feature 'is_sorted'
}
<commit_msg>Correct error location indicated by comments<commit_after>fn main() {
    // Assert `Iterator` methods are feature gated
    assert!([1, 2, 2, 9].iter().is_sorted());
    //~^ ERROR: use of unstable library feature 'is_sorted': new API
    assert!(![-2i32, -1, 0, 3].iter().is_sorted_by_key(|n| n.abs()));
    //~^ ERROR: use of unstable library feature 'is_sorted': new API

    // Assert `[T]` methods are feature gated
    assert!([1, 2, 2, 9].is_sorted());
    //~^ ERROR: use of unstable library feature 'is_sorted': new API
    assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
    //~^ ERROR: use of unstable library feature 'is_sorted': new API
}
<|endoftext|>