<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::sync::Arc;

// We use a parking_lot since it's significantly faster under low contention
// and we will need to acquire a read lock with every packet that is processed
// to be able to capture the current endpoint state and pass it to Filters.
use parking_lot::RwLock;
use slog::{debug, o, warn, Logger};

use prometheus::{Registry, Result as MetricsResult};
use tokio::sync::{mpsc, watch};

use crate::cluster::Endpoint;
use crate::config::{Endpoints, UpstreamEndpoints};
use crate::xds::ads_client::ClusterUpdate;

use super::metrics::Metrics;

pub(crate) type SharedClusterManager = Arc<RwLock<ClusterManager>>;

/// ClusterManager knows about all clusters and endpoints.
pub(crate) struct ClusterManager {
    metrics: Metrics,
    endpoints: Option<Endpoints>,
}

/// InitializeError is returned with an error message if the
/// [`ClusterManager`] fails to initialize properly.
#[derive(Debug, thiserror::Error)]
pub enum InitializeError {
    #[error("{:?}", .0)]
    Message(String),
}

impl ClusterManager {
    fn new(metrics_registry: &Registry, endpoints: Option<Endpoints>) -> MetricsResult<Self> {
        let metrics = Metrics::new(metrics_registry)?;
        Ok(Self { metrics, endpoints })
    }

    fn update(&mut self, endpoints: Option<Endpoints>) {
        self.endpoints = endpoints;
    }

    /// Returns all endpoints known at the time of invocation.
    /// Returns `None` if there are no endpoints.
    pub fn get_all_endpoints(&self) -> Option<UpstreamEndpoints> {
        self.endpoints.clone().map(|ep| ep.into())
    }

    /// Returns a ClusterManager backed by the fixed set of clusters provided in the config.
    pub fn fixed(
        metrics_registry: &Registry,
        endpoints: Endpoints,
    ) -> MetricsResult<SharedClusterManager> {
        let cm = Self::new(metrics_registry, Some(endpoints))?;
        // Set the endpoints count metrics.
        cm.metrics.active_endpoints.set(
            cm.endpoints
                .as_ref()
                .map(|ep| ep.as_ref().len())
                .unwrap_or_default() as i64,
        );
        Ok(Arc::new(RwLock::new(cm)))
    }

    /// Returns a ClusterManager where the set of clusters is continuously
    /// updated based on responses from the provided updates channel.
    pub fn dynamic(
        base_logger: Logger,
        metrics_registry: &Registry,
        cluster_updates_rx: mpsc::Receiver<ClusterUpdate>,
        shutdown_rx: watch::Receiver<()>,
    ) -> MetricsResult<SharedClusterManager> {
        let log = base_logger.new(o!("source" => "cluster::ClusterManager"));

        let cluster_manager = Self::new(metrics_registry, None)?;
        let metrics = cluster_manager.metrics.clone();
        let cluster_manager = Arc::new(RwLock::new(cluster_manager));

        // Start a task in the background to receive cluster updates
        // and update the cluster manager's cluster set in turn.
        Self::spawn_updater(
            log.clone(),
            metrics,
            cluster_manager.clone(),
            cluster_updates_rx,
            shutdown_rx,
        );

        Ok(cluster_manager)
    }

    fn update_cluster_update_metrics(metrics: &Metrics, update: &ClusterUpdate) {
        metrics.active_clusters.set(update.len() as i64);
        metrics.active_endpoints.set(
            Self::create_endpoints_from_update(update)
                .map(|ep| ep.as_ref().len() as i64)
                .unwrap_or_default(),
        )
    }

    fn create_endpoints_from_update(update: &ClusterUpdate) -> Option<Endpoints> {
        // NOTE: We don't currently have support for consuming multiple clusters
        // so here gather all endpoints into the same set, ignoring what cluster they
        // belong to.
        let endpoints = update
            .iter()
            .fold(vec![], |mut endpoints, (_name, cluster)| {
                let cluster_endpoints = cluster
                    .localities
                    .iter()
                    .map(|(_, endpoints)| {
                        endpoints
                            .endpoints
                            .iter()
                            .map(|ep| Endpoint::from_address(ep.address))
                    })
                    .flatten();
                endpoints.extend(cluster_endpoints);

                endpoints
            });

        match Endpoints::new(endpoints) {
            Ok(endpoints) => Some(endpoints),
            Err(_empty_list_error) => None,
        }
    }

    /// Spawns a task to run a loop that receives cluster updates
    /// and updates the ClusterManager's state in turn.
    fn spawn_updater(
        log: Logger,
        metrics: Metrics,
        cluster_manager: Arc<RwLock<ClusterManager>>,
        mut cluster_updates_rx: mpsc::Receiver<ClusterUpdate>,
        mut shutdown_rx: watch::Receiver<()>,
    ) {
        tokio::spawn(async move {
            loop {
                tokio::select! {
                    update = cluster_updates_rx.recv() => {
                        match update {
                            Some(update) => {
                                Self::update_cluster_update_metrics(&metrics, &update);
                                let update = Self::create_endpoints_from_update(&update);
                                debug!(log, "Received a cluster update.");
                                cluster_manager.write().update(update);
                            }
                            None => {
                                warn!(log, "Exiting cluster update receive loop because the sender dropped the channel.");
                                return;
                            }
                        }
                    }
                    _ = shutdown_rx.changed() => {
                        debug!(log, "Exiting cluster update receive loop because a shutdown signal was received.");
                        return;
                    },
                }
            }
        });
    }
}

#[cfg(test)]
mod tests {
    use super::ClusterManager;
    use crate::cluster::{Cluster, Endpoint, LocalityEndpoints};
    use crate::config::Endpoints;
    use crate::test_utils::logger;
    use prometheus::Registry;
    use tokio::sync::{mpsc, watch};

    #[test]
    fn static_cluster_manager_metrics() {
        let cm = ClusterManager::fixed(
            &Registry::default(),
            Endpoints::new(vec![
                Endpoint::from_address("127.0.0.1:80".parse().unwrap()),
                Endpoint::from_address("127.0.0.1:81".parse().unwrap()),
            ])
            .unwrap(),
        )
        .unwrap();
        let metrics = &cm.read().metrics;
        assert_eq!(2, metrics.active_endpoints.get());
        assert_eq!(0, metrics.active_clusters.get());
    }

