<commit_before>use std::fmt::{self, Display};

pub type Result<I> = ::std::result::Result<I, Error>;

/// askama error type
///
/// # Feature Interaction
///
/// If the feature `serde_json` is enabled an
/// additional error variant `Json` is added.
///
/// # Why not `failure`/`error-chain`?
///
/// Error from `error-chain` are not `Sync` which
/// can lead to problems e.g. when this is used
/// by a crate which use `failure`. Implementing
/// `Fail` on the other hand prevents the implementation
/// of `std::error::Error` until specialization lands
/// on stable. While errors impl. `Fail` can be
/// converted to a type impl. `std::error::Error`
/// using a adapter the benefits `failure` would
/// bring to this crate are small, which is why
/// `std::error::Error` was used.
///
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
    /// formatting error
    Fmt(fmt::Error),

    /// json conversion error
    #[cfg(feature = "serde_json")]
    Json(::serde_json::Error),

    /// yaml conversion error
    #[cfg(feature = "serde_yaml")]
    Yaml(::serde_yaml::Error),
}

impl std::error::Error for Error {
    fn cause(&self) -> Option<&dyn std::error::Error> {
        match *self {
            Error::Fmt(ref err) => err.source(),
            #[cfg(feature = "serde_json")]
            Error::Json(ref err) => err.source(),
            #[cfg(feature = "serde_yaml")]
            Error::Yaml(ref err) => err.source(),
            _ => None,
        }
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Fmt(ref err) => write!(formatter, "formatting error: {}", err),
            #[cfg(feature = "serde_json")]
            Error::Json(ref err) => write!(formatter, "json conversion error: {}", err),
            #[cfg(feature = "serde_yaml")]
            Error::Yaml(ref err) => write!(formatter, "yaml conversion error: {}", err),
            _ => write!(formatter, "unknown error: __Nonexhaustive"),
        }
    }
}

impl From<fmt::Error> for Error {
    fn from(err: fmt::Error) -> Self {
        Error::Fmt(err)
    }
}

#[cfg(feature = "serde_json")]
impl From<::serde_json::Error> for Error {
    fn from(err: ::serde_json::Error) -> Self {
        Error::Json(err)
    }
}

#[cfg(feature = "serde_yaml")]
impl From<::serde_yaml::Error> for Error {
    fn from(err: ::serde_yaml::Error) -> Self {
        Error::Yaml(err)
    }
}

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

    trait AssertSendSyncStatic: Send + Sync + 'static {}
    impl AssertSendSyncStatic for Error {}
}
<commit_msg>Remove now unused match arms<commit_after>use std::fmt::{self, Display};

pub type Result<I> = ::std::result::Result<I, Error>;

/// askama error type
///
/// # Feature Interaction
///
/// If the feature `serde_json` is enabled an
/// additional error variant `Json` is added.
///
/// # Why not `failure`/`error-chain`?
///
/// Error from `error-chain` are not `Sync` which
/// can lead to problems e.g. when this is used
/// by a crate which use `failure`. Implementing
/// `Fail` on the other hand prevents the implementation
/// of `std::error::Error` until specialization lands
/// on stable. While errors impl. `Fail` can be
/// converted to a type impl. `std::error::Error`
/// using a adapter the benefits `failure` would
/// bring to this crate are small, which is why
/// `std::error::Error` was used.
///
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
    /// formatting error
    Fmt(fmt::Error),

    /// json conversion error
    #[cfg(feature = "serde_json")]
    Json(::serde_json::Error),

    /// yaml conversion error
    #[cfg(feature = "serde_yaml")]
    Yaml(::serde_yaml::Error),
}

impl std::error::Error for Error {
    fn cause(&self) -> Option<&dyn std::error::Error> {
        match *self {
            Error::Fmt(ref err) => err.source(),
            #[cfg(feature = "serde_json")]
            Error::Json(ref err) => err.source(),
            #[cfg(feature = "serde_yaml")]
            Error::Yaml(ref err) => err.source(),
        }
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Fmt(ref err) => write!(formatter, "formatting error: {}", err),
            #[cfg(feature = "serde_json")]
            Error::Json(ref err) => write!(formatter, "json conversion error: {}", err),
            #[cfg(feature = "serde_yaml")]
            Error::Yaml(ref err) => write!(formatter, "yaml conversion error: {}", err),
        }
    }
}

impl From<fmt::Error> for Error {
    fn from(err: fmt::Error) -> Self {
        Error::Fmt(err)
    }
}

#[cfg(feature = "serde_json")]
impl From<::serde_json::Error> for Error {
    fn from(err: ::serde_json::Error) -> Self {
        Error::Json(err)
    }
}

#[cfg(feature = "serde_yaml")]
impl From<::serde_yaml::Error> for Error {
    fn from(err: ::serde_yaml::Error) -> Self {
        Error::Yaml(err)
    }
}

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

    trait AssertSendSyncStatic: Send + Sync + 'static {}
    impl AssertSendSyncStatic for Error {}
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#![feature(cfg_target_feature)]
#![feature(test)]
extern crate leveldb;
extern crate rand;
extern crate test;

macro_rules! bench_base {
    ($bench_name:ident, $fname:ident, $block_size:expr) => {
        #[bench]
        fn $bench_name(b: &mut ::test::Bencher) {
            let block_data = vec!['x' as u8; $block_size];
            b.bytes = block_data.len() as u64;
            b.iter(|| {
                ::leveldb::util::crc32c::$fname(0, &block_data);
            })
        }
    };
}

macro_rules! bench_sw {
    ($bench_name:ident, $block_size:expr) => {
        bench_base!($bench_name, extend_sw, $block_size);
    };
}

macro_rules! bench_hw {
    ($bench_name:ident, $block_size:expr) => {
        #[cfg(target_feature = "sse4.2")]
        bench_base!($bench_name, extend_hw, $block_size);
    };
}

bench_sw!(sw_00000256, 00000256);
bench_sw!(sw_00004096, 00004096);
bench_sw!(sw_00060056, 00060056);
bench_sw!(sw_01048576, 01048576);
bench_sw!(sw_16777216, 16777216);