    #[tokio::test]
    async fn dynamic_cluster_manager_metrics() {
        let (update_tx, update_rx) = mpsc::channel(3);
        let (_shutdown_tx, shutdown_rx) = watch::channel(());
        let cm = ClusterManager::dynamic(logger(), &Registry::default(), update_rx, shutdown_rx)
            .unwrap();

        // Initialization metrics
        {
            let metrics = &cm.read().metrics;
            assert_eq!(0, metrics.active_endpoints.get());
            assert_eq!(0, metrics.active_clusters.get());
        }

        let update = vec![
            (
                "cluster-1".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![Endpoint::from_address(
                                "127.0.0.1:80".parse().unwrap(),
                            )],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
            (
                "cluster-2".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![
                                Endpoint::from_address("127.0.0.1:82".parse().unwrap()),
                                Endpoint::from_address("127.0.0.1:83".parse().unwrap()),
                            ],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
        ]
        .into_iter()
        .collect();
        update_tx.send(update).await.unwrap();

        // Check updated metrics
        tokio::time::timeout(std::time::Duration::from_secs(3), async move {
            // Wait for the update to be processed. Here just poll until there's
            // a change we expect (or we will timeout from the enclosing future eventually.
            loop {
                {
                    let metrics = &cm.read().metrics;
                    if metrics.active_endpoints.get() == 3 {
                        break;
                    }
                }
                tokio::time::sleep(std::time::Duration::from_millis(3)).await;
            }

            // Verify the new metrics are correct.
            {
                let metrics = &cm.read().metrics;
                assert_eq!(3, metrics.active_endpoints.get());
                assert_eq!(2, metrics.active_clusters.get());
            }
        })
        .await
        .unwrap();
    }
}
<commit_msg>Include endpoint token and metadata from cluster update (#359)<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::sync::Arc;

// We use a parking_lot since it's significantly faster under low contention
// and we will need to acquire a read lock with every packet that is processed
// to be able to capture the current endpoint state and pass it to Filters.
use parking_lot::RwLock;
use slog::{debug, o, warn, Logger};

use prometheus::{Registry, Result as MetricsResult};
use tokio::sync::{mpsc, watch};

use crate::cluster::Endpoint;
use crate::config::{Endpoints, UpstreamEndpoints};
use crate::xds::ads_client::ClusterUpdate;

use super::metrics::Metrics;

pub(crate) type SharedClusterManager = Arc<RwLock<ClusterManager>>;

/// ClusterManager knows about all clusters and endpoints.
pub(crate) struct ClusterManager {
    metrics: Metrics,
    endpoints: Option<Endpoints>,
}

/// InitializeError is returned with an error message if the
/// [`ClusterManager`] fails to initialize properly.
#[derive(Debug, thiserror::Error)]
pub enum InitializeError {
    #[error("{:?}", .0)]
    Message(String),
}

impl ClusterManager {
    fn new(metrics_registry: &Registry, endpoints: Option<Endpoints>) -> MetricsResult<Self> {
        let metrics = Metrics::new(metrics_registry)?;
        Ok(Self { metrics, endpoints })
    }

    fn update(&mut self, endpoints: Option<Endpoints>) {
        self.endpoints = endpoints;
    }

    /// Returns all endpoints known at the time of invocation.
    /// Returns `None` if there are no endpoints.
    pub fn get_all_endpoints(&self) -> Option<UpstreamEndpoints> {
        self.endpoints.clone().map(|ep| ep.into())
    }

    /// Returns a ClusterManager backed by the fixed set of clusters provided in the config.
    pub fn fixed(
        metrics_registry: &Registry,
        endpoints: Endpoints,
    ) -> MetricsResult<SharedClusterManager> {
        let cm = Self::new(metrics_registry, Some(endpoints))?;
        // Set the endpoints count metrics.
        cm.metrics.active_endpoints.set(
            cm.endpoints
                .as_ref()
                .map(|ep| ep.as_ref().len())
                .unwrap_or_default() as i64,
        );
        Ok(Arc::new(RwLock::new(cm)))
    }

    /// Returns a ClusterManager where the set of clusters is continuously
    /// updated based on responses from the provided updates channel.
    pub fn dynamic(
        base_logger: Logger,
        metrics_registry: &Registry,
        cluster_updates_rx: mpsc::Receiver<ClusterUpdate>,
        shutdown_rx: watch::Receiver<()>,
    ) -> MetricsResult<SharedClusterManager> {
        let log = base_logger.new(o!("source" => "cluster::ClusterManager"));

        let cluster_manager = Self::new(metrics_registry, None)?;
        let metrics = cluster_manager.metrics.clone();
        let cluster_manager = Arc::new(RwLock::new(cluster_manager));

        // Start a task in the background to receive cluster updates
        // and update the cluster manager's cluster set in turn.
        Self::spawn_updater(
            log.clone(),
            metrics,
            cluster_manager.clone(),
            cluster_updates_rx,
            shutdown_rx,
        );

        Ok(cluster_manager)
    }

    fn process_cluster_update(metrics: &Metrics, update: ClusterUpdate) -> Option<Endpoints> {
        let num_clusters = update.len() as i64;
        let update = Self::create_endpoints_from_update(update);
        let num_endpoints = update
            .as_ref()
            .map(|ep| ep.as_ref().len() as i64)
            .unwrap_or_default();
        Self::update_cluster_update_metrics(metrics, num_clusters, num_endpoints);
        update
    }

    fn update_cluster_update_metrics(metrics: &Metrics, num_clusters: i64, num_endpoints: i64) {
        metrics.active_clusters.set(num_clusters);
        metrics.active_endpoints.set(num_endpoints)
    }

    fn create_endpoints_from_update(update: ClusterUpdate) -> Option<Endpoints> {
        // NOTE: We don't currently have support for consuming multiple clusters
        // so here gather all endpoints into the same set, ignoring what cluster they
        // belong to.
        let endpoints = update
            .into_iter()
            .fold(vec![], |mut endpoints, (_name, cluster)| {
                let cluster_endpoints = cluster
                    .localities
                    .into_iter()
                    .map(|(_, endpoints)| {
                        endpoints
                            .endpoints
                            .into_iter()
                            .map(|ep| Endpoint::new(ep.address, ep.tokens, ep.metadata))
                    })
                    .flatten();
                endpoints.extend(cluster_endpoints);

                endpoints
            });

        match Endpoints::new(endpoints) {
            Ok(endpoints) => Some(endpoints),
            Err(_empty_list_error) => None,
        }
    }

    /// Spawns a task to run a loop that receives cluster updates
    /// and updates the ClusterManager's state in turn.
    fn spawn_updater(
        log: Logger,
        metrics: Metrics,
        cluster_manager: Arc<RwLock<ClusterManager>>,
        mut cluster_updates_rx: mpsc::Receiver<ClusterUpdate>,
        mut shutdown_rx: watch::Receiver<()>,
    ) {
        tokio::spawn(async move {
            loop {
                tokio::select! {
                    update = cluster_updates_rx.recv() => {
                        match update {
                            Some(update) => {
                                debug!(log, "Received a cluster update.");
                                cluster_manager.write().update(Self::process_cluster_update(&metrics, update));
                            }
                            None => {
                                warn!(log, "Exiting cluster update receive loop because the sender dropped the channel.");
                                return;
                            }
                        }
                    }
                    _ = shutdown_rx.changed() => {
                        debug!(log, "Exiting cluster update receive loop because a shutdown signal was received.");
                        return;
                    },
                }
            }
        });
    }
}

#[cfg(test)]
mod tests {
    use super::ClusterManager;
    use crate::cluster::{Cluster, Endpoint, LocalityEndpoints};
    use crate::config::Endpoints;
    use crate::test_utils::logger;
    use prometheus::Registry;
    use tokio::sync::{mpsc, watch};

    #[tokio::test]
    async fn dynamic_cluster_manager_process_cluster_update() {
        let (update_tx, update_rx) = mpsc::channel(3);
        let (_shutdown_tx, shutdown_rx) = watch::channel(());
        let cm = ClusterManager::dynamic(logger(), &Registry::default(), update_rx, shutdown_rx)
            .unwrap();

        let test_endpoints = vec![
            Endpoint::new(
                "127.0.0.1:80".parse().unwrap(),
                vec!["abc-0".into(), "xyz-0".into()].into_iter().collect(),
                Some(serde_json::json!({
                    "key-01": "value-01",
                    "key-02": "value-02",
                })),
            ),
            Endpoint::new(
                "127.0.0.1:82".parse().unwrap(),
                vec!["abc-2".into(), "xyz-2".into()].into_iter().collect(),
                Some(serde_json::json!({
                    "key-01": "value-01",
                    "key-02": "value-02",
                })),
            ),
            Endpoint::new(
                "127.0.0.1:83".parse().unwrap(),
                vec!["abc-3".into(), "xyz-3".into()].into_iter().collect(),
                None,
            ),
        ];

        let update = vec![
            (
                "cluster-1".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![test_endpoints[0].clone()],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
            (
                "cluster-2".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![test_endpoints[1].clone(), test_endpoints[2].clone()],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
        ]
        .into_iter()
        .collect();
        update_tx.send(update).await.unwrap();

        // Check the processed update.
        tokio::time::timeout(std::time::Duration::from_secs(3), async move {
            // Wait for the update to be processed. Here just poll until there's
            // a change we expect (or we will timeout from the enclosing future eventually.
            loop {
                let endpoints = { cm.read().endpoints.clone() };
                if let Some(endpoints) = endpoints {
                    let mut endpoints = endpoints.as_ref().clone();
                    endpoints.sort_by(|a, b| a.address.cmp(&b.address));
                    assert_eq!(endpoints, test_endpoints);
                    break;
                } else {
                    tokio::time::sleep(std::time::Duration::from_millis(10)).await;
                };
            }
        })
        .await
        .unwrap();
    }

    #[test]
    fn static_cluster_manager_metrics() {
        let cm = ClusterManager::fixed(
            &Registry::default(),
            Endpoints::new(vec![
                Endpoint::from_address("127.0.0.1:80".parse().unwrap()),
                Endpoint::from_address("127.0.0.1:81".parse().unwrap()),
            ])
            .unwrap(),
        )
        .unwrap();
        let metrics = &cm.read().metrics;
        assert_eq!(2, metrics.active_endpoints.get());
        assert_eq!(0, metrics.active_clusters.get());
    }

    #[tokio::test]
    async fn dynamic_cluster_manager_metrics() {
        let (update_tx, update_rx) = mpsc::channel(3);
        let (_shutdown_tx, shutdown_rx) = watch::channel(());
        let cm = ClusterManager::dynamic(logger(), &Registry::default(), update_rx, shutdown_rx)
            .unwrap();

        // Initialization metrics
        {
            let metrics = &cm.read().metrics;
            assert_eq!(0, metrics.active_endpoints.get());
            assert_eq!(0, metrics.active_clusters.get());
        }

        let update = vec![
            (
                "cluster-1".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![Endpoint::from_address(
                                "127.0.0.1:80".parse().unwrap(),
                            )],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
            (
                "cluster-2".into(),
                Cluster {
                    localities: vec![(
                        None,
                        LocalityEndpoints {
                            endpoints: vec![
                                Endpoint::from_address("127.0.0.1:82".parse().unwrap()),
                                Endpoint::from_address("127.0.0.1:83".parse().unwrap()),
                            ],
                        },
                    )]
                    .into_iter()
                    .collect(),
                },
            ),
        ]
        .into_iter()
        .collect();
        update_tx.send(update).await.unwrap();

        // Check updated metrics
        tokio::time::timeout(std::time::Duration::from_secs(3), async move {
            // Wait for the update to be processed. Here just poll until there's
            // a change we expect (or we will timeout from the enclosing future eventually.
            loop {
                {
                    let metrics = &cm.read().metrics;
                    if metrics.active_endpoints.get() == 3 {
                        break;
                    }
                }
                tokio::time::sleep(std::time::Duration::from_millis(3)).await;
            }

            // Verify the new metrics are correct.
            {
                let metrics = &cm.read().metrics;
                assert_eq!(3, metrics.active_endpoints.get());
                assert_eq!(2, metrics.active_clusters.get());
            }
        })
        .await
        .unwrap();
    }
}
<|endoftext|>


<commit_before>//! Performs constant folding on literals

use compiler::Warning;
use compiler::visit::*;
use compiler::ast::*;
use compiler::span::Spanned;
use op::*;

use std::num::{Int, Float};

struct Folder {
    warns: Vec<Warning>,
}

fn unary_err(op: UnOp, lit: &Literal) -> String {
    format!("attempt to apply unary {} to type \"{}\"", op, lit.get_type_str())
}

fn bin_err(lhs: &Literal, op: BinOp, rhs: &Literal) -> String {
    format!("attempt to apply binary {} to {} and {}", op, lhs.get_type_str(), rhs.get_type_str())
}

/// Folds a literal used as a truth value (eg. as an if condition or in a logical operator).
fn fold_truth(lit: &Literal) -> bool {
    match *lit {
        TInt(_) | TFloat(_) | TStr(_) | TBool(true) => true,
        TBool(false) | TNil => false,
    }
}

/// Tries to fold a unary operator applied to a literal.
///
/// Returns `Ok(Literal)` on success and `Err(String)` with an appropriate error message if the
/// type of the literal mismatched the operator's expected type or domain. This causes a warning to
/// be emitted.
fn fold_unop(op: UnOp, lit: &Literal) -> Result<Literal, String> {
    match op {
        UnOp::Negate => match *lit { // -
            TInt(i) => Ok(TInt(-i)),
            TFloat(f) => Ok(TFloat(-f)),
            TStr(_) | TBool(_) | TNil => Err(unary_err(op, lit)),
        },
        UnOp::LNot => match *lit { // ! / not
            TInt(_) | TFloat(_) | TStr(_) => Ok(TBool(false)),  // all these evaluate to true
            TBool(b) => Ok(TBool(!b)),
            TNil => Err(unary_err(op, lit)),
        },
        UnOp::BNot => match *lit { // ~
            TInt(i) => Ok(TInt(!i)),
            TFloat(_) | TStr(_) | TBool(_) | TNil => Err(unary_err(op, lit)),
        },
        UnOp::Len => match *lit { // #
            TInt(_) | TFloat(_) | TBool(_) | TNil => Err(unary_err(op, lit)),

            // # of graphemes is the way to go
            TStr(ref s) => Ok(TInt(s.as_slice().graphemes(true).count() as i64)),
        },
    }
}

/// `fold_unop` for binary operators
fn fold_binop(lhs: &Literal, op: BinOp, rhs: &Literal) -> Result<Literal, String> {
    match op {
        BinOp::Add => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i + j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 + j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i + j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i + j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Sub => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i - j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 - j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i - j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i - j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Mul => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i * j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 * j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i * j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i * j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Div => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i / j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 / j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i / j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i / j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Mod => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i % j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 % j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i % j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i % j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Pow => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => {
                if j > u32::max_value() as i64 {
                    Err(format!("exponent {} is out of range", j))
                } else {
                    Ok(TInt(i.pow(j as u32)))
                }
            },
            (&TInt(i), &TFloat(j)) => {
                Ok(TFloat((i as f64).powf(j)))
            },
            (&TFloat(i), &TInt(j)) => {
                if j > i32::max_value() as i64 {
                    Err(format!("exponent {} is out of range", j))
                } else {
                    Ok(TFloat(i.powi(j as i32)))
                }
            },
            (&TFloat(i), &TFloat(j)) => {
                Ok(TFloat(i.powf(j)))
            },
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Concat => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TInt(i), &TFloat(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TFloat(i), &TInt(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TFloat(i), &TFloat(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TStr(ref s), &TInt(i)) => Ok(TStr(format!("{}{}", s, i))),
            (&TInt(i), &TStr(ref s)) => Ok(TStr(format!("{}{}", i, s))),
            (&TStr(ref s), &TFloat(f)) => Ok(TStr(format!("{}{}", s, f))),
            (&TFloat(f), &TStr(ref s)) => Ok(TStr(format!("{}{}", f, s))),
            (&TStr(ref a), &TStr(ref b)) => Ok(TStr(format!("{}{}", a, b))),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Eq | BinOp::NEq | BinOp::NEqLua => {
            let res = match (lhs, rhs) {
                (&TInt(i), &TInt(j)) => i == j,
                (&TInt(i), &TFloat(j)) => i as f64 == j,
                (&TFloat(i), &TInt(j)) => i == j as f64,
                (&TFloat(i), &TFloat(j)) => i == j,
                (&TBool(i), &TBool(j)) => i == j,
                (&TStr(ref a), &TStr(ref b)) => a == b,
                (&TNil, &TNil) => true,
                _ => false,
            };
            if op == BinOp::Eq { Ok(TBool(res)) } else { Ok(TBool(!res)) }
        },
        BinOp::LEq => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i <= j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 <= j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i <= j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i <= j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a <= b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::GEq => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i >= j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 >= j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i >= j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i >= j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a >= b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Less => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i < j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool((i as f64) < j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i < j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i < j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a < b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Greater => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i > j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 > j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i > j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i > j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a > b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::LAnd | BinOp::LAndLua => {
            if fold_truth(lhs) {
                Ok(rhs.clone())
            } else {
                Ok(lhs.clone())
            }
        },
        BinOp::LOr | BinOp::LOrLua => {
            if fold_truth(lhs) {
                Ok(lhs.clone())
            } else {
                Ok(rhs.clone())
            }
        },
        _ => Err(bin_err(lhs, op, rhs)),
    }
}

impl Visitor for Folder {
    fn visit_expr(&mut self, mut e: Expr) -> Expr {
        e.value = match e.value {
            EBinOp(mut lhs, op, mut rhs) => {
                // TODO fold these too
                lhs = Box::new(self.visit_expr(*lhs));
                rhs = Box::new(self.visit_expr(*rhs));

                let lspan = lhs.span;
                let rspan = rhs.span;
                if let ELit(lit_lhs) = lhs.value {
                    if let ELit(lit_rhs) = rhs.value {
                        match fold_binop(&lit_lhs, op, &lit_rhs) {
                            Ok(newlit) => ELit(newlit),
                            Err(msg) => {
                                self.warns.push(Warning::new(e.span, msg));
                                EBinOp(Box::new(Spanned::new(lspan, ELit(lit_lhs))), op, Box::new(Spanned::new(rspan, ELit(lit_rhs))))
                            }
                        }
                    } else {
                        EBinOp(Box::new(Spanned::new(lspan, ELit(lit_lhs))), op, rhs)
                    }
                } else {
                    EBinOp(lhs, op, rhs)
                }
            },
            EUnOp(op, mut arg) => {
                arg = Box::new(self.visit_expr(*arg));

                let span = arg.span;
                if let ELit(lit) = arg.value {
                    match fold_unop(op, &lit) {
                        Ok(newlit) => ELit(newlit),
                        Err(msg) => {
                            self.warns.push(Warning::new(e.span, msg));
                            EUnOp(op, Box::new(Spanned::new(span, ELit(lit))))
                        }
                    }
                } else {
                    EUnOp(op, arg)
                }
            },
            _ => { return walk_expr(e, self) },
        };

        e
    }
}

pub fn run(mut main: Function) -> (Function, Vec<Warning>) {
    let mut v = Folder {
        warns: vec![],
    };

    main = walk_func(main, &mut v);
    (main, v.warns)
}

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

    /// Parses and folds `raw`, then parses `folded`. Asserts that the ASTs are equal.
    fn test(raw: &str, folded: &str) {
        let mut conf = CompileConfig::empty();
        conf.add_transform(run, LintMode::Warn);

        let main = parse_and_resolve(raw).unwrap();
        let (main, _warns) = apply_transforms(main, &conf).unwrap();
        let expected = parse_and_resolve(folded).unwrap();

        assert_eq!(main, expected);
    }

    #[test]
    fn basic() {
        test("return -0, -4.5, #\"TEST\"", "return 0, -4.5, 4");
        test("return 1-1+3*(1-1)/1^1+1", "return 1");
    }
}
<commit_msg>Finalized fold.rs<commit_after>//! Performs constant folding on literals

use compiler::Warning;
use compiler::visit::*;
use compiler::ast::*;
use compiler::span::Spanned;
use op::*;

use std::num::{Int, Float};
use std::i64::BITS;

struct Folder {
    warns: Vec<Warning>,
}

fn unary_err(op: UnOp, lit: &Literal) -> String {
    format!("attempt to apply unary {} to type \"{}\"", op, lit.get_type_str())
}

fn bin_err(lhs: &Literal, op: BinOp, rhs: &Literal) -> String {
    format!("attempt to apply binary {} to {} and {}", op, lhs.get_type_str(), rhs.get_type_str())
}

/// Folds a literal used as a truth value (eg. as an if condition or in a logical operator).
fn fold_truth(lit: &Literal) -> bool {
    match *lit {
        TInt(_) | TFloat(_) | TStr(_) | TBool(true) => true,
        TBool(false) | TNil => false,
    }
}

/// Tries to fold a unary operator applied to a literal.
///
/// Returns `Ok(Literal)` on success and `Err(String)` with an appropriate error message if the
/// type of the literal mismatched the operator's expected type or domain. This causes a warning to
/// be emitted.
fn fold_unop(op: UnOp, lit: &Literal) -> Result<Literal, String> {
    match op {
        UnOp::Negate => match *lit { // -
            TInt(i) => Ok(TInt(-i)),
            TFloat(f) => Ok(TFloat(-f)),
            TStr(_) | TBool(_) | TNil => Err(unary_err(op, lit)),
        },
        UnOp::LNot => match *lit { // ! / not
            TInt(_) | TFloat(_) | TStr(_) => Ok(TBool(false)),  // all these evaluate to true
            TBool(b) => Ok(TBool(!b)),
            TNil => Err(unary_err(op, lit)),
        },
        UnOp::BNot => match *lit { // ~
            TInt(i) => Ok(TInt(!i)),
            TFloat(_) | TStr(_) | TBool(_) | TNil => Err(unary_err(op, lit)),
        },
        UnOp::Len => match *lit { // #
            TInt(_) | TFloat(_) | TBool(_) | TNil => Err(unary_err(op, lit)),

            // # of graphemes is the way to go
            TStr(ref s) => Ok(TInt(s.as_slice().graphemes(true).count() as i64)),
        },
    }
}

/// `fold_unop` for binary operators
fn fold_binop(lhs: &Literal, op: BinOp, rhs: &Literal) -> Result<Literal, String> {
    match op {
        BinOp::Add => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i + j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 + j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i + j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i + j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Sub => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i - j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 - j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i - j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i - j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Mul => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i * j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 * j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i * j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i * j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Div => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i / j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 / j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i / j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i / j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Mod => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i % j)),
            (&TInt(i), &TFloat(j)) => Ok(TFloat(i as f64 % j)),
            (&TFloat(i), &TInt(j)) => Ok(TFloat(i % j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TFloat(i % j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Pow => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => {
                if j > u32::max_value() as i64 {
                    Err(format!("exponent {} is out of range", j))
                } else {
                    Ok(TInt(i.pow(j as u32)))
                }
            },
            (&TInt(i), &TFloat(j)) => {
                Ok(TFloat((i as f64).powf(j)))
            },
            (&TFloat(i), &TInt(j)) => {
                if j > i32::max_value() as i64 {
                    Err(format!("exponent {} is out of range", j))
                } else {
                    Ok(TFloat(i.powi(j as i32)))
                }
            },
            (&TFloat(i), &TFloat(j)) => {
                Ok(TFloat(i.powf(j)))
            },
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Concat => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TInt(i), &TFloat(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TFloat(i), &TInt(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TFloat(i), &TFloat(j)) => Ok(TStr(format!("{}{}", i, j))),
            (&TStr(ref s), &TInt(i)) => Ok(TStr(format!("{}{}", s, i))),
            (&TInt(i), &TStr(ref s)) => Ok(TStr(format!("{}{}", i, s))),
            (&TStr(ref s), &TFloat(f)) => Ok(TStr(format!("{}{}", s, f))),
            (&TFloat(f), &TStr(ref s)) => Ok(TStr(format!("{}{}", f, s))),
            (&TStr(ref a), &TStr(ref b)) => Ok(TStr(format!("{}{}", a, b))),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Eq | BinOp::NEq | BinOp::NEqLua => {
            let res = match (lhs, rhs) {
                (&TInt(i), &TInt(j)) => i == j,
                (&TInt(i), &TFloat(j)) => i as f64 == j,
                (&TFloat(i), &TInt(j)) => i == j as f64,
                (&TFloat(i), &TFloat(j)) => i == j,
                (&TBool(i), &TBool(j)) => i == j,
                (&TStr(ref a), &TStr(ref b)) => a == b,
                (&TNil, &TNil) => true,
                _ => false,
            };
            if op == BinOp::Eq { Ok(TBool(res)) } else { Ok(TBool(!res)) }
        },
        BinOp::LEq => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i <= j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 <= j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i <= j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i <= j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a <= b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::GEq => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i >= j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 >= j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i >= j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i >= j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a >= b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Less => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i < j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool((i as f64) < j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i < j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i < j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a < b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::Greater => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TBool(i > j)),
            (&TInt(i), &TFloat(j)) => Ok(TBool(i as f64 > j)),
            (&TFloat(i), &TInt(j)) => Ok(TBool(i > j as f64)),
            (&TFloat(i), &TFloat(j)) => Ok(TBool(i > j)),
            (&TStr(ref a), &TStr(ref b)) => Ok(TBool(a > b)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::LAnd | BinOp::LAndLua => {
            if fold_truth(lhs) {
                Ok(rhs.clone())
            } else {
                Ok(lhs.clone())
            }
        },
        BinOp::LOr | BinOp::LOrLua => {
            if fold_truth(lhs) {
                Ok(lhs.clone())
            } else {
                Ok(rhs.clone())
            }
        },
        BinOp::BAnd => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i & j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::BOr => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i | j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::BXor => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => Ok(TInt(i ^ j)),
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::ShiftL => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => if j <= BITS as i64 {
                Ok(TInt(i << j))
            } else {
                Ok(TInt(0))
            },
            _ => Err(bin_err(lhs, op, rhs)),
        },
        BinOp::ShiftR => match (lhs, rhs) {
            (&TInt(i), &TInt(j)) => if j <= BITS as i64 {
                Ok(TInt(i >> j))
            } else {
                Ok(TInt(0))
            },
            _ => Err(bin_err(lhs, op, rhs)),
        },
    }
}

impl Visitor for Folder {
    fn visit_expr(&mut self, mut e: Expr) -> Expr {
        e.value = match e.value {
            EBinOp(mut lhs, op, mut rhs) => {
                lhs = Box::new(self.visit_expr(*lhs));
                rhs = Box::new(self.visit_expr(*rhs));

                let lspan = lhs.span;
                let rspan = rhs.span;
                if let ELit(lit_lhs) = lhs.value {
                    if let ELit(lit_rhs) = rhs.value {
                        match fold_binop(&lit_lhs, op, &lit_rhs) {
                            Ok(newlit) => ELit(newlit),
                            Err(msg) => {
                                self.warns.push(Warning::new(e.span, msg));
                                EBinOp(Box::new(Spanned::new(lspan, ELit(lit_lhs))), op, Box::new(Spanned::new(rspan, ELit(lit_rhs))))
                            }
                        }
                    } else {
                        EBinOp(Box::new(Spanned::new(lspan, ELit(lit_lhs))), op, rhs)
                    }
                } else {
                    EBinOp(lhs, op, rhs)
                }
            },
            EUnOp(op, mut arg) => {
                arg = Box::new(self.visit_expr(*arg));

                let span = arg.span;
                if let ELit(lit) = arg.value {
                    match fold_unop(op, &lit) {
                        Ok(newlit) => ELit(newlit),
                        Err(msg) => {
                            self.warns.push(Warning::new(e.span, msg));
                            EUnOp(op, Box::new(Spanned::new(span, ELit(lit))))
                        }
                    }
                } else {
                    EUnOp(op, arg)
                }
            },
            _ => { return walk_expr(e, self) },
        };

        e
    }
}

pub fn run(mut main: Function) -> (Function, Vec<Warning>) {
    let mut v = Folder {
        warns: vec![],
    };

    main = walk_func(main, &mut v);
    (main, v.warns)
}

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

    /// Parses and folds `raw`, then parses `folded`. Asserts that the ASTs are equal.
    fn test(raw: &str, folded: &str) {
        let mut conf = CompileConfig::empty();
        conf.add_transform(run, LintMode::Warn);

        let main = parse_and_resolve(raw).unwrap();
        let (main, _warns) = apply_transforms(main, &conf).unwrap();
        let expected = parse_and_resolve(folded).unwrap();

        assert_eq!(main, expected);
    }

    #[test]
    fn basic() {
        test("return -0, -4.5, #\"TEST\"", "return 0, -4.5, 4");
        test("return 1-1+3*(1-1)/1^1+1", "return 1");
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use opengles::glx::{GLXFBConfig, GLXDrawable, GLXPixmap, GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DEPTH_SIZE, GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT, GLX_RGBA};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXChooseVisual, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetFBConfigs, glXGetProcAddress, glXGetFBConfigAttrib, glXGetFBConfigs};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx;
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::libc::{c_char, c_int, c_uint, c_void};
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCloseDisplay, XCreateGC, XCreateImage, XCreatePixmap};
use xlib::xlib::{XDefaultScreen, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This holds
/// a *strong* reference to the display and will close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    #[fixed_stack_segment]
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        unsafe {
            let display = XOpenDisplay(*metadata);

            // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
            // visual.
            let compositor_visual_info =
                NativeCompositingGraphicsContext::compositor_visual_info(display);

            NativePaintingGraphicsContext {
                display: display,
                visual_info: compositor_visual_info,
            }
        }
    }
}

impl Drop for NativePaintingGraphicsContext {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            let _ = XCloseDisplay(self.display);
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: GLXFBConfig,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    #[fixed_stack_segment]
    fn compositor_visual_info(display: *Display) -> *XVisualInfo {
        unsafe {
            let glx_display: *glx::Display = cast::transmute(display);
            let screen = XDefaultScreen(display);
            let attributes = [ GLX_RGBA, GLX_DEPTH_SIZE, 24, 0 ];
            let compositor_visual_info = glXChooseVisual(glx_display, screen, &attributes[0]);
            cast::transmute(compositor_visual_info)
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    #[fixed_stack_segment]
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        unsafe {
            // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
            // visual.
            let compositor_visual_info =
                NativeCompositingGraphicsContext::compositor_visual_info(display);
            let compositor_visual_id = (*compositor_visual_info).visualid;

            // Choose an FB config.
            let glx_display = cast::transmute(display);
            let screen = XDefaultScreen(display);
            let mut fbconfig_count = 0;
            let fbconfigs = glXGetFBConfigs(glx_display, screen, &mut fbconfig_count);
            let mut fbconfig_index = None;
            for i in range(0, fbconfig_count) {
                let fbconfig = *ptr::offset(fbconfigs, i as int);
                let visual_info = glXGetVisualFromFBConfig(glx_display, fbconfig);
                let visual_info: *XVisualInfo = cast::transmute(visual_info);
                if visual_info == ptr::null() || (*visual_info).visualid != compositor_visual_id {
                    continue
                }

                let mut value = 0;
                glXGetFBConfigAttrib(glx_display, fbconfig, GLX_DRAWABLE_TYPE, &mut value);
                if (value & GLX_PIXMAP_BIT) == 0 {
                    continue
                }

                glXGetFBConfigAttrib(glx_display,
                                     fbconfig,
                                     GLX_BIND_TO_TEXTURE_RGBA_EXT,
                                     &mut value);
                if value == 0 {
                    continue
                }

                fbconfig_index = Some(i);
                break
            }

            let framebuffer_configuration = match fbconfig_index {
                None => fail!("No appropriate framebuffer config found!"),
                Some(index) => *ptr::offset(fbconfigs, index as int),
            };

            NativeCompositingGraphicsContext {
                display: display,
                visual_info: compositor_visual_info,
                framebuffer_configuration: framebuffer_configuration,
            }
        }
    }
}

/// The X display string.
pub type NativeGraphicsMetadata = *c_char;

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    #[fixed_stack_segment]
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    #[fixed_stack_segment]
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);

            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       ((stride / size.width) * 8) as c_uint);

            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    #[fixed_stack_segment]
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];
            let glx_display = cast::transmute(native_context.display);
            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration,
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    #[fixed_stack_segment]
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    #[fixed_stack_segment]
    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<commit_msg>Warning police<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

//! Implementation of cross-process surfaces for Linux. This uses X pixmaps.

use platform::surface::NativeSurfaceMethods;
use texturegl::Texture;

use geom::size::Size2D;
use opengles::glx::{GLXFBConfig, GLXDrawable, GLX_BIND_TO_TEXTURE_RGBA_EXT};
use opengles::glx::{GLX_DEPTH_SIZE, GLX_DRAWABLE_TYPE, GLX_FRONT_EXT, GLX_PIXMAP_BIT, GLX_RGBA};
use opengles::glx::{GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT};
use opengles::glx::{GLX_TEXTURE_TARGET_EXT, glXChooseVisual, glXCreatePixmap, glXDestroyPixmap};
use opengles::glx::{glXGetProcAddress, glXGetFBConfigAttrib, glXGetFBConfigs};
use opengles::glx::{glXGetVisualFromFBConfig};
use opengles::glx;
use opengles::gl2::NO_ERROR;
use opengles::gl2;
use std::cast;
use std::libc::{c_char, c_int, c_uint, c_void};
use std::ptr;
use xlib::xlib::{Display, Pixmap, XCloseDisplay, XCreateGC, XCreateImage, XCreatePixmap};
use xlib::xlib::{XDefaultScreen, XFreePixmap, XGetGeometry, XOpenDisplay, XPutImage, XRootWindow};
use xlib::xlib::{XVisualInfo, ZPixmap};

/// The display and visual info. This is needed in order to upload on the painting side. This holds
/// a *strong* reference to the display and will close it when done.
///
/// FIXME(pcwalton): Mark nonsendable and noncopyable.
pub struct NativePaintingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
}

impl NativePaintingGraphicsContext {
    #[fixed_stack_segment]
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        unsafe {
            let display = XOpenDisplay(*metadata);

            // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
            // visual.
            let compositor_visual_info =
                NativeCompositingGraphicsContext::compositor_visual_info(display);

            NativePaintingGraphicsContext {
                display: display,
                visual_info: compositor_visual_info,
            }
        }
    }
}

impl Drop for NativePaintingGraphicsContext {
    #[fixed_stack_segment]
    fn drop(&mut self) {
        unsafe {
            let _ = XCloseDisplay(self.display);
        }
    }
}

/// The display, visual info, and framebuffer configuration. This is needed in order to bind to a
/// texture on the compositor side. This holds only a *weak* reference to the display and does not
/// close it.
///
/// FIXME(pcwalton): Unchecked weak references are bad and can violate memory safety. This is hard
/// to fix because the Display is given to us by the native windowing system, but we should fix it
/// someday.
///
/// FIXME(pcwalton): Mark nonsendable.
pub struct NativeCompositingGraphicsContext {
    display: *Display,
    visual_info: *XVisualInfo,
    framebuffer_configuration: GLXFBConfig,
}

impl NativeCompositingGraphicsContext {
    /// Chooses the compositor visual info using the same algorithm that the compositor uses.
    ///
    /// FIXME(pcwalton): It would be more robust to actually have the compositor pass the visual.
    #[fixed_stack_segment]
    fn compositor_visual_info(display: *Display) -> *XVisualInfo {
        unsafe {
            let glx_display: *glx::Display = cast::transmute(display);
            let screen = XDefaultScreen(display);
            let attributes = [ GLX_RGBA, GLX_DEPTH_SIZE, 24, 0 ];
            let compositor_visual_info = glXChooseVisual(glx_display, screen, &attributes[0]);
            cast::transmute(compositor_visual_info)
        }
    }

    /// Creates a native graphics context from the given X display connection. This uses GLX. Only
    /// the compositor is allowed to call this.
    #[fixed_stack_segment]
    pub fn from_display(display: *Display) -> NativeCompositingGraphicsContext {
        unsafe {
            // FIXME(pcwalton): It would be more robust to actually have the compositor pass the
            // visual.
            let compositor_visual_info =
                NativeCompositingGraphicsContext::compositor_visual_info(display);
            let compositor_visual_id = (*compositor_visual_info).visualid;

            // Choose an FB config.
            let glx_display = cast::transmute(display);
            let screen = XDefaultScreen(display);
            let mut fbconfig_count = 0;
            let fbconfigs = glXGetFBConfigs(glx_display, screen, &mut fbconfig_count);
            let mut fbconfig_index = None;
            for i in range(0, fbconfig_count) {
                let fbconfig = *ptr::offset(fbconfigs, i as int);
                let visual_info = glXGetVisualFromFBConfig(glx_display, fbconfig);
                let visual_info: *XVisualInfo = cast::transmute(visual_info);
                if visual_info == ptr::null() || (*visual_info).visualid != compositor_visual_id {
                    continue
                }

                let mut value = 0;
                glXGetFBConfigAttrib(glx_display, fbconfig, GLX_DRAWABLE_TYPE, &mut value);
                if (value & GLX_PIXMAP_BIT) == 0 {
                    continue
                }

                glXGetFBConfigAttrib(glx_display,
                                     fbconfig,
                                     GLX_BIND_TO_TEXTURE_RGBA_EXT,
                                     &mut value);
                if value == 0 {
                    continue
                }

                fbconfig_index = Some(i);
                break
            }

            let framebuffer_configuration = match fbconfig_index {
                None => fail!("No appropriate framebuffer config found!"),
                Some(index) => *ptr::offset(fbconfigs, index as int),
            };

            NativeCompositingGraphicsContext {
                display: display,
                visual_info: compositor_visual_info,
                framebuffer_configuration: framebuffer_configuration,
            }
        }
    }
}

/// The X display string.
pub type NativeGraphicsMetadata = *c_char;

#[deriving(Eq)]
pub enum NativeSurfaceTransientData {
    NoTransientData,
    RenderTaskTransientData(*Display, *XVisualInfo),
}

pub struct NativeSurface {
    /// The pixmap.
    pixmap: Pixmap,

    /// Whether this pixmap will leak if the destructor runs. This is for debugging purposes.
    will_leak: bool,
}

impl Drop for NativeSurface {
    fn drop(&mut self) {
        if self.will_leak {
            fail!("You should have disposed of the pixmap properly with destroy()! This pixmap \
                   will leak!");
        }
    }
}

impl NativeSurface {
    #[fixed_stack_segment]
    pub fn from_pixmap(pixmap: Pixmap) -> NativeSurface {
        NativeSurface {
            pixmap: pixmap,
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    #[fixed_stack_segment]
    fn new(native_context: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32)
           -> NativeSurface {
        unsafe {
            // Create the pixmap.
            let screen = XDefaultScreen(native_context.display);
            let window = XRootWindow(native_context.display, screen);

            let pixmap = XCreatePixmap(native_context.display,
                                       window,
                                       size.width as c_uint,
                                       size.height as c_uint,
                                       ((stride / size.width) * 8) as c_uint);

            NativeSurface::from_pixmap(pixmap)
        }
    }

    /// This may only be called on the compositor side.
    #[fixed_stack_segment]
    fn bind_to_texture(&self,
                       native_context: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       _size: Size2D<int>) {
        unsafe {
            // Create the GLX pixmap.
            //
            // FIXME(pcwalton): RAII for exception safety?
            let pixmap_attributes = [
                GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
                GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
                0
            ];
            let glx_display = cast::transmute(native_context.display);
            let glx_pixmap = glXCreatePixmap(glx_display,
                                             native_context.framebuffer_configuration,
                                             self.pixmap,
                                             &pixmap_attributes[0]);

            let glXBindTexImageEXT: extern "C" fn(*Display, GLXDrawable, c_int, *c_int) =
                cast::transmute(glXGetProcAddress(cast::transmute(&"glXBindTexImageEXT\x00"[0])));
            assert!(glXBindTexImageEXT as *c_void != ptr::null());
            let _bound = texture.bind();
            glXBindTexImageEXT(native_context.display,
                               cast::transmute(glx_pixmap),
                               GLX_FRONT_EXT,
                               ptr::null());
            assert_eq!(gl2::get_error(), NO_ERROR);

            // FIXME(pcwalton): Recycle these for speed?
            glXDestroyPixmap(glx_display, glx_pixmap);
        }
    }

    /// This may only be called on the painting side.
    #[fixed_stack_segment]
    fn upload(&self, graphics_context: &NativePaintingGraphicsContext, data: &[u8]) {
        unsafe {
            // Ensure that we're running on the render task. Take the display.
            let pixmap = self.pixmap;

            // Figure out the width, height, and depth of the pixmap.
            let mut root_window = 0;
            let mut x = 0;
            let mut y = 0;
            let mut width = 0;
            let mut height = 0;
            let mut border_width = 0;
            let mut depth = 0;
            let _ = XGetGeometry(graphics_context.display,
                                 cast::transmute(pixmap),
                                 &mut root_window,
                                 &mut x,
                                 &mut y,
                                 &mut width,
                                 &mut height,
                                 &mut border_width,
                                 &mut depth);

            // Create the image.
            let image = XCreateImage(graphics_context.display,
                                     (*graphics_context.visual_info).visual,
                                     depth,
                                     ZPixmap,
                                     0,
                                     cast::transmute(&data[0]),
                                     width as c_uint,
                                     height as c_uint,
                                     32,
                                     0);

            // Create the X graphics context.
            let gc = XCreateGC(graphics_context.display, pixmap, 0, ptr::null());

            // Draw the image.
            let _ = XPutImage(graphics_context.display,
                              pixmap,
                              gc,
                              image,
                              0,
                              0,
                              0,
                              0,
                              width,
                              height);
        }
    }

    fn get_id(&self) -> int {
        self.pixmap as int
    }

    #[fixed_stack_segment]
    fn destroy(&mut self, graphics_context: &NativePaintingGraphicsContext) {
        unsafe {
            assert!(self.pixmap != 0);
            XFreePixmap(graphics_context.display, self.pixmap);
            self.mark_wont_leak()
        }
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<|endoftext|>


<commit_before>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]

/// Scheme compiler integration tests
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd).

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;
use svm::Inst::*;

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn compile_list_creation() {
    assert_eq!(
        scheme::compile("(cons 10 (cons 20 nil))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 20 (cons 10 nil)))
/// ```
#[test]
fn  compile_list_car() {
    assert_eq!(
        scheme::compile("(car (cons 20 (cons 10 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CAR)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 20 (cons 10 nil)))
/// ```
#[test]
fn compile_list_cdr() {
    assert_eq!(
        scheme::compile("(cdr (cons 20 (cons 10 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
        ))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn compile_simple_add(){
    assert_eq!(
        scheme::compile("(+ 10 10)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn compile_nested_arith() {
     assert_eq!(
        scheme::compile("(- 20 (+ 5 5))"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(ADD),
            InstCell(LDC), AtomCell(SInt(20)),
            InstCell(SUB)
        ))
    );
}

/*
/// Tests for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ```
#[test]
fn compile_basic_branching_1() {
    assert_eq!(
        scheme::compile("((if (= 0 (- 1 1)) #t #f)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(0)),
            InstCell(EQ),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
                ListCell(box list!(InstCell(NIL), InstCell(JOIN))
            )
        ))
    );
}
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ```
#[test]
fn compile_basic_branching_2() {
    assert_eq!(
        scheme::compile("(+ 10 (if (nil? nil) 10 20))"),
        Ok(list!(
            InstCell(NIL), InstCell(NULL),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN))),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN))),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}
*/
<commit_msg>Reenable branching tests<commit_after>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]

/// Scheme compiler integration tests
///
/// These are based on the sample programs in Zach Allaun's Clojure SECD
/// [implementation](https://github.com/zachallaun/secd).

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;
use svm::Inst::*;

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ```
#[test]
fn compile_list_creation() {
    assert_eq!(
        scheme::compile("(cons 10 (cons 20 nil))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (car (cons 20 (cons 10 nil)))
/// ```
#[test]
fn  compile_list_car() {
    assert_eq!(
        scheme::compile("(car (cons 20 (cons 10 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CAR)
        ))
    );
}

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 20 (cons 10 nil)))
/// ```
#[test]
fn compile_list_cdr() {
    assert_eq!(
        scheme::compile("(cdr (cons 20 (cons 10 nil)))"),
        Ok(list!(
            InstCell(NIL),
            InstCell(LDC), AtomCell(SInt(10)), InstCell(CONS),
            InstCell(LDC), AtomCell(SInt(20)), InstCell(CONS),
            InstCell(CDR)
        ))
    );
}

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ```
#[test]
fn compile_simple_add(){
    assert_eq!(
        scheme::compile("(+ 10 10)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ```
#[test]
fn compile_nested_arith() {
     assert_eq!(
        scheme::compile("(- 20 (+ 5 5))"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(LDC), AtomCell(SInt(5)),
            InstCell(ADD),
            InstCell(LDC), AtomCell(SInt(20)),
            InstCell(SUB)
        ))
    );
}

/// Tests for basic branching
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ```
#[test]
fn compile_basic_branching_1() {
    assert_eq!(
        scheme::compile("((if (= 0 (- 1 1)) #t #f)"),
        Ok(list!(
            InstCell(LDC), AtomCell(SInt(1)), InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(0)),
            InstCell(EQ),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(1)), InstCell(JOIN))),
                ListCell(box list!(InstCell(NIL), InstCell(JOIN))
            )
        ))
    );
}
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ```
#[test]
fn compile_basic_branching_2() {
    assert_eq!(
        scheme::compile("(+ 10 (if (nil? nil) 10 20))"),
        Ok(list!(
            InstCell(NIL), InstCell(NULL),
            InstCell(SEL),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(10)), InstCell(JOIN))),
                ListCell(box list!(InstCell(LDC), AtomCell(SInt(20)), InstCell(JOIN))),
            InstCell(LDC), AtomCell(SInt(10)),
            InstCell(ADD)
        ))
    );
}
<|endoftext|>


<commit_before>// Copyright 2018 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The time oracle service for Exonum.
//!
//! See [the Exonum documentation][docs:time] for a high-level overview of the service,
//! in particular, its design rationale and the proof of correctness.
//!
//! [docs:time]: https://exonum.com/doc/advanced/time

#![deny(missing_debug_implementations, missing_docs)]

extern crate bodyparser;
extern crate chrono;
#[macro_use]
extern crate exonum;
#[macro_use]
extern crate failure;
extern crate iron;
extern crate router;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

use exonum::api::Api;
use exonum::blockchain::{ApiContext, ExecutionError, Service, ServiceContext, Transaction,
                         TransactionSet};
use exonum::crypto::Hash;
use exonum::encoding;
use exonum::encoding::serialize::json::reexport::Value;
use exonum::helpers::fabric::{Context, ServiceFactory};
use exonum::messages::RawTransaction;
use exonum::storage::{Fork, Snapshot};

use iron::Handler;
use router::Router;
use schema::TimeSchema;
use time_provider::{SystemTimeProvider, TimeProvider};
use transactions::*;

/// Node API.
pub mod api;
/// Database schema.
pub mod schema;
/// Node transactions.
pub mod transactions;
/// System time provider.
pub mod time_provider;

/// Time service id.
pub const SERVICE_ID: u16 = 4;
/// Time service name.
pub const SERVICE_NAME: &str = "exonum_time";

impl From<Error> for ExecutionError {
    fn from(value: Error) -> ExecutionError {
        ExecutionError::new(value as u8)
    }
}

/// Define the service.
#[derive(Debug)]
pub struct TimeService {
    /// Current time.
    time: Box<TimeProvider>,
}

impl Default for TimeService {
    fn default() -> TimeService {
        TimeService {
            time: Box::new(SystemTimeProvider) as Box<TimeProvider>,
        }
    }
}

impl TimeService {
    /// Create a new `TimeService`.
    pub fn new() -> TimeService {
        TimeService::default()
    }

    /// Create a new `TimeService` with time provider `T`.
    pub fn with_provider<T: Into<Box<TimeProvider>>>(time_provider: T) -> TimeService {
        TimeService {
            time: time_provider.into(),
        }
    }
}

impl Service for TimeService {
    fn service_name(&self) -> &str {
        SERVICE_NAME
    }

    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash> {
        let schema = TimeSchema::new(snapshot);
        schema.state_hash()
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        TimeTransactions::tx_from_raw(raw).map(Into::into)
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Creates transaction after commit of the block.
    fn handle_commit(&self, context: &ServiceContext) {
        // The transaction must be created by the validator.
        if context.validator_id().is_none() {
            return;
        }
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context
            .transaction_sender()
            .send(Box::new(TxTime::new(
                self.time.current_time(),
                &pub_key,
                &sec_key,
            )))
            .unwrap();
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PrivateApi {
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PublicApi {
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A time service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct TimeServiceFactory;

impl ServiceFactory for TimeServiceFactory {
    fn make_service(&mut self, _: &Context) -> Box<Service> {
        Box::new(TimeService::new())
    }
}
<commit_msg>Order modules to comply with conventions. (#614)<commit_after>// Copyright 2018 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The time oracle service for Exonum.
//!
//! See [the Exonum documentation][docs:time] for a high-level overview of the service,
//! in particular, its design rationale and the proof of correctness.
//!
//! [docs:time]: https://exonum.com/doc/advanced/time

#![deny(missing_debug_implementations, missing_docs)]

extern crate bodyparser;
extern crate chrono;
#[macro_use]
extern crate exonum;
#[macro_use]
extern crate failure;
extern crate iron;
extern crate router;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

/// Node API.
pub mod api;
/// Database schema.
pub mod schema;
/// Node transactions.
pub mod transactions;
/// System time provider.
pub mod time_provider;

use exonum::api::Api;
use exonum::blockchain::{ApiContext, ExecutionError, Service, ServiceContext, Transaction,
                         TransactionSet};
use exonum::crypto::Hash;
use exonum::encoding;
use exonum::encoding::serialize::json::reexport::Value;
use exonum::helpers::fabric::{Context, ServiceFactory};
use exonum::messages::RawTransaction;
use exonum::storage::{Fork, Snapshot};

use iron::Handler;
use router::Router;
use schema::TimeSchema;
use time_provider::{SystemTimeProvider, TimeProvider};
use transactions::*;

/// Time service id.
pub const SERVICE_ID: u16 = 4;
/// Time service name.
pub const SERVICE_NAME: &str = "exonum_time";

impl From<Error> for ExecutionError {
    fn from(value: Error) -> ExecutionError {
        ExecutionError::new(value as u8)
    }
}

/// Define the service.
#[derive(Debug)]
pub struct TimeService {
    /// Current time.
    time: Box<TimeProvider>,
}

impl Default for TimeService {
    fn default() -> TimeService {
        TimeService {
            time: Box::new(SystemTimeProvider) as Box<TimeProvider>,
        }
    }
}

impl TimeService {
    /// Create a new `TimeService`.
    pub fn new() -> TimeService {
        TimeService::default()
    }

    /// Create a new `TimeService` with time provider `T`.
    pub fn with_provider<T: Into<Box<TimeProvider>>>(time_provider: T) -> TimeService {
        TimeService {
            time: time_provider.into(),
        }
    }
}

impl Service for TimeService {
    fn service_name(&self) -> &str {
        SERVICE_NAME
    }

    fn state_hash(&self, snapshot: &Snapshot) -> Vec<Hash> {
        let schema = TimeSchema::new(snapshot);
        schema.state_hash()
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        TimeTransactions::tx_from_raw(raw).map(Into::into)
    }

    fn initialize(&self, _fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Creates transaction after commit of the block.
    fn handle_commit(&self, context: &ServiceContext) {
        // The transaction must be created by the validator.
        if context.validator_id().is_none() {
            return;
        }
        let (pub_key, sec_key) = (*context.public_key(), context.secret_key().clone());
        context
            .transaction_sender()
            .send(Box::new(TxTime::new(
                self.time.current_time(),
                &pub_key,
                &sec_key,
            )))
            .unwrap();
    }

    fn private_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PrivateApi {
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }

    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = api::PublicApi {
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}

/// A time service creator for the `NodeBuilder`.
#[derive(Debug)]
pub struct TimeServiceFactory;

impl ServiceFactory for TimeServiceFactory {
    fn make_service(&mut self, _: &Context) -> Box<Service> {
        Box::new(TimeService::new())
    }
}
<|endoftext|>


<commit_before>use std::num::NonZeroU8;
use std::{fmt, ops};

/// Interests used in registering.
///
/// Interests are used in [registering] [`event::Source`]s with [`Poll`], they
/// indicate what readiness should be monitored for. For example if a socket is
/// registered with [readable] interests and the socket becomes writable, no
/// event will be returned from a call to [`poll`].
///
/// The size of `Option<Interests>` should be identical to itself.
///
/// ```
/// use std::mem::size_of;
/// use mio::Interests;
///
/// assert_eq!(size_of::<Option<Interests>>(), size_of::<Interests>());
/// ```
///
/// [registering]: crate::Registry::register
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [readable]: Interests::READABLE
/// [`poll`]: crate::Poll::poll
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Interests(NonZeroU8);

// These must be unique.
const READABLE: u8 = 0b0_001;
const WRITABLE: u8 = 0b0_010;
// The following are not available on all platforms.
#[cfg_attr(
    not(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    )),
    allow(dead_code)
)]
const AIO: u8 = 0b0_100;
#[cfg_attr(not(target_os = "freebsd"), allow(dead_code))]
const LIO: u8 = 0b1_000;

impl Interests {
    /// Returns a `Interests` set representing readable interests.
    pub const READABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(READABLE) });

    /// Returns a `Interests` set representing writable interests.
    pub const WRITABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(WRITABLE) });

    /// Returns a `Interests` set representing AIO completion interests.
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    ))]
    pub const AIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(AIO) });

    /// Returns a `Interests` set representing LIO completion interests.
    #[cfg(target_os = "freebsd")]
    pub const LIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(LIO) });

    /// Add together two `Interests`.
    ///
    /// This does the same thing as the `BitOr` implementation, but is a
    /// constant function.
    ///
    /// ```
    /// use mio::Interests;
    ///
    /// const INTERESTS: Interests = Interests::READABLE.add(Interests::WRITABLE);
    /// # fn silent_dead_code_warning(_: Interests) { }
    /// # silent_dead_code_warning(INTERESTS)
    /// ```
    #[allow(clippy::should_implement_trait)]
    pub const fn add(self, other: Interests) -> Interests {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }

    /// Returns true if the value includes readable readiness.
    pub const fn is_readable(self) -> bool {
        (self.0.get() & READABLE) != 0
    }

    /// Returns true if the value includes writable readiness.
    pub const fn is_writable(self) -> bool {
        (self.0.get() & WRITABLE) != 0
    }

    /// Returns true if `Interests` contains AIO readiness
    pub const fn is_aio(self) -> bool {
        (self.0.get() & AIO) != 0
    }

    /// Returns true if `Interests` contains LIO readiness
    pub const fn is_lio(self) -> bool {
        (self.0.get() & LIO) != 0
    }
}

impl ops::BitOr for Interests {
    type Output = Self;

    #[inline]
    fn bitor(self, other: Self) -> Self {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }
}