bench_hw!(hw_00000256, 00000256);
bench_hw!(hw_00004096, 00004096);
bench_hw!(hw_00060056, 00060056);
bench_hw!(hw_01048576, 01048576);
bench_hw!(hw_16777216, 16777216);
<commit_msg>Fix benchmark (#13)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#![feature(test)]
extern crate leveldb;
extern crate rand;
extern crate test;

macro_rules! bench_sw {
    ($bench_name:ident, $block_size:expr) => {
        #[bench]
        fn $bench_name(b: &mut ::test::Bencher) {
            let block_data = vec!['x' as u8; $block_size];
            b.bytes = block_data.len() as u64;
            b.iter(|| {
                ::leveldb::util::crc32c::extend_sw(0, &block_data);
            })
        }
    };
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[target_feature(enable = "sse4.2")]
macro_rules! bench_hw {
    ($bench_name:ident, $block_size:expr) => {
        #[bench]
        fn $bench_name(b: &mut ::test::Bencher) {
            let block_data = vec!['x' as u8; $block_size];
            b.bytes = block_data.len() as u64;
            b.iter(|| unsafe {
                ::leveldb::util::crc32c::extend_hw(0, &block_data);
            })
        }
    };
}

bench_sw!(sw_00000256, 00000256);
bench_sw!(sw_00004096, 00004096);
bench_sw!(sw_00060056, 00060056);
bench_sw!(sw_01048576, 01048576);
bench_sw!(sw_16777216, 16777216);

bench_hw!(hw_00000256, 00000256);
bench_hw!(hw_00004096, 00004096);
bench_hw!(hw_00060056, 00060056);
bench_hw!(hw_01048576, 01048576);
bench_hw!(hw_16777216, 16777216);
<|endoftext|>


<commit_before>use std::os::raw::{c_int, c_void};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, ThreadId};

use wayland_commons::utils::UserData;
use wayland_commons::wire::ArgumentType;
use wayland_commons::MessageGroup;
use {Interface, Proxy};

use super::EventQueueInner;

use wayland_sys::client::*;
use wayland_sys::common::*;

pub struct ProxyInternal {
    alive: AtomicBool,
    user_data: UserData,
    queue_thread: ThreadId,
}

impl ProxyInternal {
    pub fn new(user_data: UserData, thread_id: ThreadId) -> ProxyInternal {
        ProxyInternal {
            alive: AtomicBool::new(true),
            user_data,
            queue_thread: thread_id,
        }
    }
}

#[derive(Clone)]
pub(crate) struct ProxyInner {
    internal: Option<Arc<ProxyInternal>>,
    ptr: *mut wl_proxy,
    wrapping: Option<ThreadId>,
}

unsafe impl Send for ProxyInner {}
unsafe impl Sync for ProxyInner {}

impl ProxyInner {
    pub(crate) fn is_alive(&self) -> bool {
        self.internal
            .as_ref()
            .map(|i| i.alive.load(Ordering::Acquire))
            .unwrap_or(true)
    }

    pub(crate) fn is_external(&self) -> bool {
        self.internal.is_none()
    }

    pub(crate) fn version(&self) -> u32 {
        if !self.is_alive() {
            return 0;
        }
        unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr) as u32 }
    }

    pub(crate) fn id(&self) -> u32 {
        if !self.is_alive() {
            return 0;
        }
        unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_id, self.ptr) }
    }

    pub(crate) fn get_user_data<UD: 'static>(&self) -> Option<&UD> {
        if let Some(ref inner) = self.internal {
            inner.user_data.get::<UD>()
        } else {
            None
        }
    }

    pub(crate) fn send<I: Interface>(&self, msg: I::Request) {
        if let Some(ref internal) = self.internal {
            // object is managed
            if !internal.alive.load(Ordering::Acquire) {
                // don't send message to dead objects !
                return;
            }
        }
        let destructor = msg.is_destructor();
        msg.as_raw_c_in(|opcode, args| unsafe {
            ffi_dispatch!(
                WAYLAND_CLIENT_HANDLE,
                wl_proxy_marshal_array,
                self.ptr,
                opcode,
                args.as_ptr() as *mut _
            );
        });

        if destructor {
            // we need to destroy the proxy now
            if let Some(ref internal) = self.internal {
                internal.alive.store(false, Ordering::Release);
            }
            unsafe {
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr);
            }
        }
    }

    pub(crate) fn send_constructor<I, J>(
        &self,
        msg: I::Request,
        version: Option<u32>,
    ) -> Result<NewProxyInner, ()>
    where
        I: Interface,
        J: Interface,
    {
        if let Some(ref internal) = self.internal {
            // object is managed
            if !internal.alive.load(Ordering::Acquire) {
                // don't send message to dead objects !
                return Err(());
            }
        }
        let destructor = msg.is_destructor();

        let opcode = msg.opcode();

        // sanity check
        let mut nid_idx = I::Request::MESSAGES[opcode as usize]
            .signature
            .iter()
            .position(|&t| t == ArgumentType::NewId)
            .expect("Trying to use 'send_constructor' with a message not creating any object.");

        if let Some(o) = I::Request::child(opcode, 1, &()) {
            if !o.is_interface::<J>() {
                panic!("Trying to use 'send_constructor' with the wrong return type. Required interface {} but the message creates interface {}", J::NAME, o.interface)
            }
        } else {
            // there is no target interface in the protocol, this is a generic object-creating
            // function (likely wl_registry.bind), the newid arg will thus expand to (str, u32, obj)
            nid_idx += 2;
        }

        let version = version.unwrap_or_else(|| self.version());

        let ptr = msg.as_raw_c_in(|opcode, args| unsafe {
            assert!(
                args[nid_idx].o.is_null(),
                "Trying to use 'send_constructor' with a non-placeholder object."
            );
            ffi_dispatch!(
                WAYLAND_CLIENT_HANDLE,
                wl_proxy_marshal_array_constructor_versioned,
                self.ptr,
                opcode,
                args.as_mut_ptr(),
                J::c_interface(),
                version
            )
        });

        if destructor {
            // we need to destroy the proxy now
            if let Some(ref internal) = self.internal {
                internal.alive.store(false, Ordering::Release);
            }
            unsafe {
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr);
            }
        }

        let mut newp = unsafe { NewProxyInner::from_c_ptr(ptr) };
        newp.queue_thread = self
            .wrapping
            .or_else(|| self.internal.as_ref().map(|int| int.queue_thread))
            .unwrap_or_else(|| thread::current().id());
        Ok(newp)
    }

    pub(crate) fn equals(&self, other: &ProxyInner) -> bool {
        if !self.is_alive() {
            return false;
        }
        match (&self.internal, &other.internal) {
            (&Some(ref my_inner), &Some(ref other_inner)) => Arc::ptr_eq(my_inner, other_inner),
            (&None, &None) => self.ptr == other.ptr,
            _ => false,
        }
    }

    pub(crate) fn make_wrapper(&self, queue: &EventQueueInner) -> Result<ProxyInner, ()> {
        if !self.is_external() && !self.is_alive() {
            return Err(());
        }

        let wrapper_ptr;
        unsafe {
            wrapper_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create_wrapper, self.ptr);
            queue.assign_proxy(wrapper_ptr);
        }

        Ok(ProxyInner {
            internal: self.internal.clone(),
            ptr: wrapper_ptr,
            wrapping: Some(thread::current().id()), // EventQueueInner is not Send, so we are necessarily on the right thread
        })
    }

    pub(crate) fn child<I: Interface>(&self) -> NewProxyInner {
        let ptr =
            unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create, self.ptr, I::c_interface()) };
        let queue_thread = self
            .wrapping
            .or_else(|| self.internal.as_ref().map(|internal| internal.queue_thread))
            .unwrap_or_else(|| thread::current().id());
        NewProxyInner { ptr, queue_thread }
    }

    pub(crate) fn c_ptr(&self) -> *mut wl_proxy {
        self.ptr
    }

    pub(crate) unsafe fn from_c_ptr<I: Interface + From<Proxy<I>>>(ptr: *mut wl_proxy) -> Self {
        if ptr.is_null() {
            return ProxyInner {
                internal: Some(Arc::new(ProxyInternal {
                    alive: AtomicBool::new(false),
                    user_data: UserData::empty(),
                    queue_thread: thread::current().id(),
                })),
                ptr,
                wrapping: None,
            };
        }

        let is_managed = {
            ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr)
                == &::wayland_sys::RUST_MANAGED as *const u8 as *const _
        };
        let internal = if is_managed {
            let user_data =
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut ProxyUserData<I>;
            Some((*user_data).internal.clone())
        } else {
            None
        };
        ProxyInner {
            internal,
            ptr,
            wrapping: None,
        }
    }

    pub(crate) unsafe fn from_c_display_wrapper(d: *mut wl_proxy) -> ProxyInner {
        ProxyInner {
            internal: None,
            ptr: d,
            wrapping: Some(thread::current().id()),
        }
    }

    pub(crate) fn child_placeholder(&self) -> ProxyInner {
        ProxyInner {
            internal: Some(Arc::new(ProxyInternal {
                alive: AtomicBool::new(false),
                user_data: UserData::empty(),
                queue_thread: self
                    .internal
                    .as_ref()
                    .map(|int| int.queue_thread)
                    .unwrap_or_else(|| thread::current().id()),
            })),
            ptr: ::std::ptr::null_mut(),
            wrapping: None,
        }
    }
}

pub(crate) struct NewProxyInner {
    ptr: *mut wl_proxy,
    queue_thread: ThreadId,
}

impl NewProxyInner {
    pub(crate) fn is_queue_on_current_thread(&self) -> bool {
        self.queue_thread == thread::current().id()
    }

    pub(crate) unsafe fn implement<I: Interface, F>(
        self,
        implementation: F,
        user_data: UserData,
    ) -> ProxyInner
    where
        I: From<Proxy<I>>,
        F: FnMut(I::Event, I) + 'static,
    {
        let new_user_data = Box::new(ProxyUserData::new(implementation, user_data, self.queue_thread));
        let internal = new_user_data.internal.clone();

        ffi_dispatch!(
            WAYLAND_CLIENT_HANDLE,
            wl_proxy_add_dispatcher,
            self.ptr,
            proxy_dispatcher::<I>,
            &::wayland_sys::RUST_MANAGED as *const _ as *const _,
            Box::into_raw(new_user_data) as *mut _
        );

        ProxyInner {
            internal: Some(internal),
            ptr: self.ptr,
            wrapping: None,
        }
    }

    pub(crate) fn c_ptr(&self) -> *mut wl_proxy {
        self.ptr
    }

    pub(crate) unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> NewProxyInner {
        NewProxyInner {
            ptr,
            queue_thread: thread::current().id(),
        }
    }
}

type BoxedCallback<I> = Box<FnMut(<I as Interface>::Event, I)>;

struct ProxyUserData<I: Interface + From<Proxy<I>>> {
    internal: Arc<ProxyInternal>,
    implem: Option<BoxedCallback<I>>,
}

impl<I: Interface + From<Proxy<I>>> ProxyUserData<I> {
    fn new<F>(implem: F, user_data: UserData, thread_id: ThreadId) -> ProxyUserData<I>
    where
        F: FnMut(I::Event, I) + 'static,
    {
        ProxyUserData {
            internal: Arc::new(ProxyInternal::new(user_data, thread_id)),
            implem: Some(Box::new(implem)),
        }
    }
}