impl ops::BitOrAssign for Interests {
    #[inline]
    fn bitor_assign(&mut self, other: Self) {
        self.0 = (*self | other).0;
    }
}

impl fmt::Debug for Interests {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut one = false;
        if self.is_readable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "READABLE")?;
            one = true
        }
        if self.is_writable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "WRITABLE")?;
            one = true
        }
        #[cfg(any(
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "ios",
            target_os = "macos"
        ))]
        {
            if self.is_aio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "AIO")?;
                one = true
            }
        }
        #[cfg(any(target_os = "freebsd"))]
        {
            if self.is_lio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "LIO")?;
                one = true
            }
        }
        debug_assert!(one, "printing empty interests");
        Ok(())
    }
}
<commit_msg>Remove size example from Interests<commit_after>use std::num::NonZeroU8;
use std::{fmt, ops};

/// Interests used in registering.
///
/// Interests are used in [registering] [`event::Source`]s with [`Poll`], they
/// indicate what readiness should be monitored for. For example if a socket is
/// registered with [readable] interests and the socket becomes writable, no
/// event will be returned from a call to [`poll`].
///
/// [registering]: crate::Registry::register
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [readable]: Interests::READABLE
/// [`poll`]: crate::Poll::poll
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Interests(NonZeroU8);