unsafe extern "C" fn proxy_dispatcher<I: Interface>(
    _implem: *const c_void,
    proxy: *mut c_void,
    opcode: u32,
    _msg: *const wl_message,
    args: *const wl_argument,
) -> c_int
where
    I: Interface + From<Proxy<I>>,
{
    let proxy = proxy as *mut wl_proxy;

    // We don't need to worry about panic-safeness, because if there is a panic,
    // we'll abort the process, so no access to corrupted data is possible.
    let ret = ::std::panic::catch_unwind(move || {
        // parse the message:
        let msg = I::Event::from_raw_c(proxy as *mut _, opcode, args)?;
        let must_destroy = msg.is_destructor();
        // create the proxy object
        let proxy_obj = ::Proxy::<I>::from_c_ptr(proxy).into();
        // retrieve the impl
        let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, proxy);
        {
            let user_data = &mut *(user_data as *mut ProxyUserData<I>);
            let implem = user_data.implem.as_mut().unwrap();
            if must_destroy {
                user_data.internal.alive.store(false, Ordering::Release);
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy);
            }
            // call the impl
            implem(msg, proxy_obj);
        }
        if must_destroy {
            // final cleanup
            let _ = Box::from_raw(user_data as *mut ProxyUserData<I>);
        }
        Ok(())
    });
    // check the return status
    match ret {
        Ok(Ok(())) => 0,
        Ok(Err(())) => {
            eprintln!(
                "[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.",
                opcode,
                I::NAME
            );
            ::libc::abort();
        }
        Err(_) => {
            eprintln!("[wayland-client error] A handler for {} panicked.", I::NAME);
            ::libc::abort()
        }
    }
}
<commit_msg>client: override wl_display version on native_lib<commit_after>use std::os::raw::{c_int, c_void};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, ThreadId};

use wayland_commons::utils::UserData;
use wayland_commons::wire::ArgumentType;
use wayland_commons::MessageGroup;
use {Interface, Proxy};

use super::EventQueueInner;

use wayland_sys::client::*;
use wayland_sys::common::*;

pub struct ProxyInternal {
    alive: AtomicBool,
    user_data: UserData,
    queue_thread: ThreadId,
}

impl ProxyInternal {
    pub fn new(user_data: UserData, thread_id: ThreadId) -> ProxyInternal {
        ProxyInternal {
            alive: AtomicBool::new(true),
            user_data,
            queue_thread: thread_id,
        }
    }
}

#[derive(Clone)]
pub(crate) struct ProxyInner {
    internal: Option<Arc<ProxyInternal>>,
    ptr: *mut wl_proxy,
    wrapping: Option<ThreadId>,
}

unsafe impl Send for ProxyInner {}
unsafe impl Sync for ProxyInner {}

impl ProxyInner {
    pub(crate) fn is_alive(&self) -> bool {
        self.internal
            .as_ref()
            .map(|i| i.alive.load(Ordering::Acquire))
            .unwrap_or(true)
    }

    pub(crate) fn is_external(&self) -> bool {
        self.internal.is_none()
    }

    pub(crate) fn version(&self) -> u32 {
        if !self.is_alive() {
            return 0;
        }
        let version = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_version, self.ptr) as u32 };
        if version == 0 {
            // For backcompat reasons the C libs return 0 as a version for the wl_display
            // So override it
            return 1;
        }
        version
    }

    pub(crate) fn id(&self) -> u32 {
        if !self.is_alive() {
            return 0;
        }
        unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_id, self.ptr) }
    }

    pub(crate) fn get_user_data<UD: 'static>(&self) -> Option<&UD> {
        if let Some(ref inner) = self.internal {
            inner.user_data.get::<UD>()
        } else {
            None
        }
    }

    pub(crate) fn send<I: Interface>(&self, msg: I::Request) {
        if let Some(ref internal) = self.internal {
            // object is managed
            if !internal.alive.load(Ordering::Acquire) {
                // don't send message to dead objects !
                return;
            }
        }
        let destructor = msg.is_destructor();
        msg.as_raw_c_in(|opcode, args| unsafe {
            ffi_dispatch!(
                WAYLAND_CLIENT_HANDLE,
                wl_proxy_marshal_array,
                self.ptr,
                opcode,
                args.as_ptr() as *mut _
            );
        });

        if destructor {
            // we need to destroy the proxy now
            if let Some(ref internal) = self.internal {
                internal.alive.store(false, Ordering::Release);
            }
            unsafe {
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr);
            }
        }
    }

    pub(crate) fn send_constructor<I, J>(
        &self,
        msg: I::Request,
        version: Option<u32>,
    ) -> Result<NewProxyInner, ()>
    where
        I: Interface,
        J: Interface,
    {
        if let Some(ref internal) = self.internal {
            // object is managed
            if !internal.alive.load(Ordering::Acquire) {
                // don't send message to dead objects !
                return Err(());
            }
        }
        let destructor = msg.is_destructor();

        let opcode = msg.opcode();

        // sanity check
        let mut nid_idx = I::Request::MESSAGES[opcode as usize]
            .signature
            .iter()
            .position(|&t| t == ArgumentType::NewId)
            .expect("Trying to use 'send_constructor' with a message not creating any object.");

        if let Some(o) = I::Request::child(opcode, 1, &()) {
            if !o.is_interface::<J>() {
                panic!("Trying to use 'send_constructor' with the wrong return type. Required interface {} but the message creates interface {}", J::NAME, o.interface)
            }
        } else {
            // there is no target interface in the protocol, this is a generic object-creating
            // function (likely wl_registry.bind), the newid arg will thus expand to (str, u32, obj)
            nid_idx += 2;
        }

        let version = version.unwrap_or_else(|| self.version());

        let ptr = msg.as_raw_c_in(|opcode, args| unsafe {
            assert!(
                args[nid_idx].o.is_null(),
                "Trying to use 'send_constructor' with a non-placeholder object."
            );
            ffi_dispatch!(
                WAYLAND_CLIENT_HANDLE,
                wl_proxy_marshal_array_constructor_versioned,
                self.ptr,
                opcode,
                args.as_mut_ptr(),
                J::c_interface(),
                version
            )
        });

        if destructor {
            // we need to destroy the proxy now
            if let Some(ref internal) = self.internal {
                internal.alive.store(false, Ordering::Release);
            }
            unsafe {
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, self.ptr);
            }
        }

        let mut newp = unsafe { NewProxyInner::from_c_ptr(ptr) };
        newp.queue_thread = self
            .wrapping
            .or_else(|| self.internal.as_ref().map(|int| int.queue_thread))
            .unwrap_or_else(|| thread::current().id());
        Ok(newp)
    }

    pub(crate) fn equals(&self, other: &ProxyInner) -> bool {
        if !self.is_alive() {
            return false;
        }
        match (&self.internal, &other.internal) {
            (&Some(ref my_inner), &Some(ref other_inner)) => Arc::ptr_eq(my_inner, other_inner),
            (&None, &None) => self.ptr == other.ptr,
            _ => false,
        }
    }

    pub(crate) fn make_wrapper(&self, queue: &EventQueueInner) -> Result<ProxyInner, ()> {
        if !self.is_external() && !self.is_alive() {
            return Err(());
        }

        let wrapper_ptr;
        unsafe {
            wrapper_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create_wrapper, self.ptr);
            queue.assign_proxy(wrapper_ptr);
        }

        Ok(ProxyInner {
            internal: self.internal.clone(),
            ptr: wrapper_ptr,
            wrapping: Some(thread::current().id()), // EventQueueInner is not Send, so we are necessarily on the right thread
        })
    }

    pub(crate) fn child<I: Interface>(&self) -> NewProxyInner {
        let ptr =
            unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_create, self.ptr, I::c_interface()) };
        let queue_thread = self
            .wrapping
            .or_else(|| self.internal.as_ref().map(|internal| internal.queue_thread))
            .unwrap_or_else(|| thread::current().id());
        NewProxyInner { ptr, queue_thread }
    }

    pub(crate) fn c_ptr(&self) -> *mut wl_proxy {
        self.ptr
    }

    pub(crate) unsafe fn from_c_ptr<I: Interface + From<Proxy<I>>>(ptr: *mut wl_proxy) -> Self {
        if ptr.is_null() {
            return ProxyInner {
                internal: Some(Arc::new(ProxyInternal {
                    alive: AtomicBool::new(false),
                    user_data: UserData::empty(),
                    queue_thread: thread::current().id(),
                })),
                ptr,
                wrapping: None,
            };
        }

        let is_managed = {
            ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_listener, ptr)
                == &::wayland_sys::RUST_MANAGED as *const u8 as *const _
        };
        let internal = if is_managed {
            let user_data =
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, ptr) as *mut ProxyUserData<I>;
            Some((*user_data).internal.clone())
        } else {
            None
        };
        ProxyInner {
            internal,
            ptr,
            wrapping: None,
        }
    }

    pub(crate) unsafe fn from_c_display_wrapper(d: *mut wl_proxy) -> ProxyInner {
        ProxyInner {
            internal: None,
            ptr: d,
            wrapping: Some(thread::current().id()),
        }
    }

    pub(crate) fn child_placeholder(&self) -> ProxyInner {
        ProxyInner {
            internal: Some(Arc::new(ProxyInternal {
                alive: AtomicBool::new(false),
                user_data: UserData::empty(),
                queue_thread: self
                    .internal
                    .as_ref()
                    .map(|int| int.queue_thread)
                    .unwrap_or_else(|| thread::current().id()),
            })),
            ptr: ::std::ptr::null_mut(),
            wrapping: None,
        }
    }
}

pub(crate) struct NewProxyInner {
    ptr: *mut wl_proxy,
    queue_thread: ThreadId,
}

impl NewProxyInner {
    pub(crate) fn is_queue_on_current_thread(&self) -> bool {
        self.queue_thread == thread::current().id()
    }

    pub(crate) unsafe fn implement<I: Interface, F>(
        self,
        implementation: F,
        user_data: UserData,
    ) -> ProxyInner
    where
        I: From<Proxy<I>>,
        F: FnMut(I::Event, I) + 'static,
    {
        let new_user_data = Box::new(ProxyUserData::new(implementation, user_data, self.queue_thread));
        let internal = new_user_data.internal.clone();

        ffi_dispatch!(
            WAYLAND_CLIENT_HANDLE,
            wl_proxy_add_dispatcher,
            self.ptr,
            proxy_dispatcher::<I>,
            &::wayland_sys::RUST_MANAGED as *const _ as *const _,
            Box::into_raw(new_user_data) as *mut _
        );

        ProxyInner {
            internal: Some(internal),
            ptr: self.ptr,
            wrapping: None,
        }
    }

    pub(crate) fn c_ptr(&self) -> *mut wl_proxy {
        self.ptr
    }

    pub(crate) unsafe fn from_c_ptr(ptr: *mut wl_proxy) -> NewProxyInner {
        NewProxyInner {
            ptr,
            queue_thread: thread::current().id(),
        }
    }
}

type BoxedCallback<I> = Box<FnMut(<I as Interface>::Event, I)>;

struct ProxyUserData<I: Interface + From<Proxy<I>>> {
    internal: Arc<ProxyInternal>,
    implem: Option<BoxedCallback<I>>,
}

impl<I: Interface + From<Proxy<I>>> ProxyUserData<I> {
    fn new<F>(implem: F, user_data: UserData, thread_id: ThreadId) -> ProxyUserData<I>
    where
        F: FnMut(I::Event, I) + 'static,
    {
        ProxyUserData {
            internal: Arc::new(ProxyInternal::new(user_data, thread_id)),
            implem: Some(Box::new(implem)),
        }
    }
}

unsafe extern "C" fn proxy_dispatcher<I: Interface>(
    _implem: *const c_void,
    proxy: *mut c_void,
    opcode: u32,
    _msg: *const wl_message,
    args: *const wl_argument,
) -> c_int
where
    I: Interface + From<Proxy<I>>,
{
    let proxy = proxy as *mut wl_proxy;

    // We don't need to worry about panic-safeness, because if there is a panic,
    // we'll abort the process, so no access to corrupted data is possible.
    let ret = ::std::panic::catch_unwind(move || {
        // parse the message:
        let msg = I::Event::from_raw_c(proxy as *mut _, opcode, args)?;
        let must_destroy = msg.is_destructor();
        // create the proxy object
        let proxy_obj = ::Proxy::<I>::from_c_ptr(proxy).into();
        // retrieve the impl
        let user_data = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_get_user_data, proxy);
        {
            let user_data = &mut *(user_data as *mut ProxyUserData<I>);
            let implem = user_data.implem.as_mut().unwrap();
            if must_destroy {
                user_data.internal.alive.store(false, Ordering::Release);
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_proxy_destroy, proxy);
            }
            // call the impl
            implem(msg, proxy_obj);
        }
        if must_destroy {
            // final cleanup
            let _ = Box::from_raw(user_data as *mut ProxyUserData<I>);
        }
        Ok(())
    });
    // check the return status
    match ret {
        Ok(Ok(())) => 0,
        Ok(Err(())) => {
            eprintln!(
                "[wayland-client error] Attempted to dispatch unknown opcode {} for {}, aborting.",
                opcode,
                I::NAME
            );
            ::libc::abort();
        }
        Err(_) => {
            eprintln!("[wayland-client error] A handler for {} panicked.", I::NAME);
            ::libc::abort()
        }
    }
}
<|endoftext|>


<commit_before>use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
use crate::spec::{Target, TargetOptions};

pub fn target() -> Target {
    Target {
        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(),
        llvm_target: "riscv64".to_string(),
        pointer_width: 64,
        arch: "riscv64".to_string(),

        options: TargetOptions {
            linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
            linker: Some("rust-lld".to_string()),
            cpu: "generic-rv64".to_string(),
            max_atomic_width: Some(64),
            atomic_cas: true,
            features: "+m,+a,+f,+d,+c".to_string(),
            executables: true,
            panic_strategy: PanicStrategy::Abort,
            relocation_model: RelocModel::Static,
            code_model: Some(CodeModel::Medium),
            emit_debug_gdb_scripts: false,
            unsupported_abis: super::riscv_base::unsupported_abis(),
            eh_frame_header: false,
            ..Default::default()
        },
    }
}
<commit_msg>riscvgc-unknown-none-elf use lp64d ABI<commit_after>use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel};
use crate::spec::{Target, TargetOptions};