// These must be unique.
const READABLE: u8 = 0b0_001;
const WRITABLE: u8 = 0b0_010;
// The following are not available on all platforms.
#[cfg_attr(
    not(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    )),
    allow(dead_code)
)]
const AIO: u8 = 0b0_100;
#[cfg_attr(not(target_os = "freebsd"), allow(dead_code))]
const LIO: u8 = 0b1_000;

impl Interests {
    /// Returns a `Interests` set representing readable interests.
    pub const READABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(READABLE) });

    /// Returns a `Interests` set representing writable interests.
    pub const WRITABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(WRITABLE) });

    /// Returns a `Interests` set representing AIO completion interests.
    #[cfg(any(
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "ios",
        target_os = "macos"
    ))]
    pub const AIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(AIO) });

    /// Returns a `Interests` set representing LIO completion interests.
    #[cfg(target_os = "freebsd")]
    pub const LIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(LIO) });

    /// Add together two `Interests`.
    ///
    /// This does the same thing as the `BitOr` implementation, but is a
    /// constant function.
    ///
    /// ```
    /// use mio::Interests;
    ///
    /// const INTERESTS: Interests = Interests::READABLE.add(Interests::WRITABLE);
    /// # fn silent_dead_code_warning(_: Interests) { }
    /// # silent_dead_code_warning(INTERESTS)
    /// ```
    #[allow(clippy::should_implement_trait)]
    pub const fn add(self, other: Interests) -> Interests {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }

    /// Returns true if the value includes readable readiness.
    pub const fn is_readable(self) -> bool {
        (self.0.get() & READABLE) != 0
    }

    /// Returns true if the value includes writable readiness.
    pub const fn is_writable(self) -> bool {
        (self.0.get() & WRITABLE) != 0
    }

    /// Returns true if `Interests` contains AIO readiness
    pub const fn is_aio(self) -> bool {
        (self.0.get() & AIO) != 0
    }

    /// Returns true if `Interests` contains LIO readiness
    pub const fn is_lio(self) -> bool {
        (self.0.get() & LIO) != 0
    }
}

impl ops::BitOr for Interests {
    type Output = Self;

    #[inline]
    fn bitor(self, other: Self) -> Self {
        Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
    }
}

impl ops::BitOrAssign for Interests {
    #[inline]
    fn bitor_assign(&mut self, other: Self) {
        self.0 = (*self | other).0;
    }
}

impl fmt::Debug for Interests {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut one = false;
        if self.is_readable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "READABLE")?;
            one = true
        }
        if self.is_writable() {
            if one {
                write!(fmt, " | ")?
            }
            write!(fmt, "WRITABLE")?;
            one = true
        }
        #[cfg(any(
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "ios",
            target_os = "macos"
        ))]
        {
            if self.is_aio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "AIO")?;
                one = true
            }
        }
        #[cfg(any(target_os = "freebsd"))]
        {
            if self.is_lio() {
                if one {
                    write!(fmt, " | ")?
                }
                write!(fmt, "LIO")?;
                one = true
            }
        }
        debug_assert!(one, "printing empty interests");
        Ok(())
    }
}
<|endoftext|>