pub fn target() -> Target {
    Target {
        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(),
        llvm_target: "riscv64".to_string(),
        pointer_width: 64,
        arch: "riscv64".to_string(),

        options: TargetOptions {
            linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
            linker: Some("rust-lld".to_string()),
            llvm_abiname: "lp64d".to_string(),
            cpu: "generic-rv64".to_string(),
            max_atomic_width: Some(64),
            atomic_cas: true,
            features: "+m,+a,+f,+d,+c".to_string(),
            executables: true,
            panic_strategy: PanicStrategy::Abort,
            relocation_model: RelocModel::Static,
            code_model: Some(CodeModel::Medium),
            emit_debug_gdb_scripts: false,
            unsupported_abis: super::riscv_base::unsupported_abis(),
            eh_frame_header: false,
            ..Default::default()
        },
    }
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
mod demo;
#[allow(dead_code)]
mod util;

use crate::demo::{ui, App};
use argh::FromArgs;
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::{
    error::Error,
    io::{stdout, Write},
    sync::mpsc,
    thread,
    time::{Duration, Instant},
};
use tui::{backend::CrosstermBackend, Terminal};

enum Event<I> {
    Input(I),
    Tick,
}

/// Crossterm demo
#[derive(Debug, FromArgs)]
struct Cli {
    /// time in ms between two ticks.
    #[argh(option, default = "250")]
    tick_rate: u64,
    /// whether unicode symbols are used to improve the overall look of the app
    #[argh(option, default = "true")]
    enhanced_graphics: bool,
}

fn main() -> Result<(), Box<dyn Error>> {
    let cli: Cli = argh::from_env();

    enable_raw_mode()?;

    let mut stdout = stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;

    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    // Setup input handling
    let (tx, rx) = mpsc::channel();

    let tick_rate = Duration::from_millis(cli.tick_rate);
    thread::spawn(move || {
        let mut last_tick = Instant::now();
        loop {
            // poll for tick rate duration, if no events, sent tick event.
            if event::poll(tick_rate - last_tick.elapsed()).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
            if last_tick.elapsed() >= tick_rate {
                tx.send(Event::Tick).unwrap();
                last_tick = Instant::now();
            }
        }
    });

    let mut app = App::new("Crossterm Demo", cli.enhanced_graphics);

    terminal.clear()?;

    loop {
        terminal.draw(|f| ui::draw(f, &mut app))?;
        match rx.recv()? {
            Event::Input(event) => match event.code {
                KeyCode::Char('q') => {
                    disable_raw_mode()?;
                    execute!(
                        terminal.backend_mut(),
                        LeaveAlternateScreen,
                        DisableMouseCapture
                    )?;
                    terminal.show_cursor()?;
                    break;
                }
                KeyCode::Char(c) => app.on_key(c),
                KeyCode::Left => app.on_left(),
                KeyCode::Up => app.on_up(),
                KeyCode::Right => app.on_right(),
                KeyCode::Down => app.on_down(),
                _ => {}
            },
            Event::Tick => {
                app.on_tick();
            }
        }
        if app.should_quit {
            break;
        }
    }

    Ok(())
}
<commit_msg>fix(examples): avoid panic when computing event poll timeout in crossterm demo (#380)<commit_after>#[allow(dead_code)]
mod demo;
#[allow(dead_code)]
mod util;

use crate::demo::{ui, App};
use argh::FromArgs;
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::{
    error::Error,
    io::{stdout, Write},
    sync::mpsc,
    thread,
    time::{Duration, Instant},
};
use tui::{backend::CrosstermBackend, Terminal};

enum Event<I> {
    Input(I),
    Tick,
}

/// Crossterm demo
#[derive(Debug, FromArgs)]
struct Cli {
    /// time in ms between two ticks.
    #[argh(option, default = "250")]
    tick_rate: u64,
    /// whether unicode symbols are used to improve the overall look of the app
    #[argh(option, default = "true")]
    enhanced_graphics: bool,
}

fn main() -> Result<(), Box<dyn Error>> {
    let cli: Cli = argh::from_env();

    enable_raw_mode()?;

    let mut stdout = stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;

    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    // Setup input handling
    let (tx, rx) = mpsc::channel();

    let tick_rate = Duration::from_millis(cli.tick_rate);
    thread::spawn(move || {
        let mut last_tick = Instant::now();
        loop {
            // poll for tick rate duration, if no events, sent tick event.
            let timeout = tick_rate
                .checked_sub(last_tick.elapsed())
                .unwrap_or_else(|| Duration::from_secs(0));
            if event::poll(timeout).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
            if last_tick.elapsed() >= tick_rate {
                tx.send(Event::Tick).unwrap();
                last_tick = Instant::now();
            }
        }
    });

    let mut app = App::new("Crossterm Demo", cli.enhanced_graphics);

    terminal.clear()?;

    loop {
        terminal.draw(|f| ui::draw(f, &mut app))?;
        match rx.recv()? {
            Event::Input(event) => match event.code {
                KeyCode::Char('q') => {
                    disable_raw_mode()?;
                    execute!(
                        terminal.backend_mut(),
                        LeaveAlternateScreen,
                        DisableMouseCapture
                    )?;
                    terminal.show_cursor()?;
                    break;
                }
                KeyCode::Char(c) => app.on_key(c),
                KeyCode::Left => app.on_left(),
                KeyCode::Up => app.on_up(),
                KeyCode::Right => app.on_right(),
                KeyCode::Down => app.on_down(),
                _ => {}
            },
            Event::Tick => {
                app.on_tick();
            }
        }
        if app.should_quit {
            break;
        }
    }

    Ok(())
}
<|endoftext|>


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

 <%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>

${helpers.single_keyword("border-collapse", "separate collapse",
                         gecko_constant_prefix="NS_STYLE_BORDER",
                         animatable=False)}
${helpers.single_keyword("empty-cells", "show hide",
                         gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
                         animatable=False)}
${helpers.single_keyword("caption-side", "top bottom",
                         extra_gecko_values="right left top-outside bottom-outside",
                         animatable=False)}

<%helpers:longhand name="border-spacing" animatable="False">
    use app_units::Au;
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;

    pub mod computed_value {
        use app_units::Au;
        use properties::animated_properties::Interpolate;

        #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            pub horizontal: Au,
            pub vertical: Au,
        }

        /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
        impl Interpolate for T {
            #[inline]
            fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
                Ok(T {
                    horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)),
                    vertical: try!(self.vertical.interpolate(&other.vertical, time)),
                })
            }
        }
    }

    impl HasViewportPercentage for SpecifiedValue {
        fn has_viewport_percentage(&self) -> bool {
            return self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage()
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub horizontal: specified::Length,
        pub vertical: specified::Length,
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            horizontal: Au(0),
            vertical: Au(0),
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.horizontal.to_css(dest));
            try!(dest.write_str(" "));
            self.vertical.to_css(dest)
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.horizontal.to_css(dest));
            try!(dest.write_str(" "));
            self.vertical.to_css(dest)
        }
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            computed_value::T {
                horizontal: self.horizontal.to_computed_value(context),
                vertical: self.vertical.to_computed_value(context),
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            SpecifiedValue {
                horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
                vertical: ToComputedValue::from_computed_value(&computed.vertical),
            }
        }
    }

    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        let mut lengths = [ None, None ];
        for i in 0..2 {
            match specified::Length::parse_non_negative(input) {
                Err(()) => break,
                Ok(length) => lengths[i] = Some(length),
            }
        }
        if input.next().is_ok() {
            return Err(())
        }
        match (lengths[0], lengths[1]) {
            (None, None) => Err(()),
            (Some(length), None) => {
                Ok(SpecifiedValue {
                    horizontal: length,
                    vertical: length,
                })
            }
            (Some(horizontal), Some(vertical)) => {
                Ok(SpecifiedValue {
                    horizontal: horizontal,
                    vertical: vertical,
                })
            }
            (None, Some(_)) => panic!("shouldn't happen"),
        }
    }
</%helpers:longhand>
<commit_msg>Add spec links for inherited_table properties<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

 <%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>

${helpers.single_keyword("border-collapse", "separate collapse",
                         gecko_constant_prefix="NS_STYLE_BORDER",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse")}
${helpers.single_keyword("empty-cells", "show hide",
                         gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells")}
${helpers.single_keyword("caption-side", "top bottom",
                         extra_gecko_values="right left top-outside bottom-outside",
                         animatable=False,
                         spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}

<%helpers:longhand name="border-spacing" animatable="False"
                   spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
    use app_units::Au;
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;

    pub mod computed_value {
        use app_units::Au;
        use properties::animated_properties::Interpolate;

        #[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            pub horizontal: Au,
            pub vertical: Au,
        }

        /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
        impl Interpolate for T {
            #[inline]
            fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
                Ok(T {
                    horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)),
                    vertical: try!(self.vertical.interpolate(&other.vertical, time)),
                })
            }
        }
    }

    impl HasViewportPercentage for SpecifiedValue {
        fn has_viewport_percentage(&self) -> bool {
            return self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage()
        }
    }

    #[derive(Clone, Debug, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub horizontal: specified::Length,
        pub vertical: specified::Length,
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            horizontal: Au(0),
            vertical: Au(0),
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.horizontal.to_css(dest));
            try!(dest.write_str(" "));
            self.vertical.to_css(dest)
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            try!(self.horizontal.to_css(dest));
            try!(dest.write_str(" "));
            self.vertical.to_css(dest)
        }
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            computed_value::T {
                horizontal: self.horizontal.to_computed_value(context),
                vertical: self.vertical.to_computed_value(context),
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            SpecifiedValue {
                horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
                vertical: ToComputedValue::from_computed_value(&computed.vertical),
            }
        }
    }

    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        let mut lengths = [ None, None ];
        for i in 0..2 {
            match specified::Length::parse_non_negative(input) {
                Err(()) => break,
                Ok(length) => lengths[i] = Some(length),
            }
        }
        if input.next().is_ok() {
            return Err(())
        }
        match (lengths[0], lengths[1]) {
            (None, None) => Err(()),
            (Some(length), None) => {
                Ok(SpecifiedValue {
                    horizontal: length,
                    vertical: length,
                })
            }
            (Some(horizontal), Some(vertical)) => {
                Ok(SpecifiedValue {
                    horizontal: horizontal,
                    vertical: vertical,
                })
            }
            (None, Some(_)) => panic!("shouldn't happen"),
        }
    }
</%helpers:longhand>
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Final phase of cleanup and optimization.

use crate::{
    dataflow_analysis::{DataflowAnalysis, TransferFunctions},
    function_target::{FunctionData, FunctionTarget},
    function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
    stackless_bytecode::{BorrowNode, Bytecode, Operation},
    stackless_control_flow_graph::StacklessControlFlowGraph,
};
use move_binary_format::file_format::CodeOffset;
use move_model::{
    model::FunctionEnv,
    native::{EVENT_EMIT_EVENT, VECTOR_BORROW_MUT},
};

use crate::dataflow_domains::{AbstractDomain, JoinResult};
use std::collections::BTreeSet;

pub struct CleanAndOptimizeProcessor();

impl CleanAndOptimizeProcessor {
    pub fn new() -> Box<Self> {
        Box::new(Self())
    }
}

impl FunctionTargetProcessor for CleanAndOptimizeProcessor {
    fn process(
        &self,
        _targets: &mut FunctionTargetsHolder,
        func_env: &FunctionEnv<'_>,
        mut data: FunctionData,
    ) -> FunctionData {
        if func_env.is_native() {
            // Nothing to do
            return data;
        }

        // Run optimizer
        let instrs = std::mem::take(&mut data.code);
        let new_instrs = Optimizer {
            target: &FunctionTarget::new(func_env, &data),
        }
        .run(instrs);
        data.code = new_instrs;
        data
    }

    fn name(&self) -> String {
        "clean_and_optimize".to_string()
    }
}

// Analysis
// ========

/// A data flow analysis state used for optimization analysis. Currently it tracks the nodes
/// which have been updated but not yet written back.
#[derive(Debug, Clone, Default, Eq, PartialEq, PartialOrd)]
struct AnalysisState {
    unwritten: BTreeSet<BorrowNode>,
}

impl AbstractDomain for AnalysisState {
    fn join(&mut self, other: &Self) -> JoinResult {
        let n = self.unwritten.len();
        self.unwritten.extend(other.unwritten.iter().cloned());
        if self.unwritten.len() == n {
            JoinResult::Unchanged
        } else {
            JoinResult::Changed
        }
    }
}

struct Optimizer<'a> {
    target: &'a FunctionTarget<'a>,
}

impl<'a> TransferFunctions for Optimizer<'a> {
    type State = AnalysisState;
    const BACKWARD: bool = false;

    fn execute(&self, state: &mut AnalysisState, instr: &Bytecode, _offset: CodeOffset) {
        use BorrowNode::*;
        use Bytecode::*;
        use Operation::*;
        if let Call(_, _, oper, srcs, _) = instr {
            match oper {
                WriteRef => {
                    state.unwritten.insert(Reference(srcs[0]));
                }
                WriteBack(Reference(dest), ..) => {
                    if state.unwritten.contains(&Reference(srcs[0])) {
                        state.unwritten.insert(Reference(*dest));
                    }
                }
                Function(mid, fid, _) => {
                    let callee_env = &self
                        .target
                        .global_env()
                        .get_function_qid(mid.qualified(*fid));
                    let has_effect = if callee_env.is_native_or_intrinsic() {
                        // Exploit knowledge about builtin functions
                        let pool = callee_env.symbol_pool();
                        !matches!(
                            format!(
                                "{}::{}",
                                callee_env.module_env.get_name().display_full(pool),
                                callee_env.get_name().display(pool)
                            )
                            .as_str(),
                            VECTOR_BORROW_MUT | EVENT_EMIT_EVENT
                        )
                    } else {
                        true
                    };

                    // Mark &mut parameters to functions as unwritten.
                    if has_effect {
                        for src in srcs {
                            if self.target.get_local_type(*src).is_mutable_reference() {
                                state.unwritten.insert(Reference(*src));
                            }
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

impl<'a> DataflowAnalysis for Optimizer<'a> {}

// Transformation
// ==============

impl<'a> Optimizer<'a> {
    fn run(&mut self, instrs: Vec<Bytecode>) -> Vec<Bytecode> {
        // Rum Analysis
        let cfg = StacklessControlFlowGraph::new_forward(&instrs);
        let state = self.analyze_function(AnalysisState::default(), &instrs, &cfg);
        let data = self.state_per_instruction(state, &instrs, &cfg, |before, _| before.clone());

        // Transform code.
        let mut new_instrs = vec![];
        for (code_offset, instr) in instrs.into_iter().enumerate() {
            use BorrowNode::*;
            use Bytecode::*;
            use Operation::*;

            let is_unwritten = |code_offset: CodeOffset, node: &BorrowNode| {
                if let Some(unwritten) = data.get(&code_offset).map(|d| &d.unwritten) {
                    unwritten.contains(node)
                } else {
                    // No data for this node, so assume it is unwritten.
                    true
                }
            };
            if !new_instrs.is_empty() {
                // Perform peephole optimization
                match (&new_instrs[new_instrs.len() - 1], &instr) {
                    (Call(_, _, UnpackRef, srcs1, _), Call(_, _, PackRef, srcs2, _))
                        if srcs1[0] == srcs2[0] =>
                    {
                        // skip this redundant unpack/pack pair.
                        new_instrs.pop();
                        continue;
                    }
                    (Call(_, dests, IsParent(..), srcs, _), Branch(_, _, _, tmp))
                        if dests[0] == *tmp
                            && !is_unwritten(code_offset as CodeOffset, &Reference(srcs[0])) =>
                    {
                        // skip this obsolete IsParent check
                        new_instrs.pop();
                        continue;
                    }
                    _ => {}
                }
            }
            // Remove unnecessary WriteBack
            match &instr {
                Call(_, _, WriteBack(..), srcs, _)
                    if !is_unwritten(code_offset as CodeOffset, &Reference(srcs[0])) =>
                {
                    // skip this obsolete WriteBack
                    continue;
                }
                _ => {}
            }
            new_instrs.push(instr);
        }
        new_instrs
    }
}
<commit_msg>[move-prover] mark all &mut parameters to functions as unwritten<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Final phase of cleanup and optimization.

use crate::{
    dataflow_analysis::{DataflowAnalysis, TransferFunctions},
    function_target::{FunctionData, FunctionTarget},
    function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
    options::ProverOptions,
    stackless_bytecode::{BorrowNode, Bytecode, Operation},
    stackless_control_flow_graph::StacklessControlFlowGraph,
};
use move_binary_format::file_format::CodeOffset;
use move_model::{
    model::FunctionEnv,
    native::{EVENT_EMIT_EVENT, VECTOR_BORROW_MUT},
};

use crate::dataflow_domains::{AbstractDomain, JoinResult};
use std::collections::BTreeSet;

pub struct CleanAndOptimizeProcessor();

impl CleanAndOptimizeProcessor {
    pub fn new() -> Box<Self> {
        Box::new(Self())
    }
}

impl FunctionTargetProcessor for CleanAndOptimizeProcessor {
    fn process(
        &self,
        _targets: &mut FunctionTargetsHolder,
        func_env: &FunctionEnv<'_>,
        mut data: FunctionData,
    ) -> FunctionData {
        if func_env.is_native() {
            // Nothing to do
            return data;
        }

        // Run optimizer
        let options = ProverOptions::get(func_env.module_env.env);
        let instrs = std::mem::take(&mut data.code);
        let new_instrs = Optimizer {
            options: &*options,
            target: &FunctionTarget::new(func_env, &data),
        }
        .run(instrs);
        data.code = new_instrs;
        data
    }

    fn name(&self) -> String {
        "clean_and_optimize".to_string()
    }
}

// Analysis
// ========

/// A data flow analysis state used for optimization analysis. Currently it tracks the nodes
/// which have been updated but not yet written back.
#[derive(Debug, Clone, Default, Eq, PartialEq, PartialOrd)]
struct AnalysisState {
    unwritten: BTreeSet<BorrowNode>,
}

impl AbstractDomain for AnalysisState {
    fn join(&mut self, other: &Self) -> JoinResult {
        let n = self.unwritten.len();
        self.unwritten.extend(other.unwritten.iter().cloned());
        if self.unwritten.len() == n {
            JoinResult::Unchanged
        } else {
            JoinResult::Changed
        }
    }
}

struct Optimizer<'a> {
    options: &'a ProverOptions,
    target: &'a FunctionTarget<'a>,
}

impl<'a> TransferFunctions for Optimizer<'a> {
    type State = AnalysisState;
    const BACKWARD: bool = false;

    fn execute(&self, state: &mut AnalysisState, instr: &Bytecode, _offset: CodeOffset) {
        use BorrowNode::*;
        use Bytecode::*;
        use Operation::*;
        if let Call(_, _, oper, srcs, _) = instr {
            match oper {
                WriteRef => {
                    state.unwritten.insert(Reference(srcs[0]));
                }
                WriteBack(Reference(dest), ..) => {
                    if state.unwritten.contains(&Reference(srcs[0])) {
                        state.unwritten.insert(Reference(*dest));
                    }
                }
                Function(mid, fid, _) => {
                    let callee_env = &self
                        .target
                        .global_env()
                        .get_function_qid(mid.qualified(*fid));
                    let has_effect = if !self.options.for_interpretation
                        && callee_env.is_native_or_intrinsic()
                    {
                        // Exploit knowledge about builtin functions
                        let pool = callee_env.symbol_pool();
                        !matches!(
                            format!(
                                "{}::{}",
                                callee_env.module_env.get_name().display_full(pool),
                                callee_env.get_name().display(pool)
                            )
                            .as_str(),
                            VECTOR_BORROW_MUT | EVENT_EMIT_EVENT
                        )
                    } else {
                        true
                    };

                    // Mark &mut parameters to functions as unwritten.
                    if has_effect {
                        for src in srcs {
                            if self.target.get_local_type(*src).is_mutable_reference() {
                                state.unwritten.insert(Reference(*src));
                            }
                        }
                    }
                }
                _ => {}
            }
        }
    }
}

impl<'a> DataflowAnalysis for Optimizer<'a> {}

// Transformation
// ==============

impl<'a> Optimizer<'a> {
    fn run(&mut self, instrs: Vec<Bytecode>) -> Vec<Bytecode> {
        // Rum Analysis
        let cfg = StacklessControlFlowGraph::new_forward(&instrs);
        let state = self.analyze_function(AnalysisState::default(), &instrs, &cfg);
        let data = self.state_per_instruction(state, &instrs, &cfg, |before, _| before.clone());

        // Transform code.
        let mut new_instrs = vec![];
        for (code_offset, instr) in instrs.into_iter().enumerate() {
            use BorrowNode::*;
            use Bytecode::*;
            use Operation::*;

            let is_unwritten = |code_offset: CodeOffset, node: &BorrowNode| {
                if let Some(unwritten) = data.get(&code_offset).map(|d| &d.unwritten) {
                    unwritten.contains(node)
                } else {
                    // No data for this node, so assume it is unwritten.
                    true
                }
            };
            if !new_instrs.is_empty() {
                // Perform peephole optimization
                match (&new_instrs[new_instrs.len() - 1], &instr) {
                    (Call(_, _, UnpackRef, srcs1, _), Call(_, _, PackRef, srcs2, _))
                        if srcs1[0] == srcs2[0] =>
                    {
                        // skip this redundant unpack/pack pair.
                        new_instrs.pop();
                        continue;
                    }
                    (Call(_, dests, IsParent(..), srcs, _), Branch(_, _, _, tmp))
                        if dests[0] == *tmp
                            && !is_unwritten(code_offset as CodeOffset, &Reference(srcs[0])) =>
                    {
                        // skip this obsolete IsParent check
                        new_instrs.pop();
                        continue;
                    }
                    _ => {}
                }
            }
            // Remove unnecessary WriteBack
            match &instr {
                Call(_, _, WriteBack(..), srcs, _)
                    if !is_unwritten(code_offset as CodeOffset, &Reference(srcs[0])) =>
                {
                    // skip this obsolete WriteBack
                    continue;
                }
                _ => {}
            }
            new_instrs.push(instr);
        }
        new_instrs
    }
}
<|endoftext|>


<commit_before>fn main() {
    manage_docs();
}

#[cfg(any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs"))]
fn manage_docs () {
    extern crate lgpl_docs;
    const PATH: &'static str = "src";
    const IGNORES: &'static [&'static str] = &[];
    lgpl_docs::purge(PATH, IGNORES);
    if cfg!(feature = "embed-lgpl-docs") {
        lgpl_docs::embed(lgpl_docs::CAIRO_DOCS, PATH, IGNORES);
    }
}

#[cfg(not(any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs")))]
fn manage_docs() { }
<commit_msg>lgpl-docs API update<commit_after>fn main() {
    manage_docs();
}

#[cfg(any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs"))]
fn manage_docs () {
    extern crate lgpl_docs;
    const PATH: &'static str = "src";
    const IGNORES: &'static [&'static str] = &[];
    lgpl_docs::purge(PATH, IGNORES);
    if cfg!(feature = "embed-lgpl-docs") {
        lgpl_docs::embed(lgpl_docs::Library::Cairo, PATH, IGNORES);
    }
}

#[cfg(not(any(feature = "embed-lgpl-docs", feature = "purge-lgpl-docs")))]
fn manage_docs() { }
<|endoftext|>


<commit_before>// Copyright 2016 Kyle Mayes
//
// 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.

//! Finds and links to the required `libclang` libraries.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]

extern crate glob;

use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

// Environment variables:
//
// * LLVM_CONFIG_PATH - provides a path to an `llvm-config` executable
// * LIBCLANG_PATH - provides a path to a directory containing a `libclang` shared library
// * LIBCLANG_STATIC_PATH - provides a path to a directory containing LLVM and Clang static libraries

/// Returns whether the supplied directory contains the supplied file.
fn contains<D: AsRef<Path>>(directory: D, file: &str) -> bool {
    directory.as_ref().join(file).exists()
}

/// Panics with a user friendly error message.
fn error(file: &str, env: &str) -> ! {
    panic!("could not find {0}, set the {1} environment variable to a path where {0} can be found", file, env);
}

/// Runs a console command, returning the output if the command was successfully executed.
fn run(command: &str, arguments: &[&str]) -> Option<String> {
    Command::new(command).args(arguments).output().map(|o| {
        String::from_utf8_lossy(&o.stdout).into_owned()
    }).ok()
}

/// Runs `llvm-config`, returning the output if the command was successfully executed.
fn run_llvm_config(arguments: &[&str]) -> Option<String> {
    run(&env::var("LLVM_CONFIG_PATH").unwrap_or("llvm-config".into()), arguments)
}

/// Backup search directory globs for FreeBSD and Linux.
const SEARCH_LINUX: &'static [&'static str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib",
];

/// Backup search directory globs for OS X.
const SEARCH_OSX: &'static [&'static str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// Backup search directory globs for Windows.
const SEARCH_WINDOWS: &'static [&'static str] = &[
    "C:\\LLVM\\bin",
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\bin",
    "C:\\Program Files*\\LLVM\\lib",
];

/// Searches for a library, returning the directory it can be found in if the search was successful.
fn find(file: &str, env: &str) -> Option<PathBuf> {
    // Search the directory provided by the relevant environment variable, if set.
    if let Some(directory) = env::var(env).map(|d| Path::new(&d).to_path_buf()).ok() {
        if contains(&directory, file) {
            return Some(directory);
        }

        // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
        // `libclang.lib` is usually found in the LLVM `lib` directory. Search the other if one is
        // specified with `LIBCLANG_PATH`.
        if cfg!(target_os="windows") {
            let suffix = if directory.ends_with("lib") {
                Some("bin")
            } else if directory.ends_with("bin") {
                Some("lib")
            } else {
                None
            };
            if let Some(suffix) = suffix {
                let alternative = directory.parent().unwrap().join(suffix);
                if contains(&alternative, file) {
                    return Some(alternative);
                }
            }
        }
    }

    // Search the `bin` and `lib` subdirectories in the directory returned by
    // `llvm-config --prefix`, if `llvm-config` is available.
    if let Some(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        let bin = directory.join("bin");
        if contains(&bin, file) {
            return Some(bin);
        }
        let lib = directory.join("lib");
        if contains(&lib, file) {
            return Some(lib);
        }
    }

    // Search the backup directories.
    let search = if cfg!(any(target_os="freebsd", target_os="linux")) {
        SEARCH_LINUX
    } else if cfg!(target_os="macos") {
        SEARCH_OSX
    } else if cfg!(target_os="windows") {
        SEARCH_WINDOWS
    } else {
        return None;
    };
    for pattern in search {
        let mut options = MatchOptions::new();
        options.case_sensitive = false;
        options.require_literal_separator = true;
        if let Ok(paths) = glob::glob_with(pattern, &options) {
            for path in paths.filter_map(Result::ok).filter(|p| p.is_dir()) {
                if contains(&path, file) {
                    return Some(path);
                }
            }
        }
    }
    None
}

/// Returns the name of an LLVM or Clang library from a path.
fn get_library_name(path: &Path) -> Option<String> {
    path.file_stem().map(|l| l.to_string_lossy()[3..].into())
}

/// Returns the LLVM libraries required to link to `libclang` statically.
fn get_llvm_libraries() -> Vec<String> {
    run_llvm_config(&["--libs"]).expect(
        "could not execute `llvm-config --libs`, set the LLVM_CONFIG_PATH environment variable to \
         a path to an `llvm-config` executable"
    ).split_whitespace().filter_map(|p| {
        // Depending on the version of `llvm-config` in use, listed libraries may be in one of two
        // forms, a full path to the library or simply prefixed with `-l`.
        if p.starts_with("-l") {
            Some(p[2..].into())
        } else {
            get_library_name(Path::new(p))
        }
    }).collect()
}

/// Clang libraries required to link to `libclang` 3.5 and later statically.
const CLANG_LIBRARIES: &'static [&'static str] = &[
    "clang",
    "clangAST",
    "clangAnalysis",
    "clangBasic",
    "clangDriver",
    "clangEdit",
    "clangFrontend",
    "clangIndex",
    "clangLex",
    "clangParse",
    "clangRewrite",
    "clangSema",
    "clangSerialization",
];

/// Returns the Clang libraries required to link to `libclang` statically.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
    let pattern = directory.as_ref().join("libclang*.a").to_string_lossy().to_string();
    if let Ok(libraries) = glob::glob(&pattern) {
        libraries.filter_map(|l| l.ok().and_then(|l| get_library_name(&l))).collect()
    } else {
        CLANG_LIBRARIES.iter().map(|l| l.to_string()).collect()
    }
}

fn main() {
    if cfg!(feature="static") {
        // Find LLVM and Clang static libraries.
        let directory = match find("libclang.a", "LIBCLANG_STATIC_PATH") {
            Some(directory) => directory,
            _ => error("libclang.a", "LIBCLANG_STATIC_PATH"),
        };

        print!("cargo:rustc-flags=");

        // Specify required LLVM and Clang static libraries.
        print!("-L {} ", directory.display());
        for library in get_llvm_libraries() {
            print!("-l static={} ", library);
        }
        for library in get_clang_libraries(&directory) {
            print!("-l static={} ", library);
        }

        // Specify required system libraries.
        if cfg!(any(target_os="freebsd", target_os="linux")) {
            println!("-l ffi -l ncursesw -l stdc++ -l z");
        } else if cfg!(target_os="macos") {
            println!("-l ffi -l ncurses -l stdc++ -l z");
        } else {
            panic!("unsupported operating system for static linking");
        }
    } else {
        let file = if cfg!(target_os="windows") {
            // The filename of the `libclang` shared library on Windows is `libclang.dll` instead of
            // the expected `clang.dll`.
            "libclang.dll".into()
        } else {
            format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)
        };

        // Find the `libclang` shared library.
        let directory = match find(&file, "LIBCLANG_PATH") {
            Some(directory) => directory,
            _ => error(&file, "LIBCLANG_PATH")
        };

        println!("cargo:rustc-link-search={}", directory.display());
        if cfg!(all(target_os="windows", target_env="msvc")) {
            // Find the `libclang` stub static library required for the MSVC toolchain.
            let directory = match find("libclang.lib", "LIBCLANG_PATH") {
                Some(directory) => directory,
                _ => error("libclang.lib", "LIBCLANG_PATH"),
            };

            println!("cargo:rustc-link-search={}", directory.display());
            println!("cargo:rustc-link-lib=dylib=libclang");
        } else {
            println!("cargo:rustc-link-lib=dylib=clang");
        }
    }
}
<commit_msg>Simplify build script<commit_after>// Copyright 2016 Kyle Mayes
//
// 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.

//! Finds and links to the required `libclang` libraries.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]

extern crate glob;

use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

// Environment variables:
//
// * LLVM_CONFIG_PATH - provides a path to an `llvm-config` executable
// * LIBCLANG_PATH - provides a path to a directory containing a `libclang` shared library
// * LIBCLANG_STATIC_PATH - provides a path to a directory containing LLVM and Clang static libraries

/// Returns whether the supplied directory contains the supplied file.
fn contains<D: AsRef<Path>>(directory: D, file: &str) -> bool {
    directory.as_ref().join(file).exists()
}

/// Runs a console command, returning the output if the command was successfully executed.
fn run(command: &str, arguments: &[&str]) -> Option<String> {
    Command::new(command).args(arguments).output().map(|o| {
        String::from_utf8_lossy(&o.stdout).into_owned()
    }).ok()
}

/// Runs `llvm-config`, returning the output if the command was successfully executed.
fn run_llvm_config(arguments: &[&str]) -> Option<String> {
    run(&env::var("LLVM_CONFIG_PATH").unwrap_or("llvm-config".into()), arguments)
}

/// Backup search directory globs for FreeBSD and Linux.
const SEARCH_LINUX: &'static [&'static str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib",
];

/// Backup search directory globs for OS X.
const SEARCH_OSX: &'static [&'static str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// Backup search directory globs for Windows.
const SEARCH_WINDOWS: &'static [&'static str] = &[
    "C:\\LLVM\\bin",
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\bin",
    "C:\\Program Files*\\LLVM\\lib",
];

/// Searches for a library, returning the directory it can be found in if the search was successful.
fn find(file: &str, env: &str) -> Result<PathBuf, String> {
    // Search the directory provided by the relevant environment variable, if set.
    if let Some(directory) = env::var(env).map(|d| Path::new(&d).to_path_buf()).ok() {
        if contains(&directory, file) {
            return Ok(directory);
        }

        // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
        // `libclang.lib` is usually found in the LLVM `lib` directory. Search the other if one is
        // specified with `LIBCLANG_PATH`.
        if cfg!(target_os="windows") {
            let suffix = if directory.ends_with("lib") {
                Some("bin")
            } else if directory.ends_with("bin") {
                Some("lib")
            } else {
                None
            };
            if let Some(suffix) = suffix {
                let alternative = directory.parent().unwrap().join(suffix);
                if contains(&alternative, file) {
                    return Ok(alternative);
                }
            }
        }
    }

    // Search the `bin` and `lib` subdirectories in the directory returned by
    // `llvm-config --prefix`, if `llvm-config` is available.
    if let Some(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        let bin = directory.join("bin");
        if contains(&bin, file) {
            return Ok(bin);
        }
        let lib = directory.join("lib");
        if contains(&lib, file) {
            return Ok(lib);
        }
    }

    // Search the backup directories.
    let search = if cfg!(any(target_os="freebsd", target_os="linux")) {
        SEARCH_LINUX
    } else if cfg!(target_os="macos") {
        SEARCH_OSX
    } else if cfg!(target_os="windows") {
        SEARCH_WINDOWS
    } else {
        &[]
    };
    for pattern in search {
        let mut options = MatchOptions::new();
        options.case_sensitive = false;
        options.require_literal_separator = true;
        if let Ok(paths) = glob::glob_with(pattern, &options) {
            for path in paths.filter_map(Result::ok).filter(|p| p.is_dir()) {
                if contains(&path, file) {
                    return Ok(path);
                }
            }
        }
    }
    let message = format!(
        "couldn't find '{0}', set the {1} environment variable to a path where '{0}' can be found",
        file,
        env,
    );
    Err(message)
}

/// Returns the name of an LLVM or Clang library from a path.
fn get_library_name(path: &Path) -> Option<String> {
    path.file_stem().map(|l| l.to_string_lossy()[3..].into())
}

/// Returns the LLVM libraries required to link to `libclang` statically.
fn get_llvm_libraries() -> Vec<String> {
    run_llvm_config(&["--libs"]).expect(
        "couldn't execute `llvm-config --libs`, set the LLVM_CONFIG_PATH environment variable to \
         a path to an `llvm-config` executable"
    ).split_whitespace().filter_map(|p| {
        // Depending on the version of `llvm-config` in use, listed libraries may be in one of two
        // forms, a full path to the library or simply prefixed with `-l`.
        if p.starts_with("-l") {
            Some(p[2..].into())
        } else {
            get_library_name(Path::new(p))
        }
    }).collect()
}

/// Clang libraries required to link to `libclang` 3.5 and later statically.
const CLANG_LIBRARIES: &'static [&'static str] = &[
    "clang",
    "clangAST",
    "clangAnalysis",
    "clangBasic",
    "clangDriver",
    "clangEdit",
    "clangFrontend",
    "clangIndex",
    "clangLex",
    "clangParse",
    "clangRewrite",
    "clangSema",
    "clangSerialization",
];

/// Returns the Clang libraries required to link to `libclang` statically.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
    let pattern = directory.as_ref().join("libclang*.a").to_string_lossy().to_string();
    if let Ok(libraries) = glob::glob(&pattern) {
        libraries.filter_map(|l| l.ok().and_then(|l| get_library_name(&l))).collect()
    } else {
        CLANG_LIBRARIES.iter().map(|l| l.to_string()).collect()
    }
}

fn main() {
    if cfg!(feature="static") {
        // Find LLVM and Clang static libraries.
        let directory = find("libclang.a", "LIBCLANG_STATIC_PATH").unwrap();

        print!("cargo:rustc-flags=");

        // Specify required LLVM and Clang static libraries.
        print!("-L {} ", directory.display());
        for library in get_llvm_libraries() {
            print!("-l static={} ", library);
        }
        for library in get_clang_libraries(&directory) {
            print!("-l static={} ", library);
        }

        // Specify required system libraries.
        if cfg!(any(target_os="freebsd", target_os="linux")) {
            println!("-l ffi -l ncursesw -l stdc++ -l z");
        } else if cfg!(target_os="macos") {
            println!("-l ffi -l ncurses -l stdc++ -l z");
        } else {
            panic!("unsupported operating system for static linking");
        }
    } else {
        let file = if cfg!(target_os="windows") {
            // The filename of the `libclang` shared library on Windows is `libclang.dll` instead of
            // the expected `clang.dll`.
            "libclang.dll".into()
        } else {
            format!("{}clang{}", env::consts::DLL_PREFIX, env::consts::DLL_SUFFIX)
        };

        // Find the `libclang` shared library.
        let directory = find(&file, "LIBCLANG_PATH").unwrap();

        println!("cargo:rustc-link-search={}", directory.display());
        if cfg!(all(target_os="windows", target_env="msvc")) {
            // Find the `libclang` stub static library required for the MSVC toolchain.
            let directory = find("libclang.lib", "LIBCLANG_PATH").unwrap();

            println!("cargo:rustc-link-search={}", directory.display());
            println!("cargo:rustc-link-lib=dylib=libclang");
        } else {
            println!("cargo:rustc-link-lib=dylib=clang");
        }
    }
}
<|endoftext|>