<commit_before>use api_client::ApiClient;
use serde_json;
use serde_json::Value;
use std::collections::HashMap;
use std::io;
use std::io::{Cursor, Read};
use std::io::ErrorKind as IoErrorKind;
use utils::decode_list;
use errors::*;

chef_json_type!(EnvironmentJsonClass, "Chef::Environment");
chef_json_type!(EnvironmentChefType, "environment");

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Environment {
    #[serde(default)]
    pub name: Option<String>,
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    chef_type: EnvironmentChefType,
    #[serde(default)]
    json_class: EnvironmentJsonClass,
    #[serde(default)]
    pub cookbook_versions: HashMap<String, Value>,
    #[serde(default)]
    pub default_attributes: HashMap<String, Value>,
    #[serde(default, rename = "override")]
    pub override_attributes: HashMap<String, Value>,
}

impl Read for Environment {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Ok(environment) = serde_json::to_vec(self) {
            let mut environment = Cursor::new(environment.as_ref() as &[u8]);
            Read::read(&mut environment, buf)
        } else {
            Err(io::Error::new(
                IoErrorKind::InvalidData,
                "Failed to convert environment to JSON",
            ))
        }
    }
}

impl Environment {
    pub fn new<S>(name: S) -> Environment
    where
        S: Into<String>,
    {
        Environment {
            name: Some(name.into()),
            ..Default::default()
        }
    }

    pub fn fetch<S: Into<String>>(client: &ApiClient, name: S) -> Result<Environment> {
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name.into());
        client
            .get(path.as_ref())
            .and_then(|r| r.from_json::<Environment>())
    }

    pub fn save(&self, client: &ApiClient) -> Result<Environment> {
        let name = &self.name.clone().unwrap();
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name);
        client
            .put(path.as_ref(), self)
            .and_then(|r| r.from_json::<Environment>())
    }

    pub fn delete(&self, client: &ApiClient) -> Result<Environment> {
        let name = &self.name.clone().unwrap();
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name);
        client
            .delete(path.as_ref())
            .and_then(|r| r.from_json::<Environment>())
    }

    pub fn from_json<R>(r: R) -> Result<Environment>
    where
        R: Read,
    {
        Ok(try!(serde_json::from_reader::<R, Environment>(r)))
    }
}

pub fn delete_environment(client: &ApiClient, name: &str) -> Result<Environment> {
    let org = &client.config.organization_path();
    let path = format!("{}/environments/{}", org, name);
    client
        .delete(path.as_ref())
        .and_then(|r| r.from_json::<Environment>())
}

#[derive(Debug)]
pub struct EnvironmentList {
    count: usize,
    environments: Vec<String>,
    client: ApiClient,
}

impl EnvironmentList {
    pub fn new(client: &ApiClient) -> EnvironmentList {
        let org = &client.config.organization_path();
        let path = format!("{}/environments", org);
        client
            .get(path.as_ref())
            .and_then(decode_list)
            .and_then(|list| {
                Ok(EnvironmentList {
                    environments: list,
                    count: 0,
                    client: client.clone(),
                })
            })
            .unwrap()
    }
}

impl Iterator for EnvironmentList {
    type Item = Result<Environment>;

    fn count(self) -> usize {
        self.environments.len()
    }

    fn next(&mut self) -> Option<Result<Environment>> {
        if self.environments.len() >= 1 {
            Some(Environment::fetch(
                &self.client,
                self.environments.remove(0),
            ))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Environment;
    use std::fs::File;

    #[test]
    fn test_environment_from_file() {
        let fh = File::open("fixtures/environment.json").unwrap();
        let environment = Environment::from_json(fh).unwrap();
        assert_eq!(environment.name.unwrap(), "test")
    }
}
<commit_msg>Cookbook versions Replace HashMap with String String<commit_after>use api_client::ApiClient;
use serde_json;
use serde_json::Value;
use std::collections::HashMap;
use std::io;
use std::io::{Cursor, Read};
use std::io::ErrorKind as IoErrorKind;
use utils::decode_list;
use errors::*;

chef_json_type!(EnvironmentJsonClass, "Chef::Environment");
chef_json_type!(EnvironmentChefType, "environment");

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Environment {
    #[serde(default)]
    pub name: Option<String>,
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    chef_type: EnvironmentChefType,
    #[serde(default)]
    json_class: EnvironmentJsonClass,
    #[serde(default)]
    pub cookbook_versions: HashMap<String, String>,
    #[serde(default)]
    pub default_attributes: HashMap<String, Value>,
    #[serde(default, rename = "override")]
    pub override_attributes: HashMap<String, Value>,
}

impl Read for Environment {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if let Ok(environment) = serde_json::to_vec(self) {
            let mut environment = Cursor::new(environment.as_ref() as &[u8]);
            Read::read(&mut environment, buf)
        } else {
            Err(io::Error::new(
                IoErrorKind::InvalidData,
                "Failed to convert environment to JSON",
            ))
        }
    }
}

impl Environment {
    pub fn new<S>(name: S) -> Environment
    where
        S: Into<String>,
    {
        Environment {
            name: Some(name.into()),
            ..Default::default()
        }
    }

    pub fn fetch<S: Into<String>>(client: &ApiClient, name: S) -> Result<Environment> {
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name.into());
        client.get(path.as_ref()).and_then(
            |r| r.from_json::<Environment>(),
        )
    }

    pub fn save(&self, client: &ApiClient) -> Result<Environment> {
        let name = &self.name.clone().unwrap();
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name);
        client.put(path.as_ref(), self).and_then(|r| {
            r.from_json::<Environment>()
        })
    }

    pub fn delete(&self, client: &ApiClient) -> Result<Environment> {
        let name = &self.name.clone().unwrap();
        let org = &client.config.organization_path();
        let path = format!("{}/environments/{}", org, name);
        client.delete(path.as_ref()).and_then(
            |r| r.from_json::<Environment>(),
        )
    }

    pub fn from_json<R>(r: R) -> Result<Environment>
    where
        R: Read,
    {
        Ok(try!(serde_json::from_reader::<R, Environment>(r)))
    }
}

pub fn delete_environment(client: &ApiClient, name: &str) -> Result<Environment> {
    let org = &client.config.organization_path();
    let path = format!("{}/environments/{}", org, name);
    client.delete(path.as_ref()).and_then(
        |r| r.from_json::<Environment>(),
    )
}

#[derive(Debug)]
pub struct EnvironmentList {
    count: usize,
    environments: Vec<String>,
    client: ApiClient,
}

impl EnvironmentList {
    pub fn new(client: &ApiClient) -> EnvironmentList {
        let org = &client.config.organization_path();
        let path = format!("{}/environments", org);
        client
            .get(path.as_ref())
            .and_then(decode_list)
            .and_then(|list| {
                Ok(EnvironmentList {
                    environments: list,
                    count: 0,
                    client: client.clone(),
                })
            })
            .unwrap()
    }
}

impl Iterator for EnvironmentList {
    type Item = Result<Environment>;

    fn count(self) -> usize {
        self.environments.len()
    }

    fn next(&mut self) -> Option<Result<Environment>> {
        if self.environments.len() >= 1 {
            Some(Environment::fetch(
                &self.client,
                self.environments.remove(0),
            ))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Environment;
    use std::fs::File;

    #[test]
    fn test_environment_from_file() {
        let fh = File::open("fixtures/environment.json").unwrap();
        let environment = Environment::from_json(fh).unwrap();
        assert_eq!(environment.name.unwrap(), "test")
    }
}
<|endoftext|>


<commit_before>use slog::{error, trace, warn};
use std::fmt;
use wayland_server::{
    backend::{ClientId, ObjectId},
    protocol::{
        wl_keyboard::{self, KeyState as WlKeyState, KeymapFormat, WlKeyboard},
        wl_surface::WlSurface,
    },
    Dispatch, DisplayHandle, Resource,
};

use super::WaylandFocus;
use crate::{
    backend::input::KeyState,
    input::{
        keyboard::{KeyboardHandle, KeyboardTarget, KeysymHandle, ModifiersState},
        Seat, SeatHandler, SeatState,
    },
    utils::Serial,
};

impl<D: SeatHandler + 'static> KeyboardHandle<D>
where
    D: SeatHandler + 'static,
    <D as SeatHandler>::KeyboardFocus: WaylandFocus,
{
    /// Check if client of given resource currently has keyboard focus
    pub fn client_of_object_has_focus(&self, id: &ObjectId) -> bool {
        self.arc
            .internal
            .lock()
            .unwrap()
            .focus
            .as_ref()
            .map(|f| f.0.same_client_as(id))
            .unwrap_or(false)
    }

    /// Register a new keyboard to this handler
    ///
    /// The keymap will automatically be sent to it
    ///
    /// This should be done first, before anything else is done with this keyboard.
    pub(crate) fn new_kbd(&self, kbd: WlKeyboard) {
        trace!(self.arc.logger, "Sending keymap to client");

        // prepare a tempfile with the keymap, to send it to the client
        let ret = self.arc.keymap.with_fd(kbd.version() >= 7, |fd, size| {
            kbd.keymap(KeymapFormat::XkbV1, fd, size as u32);
        });

        if let Err(e) = ret {
            warn!(self.arc.logger,
                "Failed write keymap to client in a tempfile";
                "err" => format!("{:?}", e)
            );
            return;
        };

        let guard = self.arc.internal.lock().unwrap();
        if kbd.version() >= 4 {
            kbd.repeat_info(guard.repeat_rate, guard.repeat_delay);
        }
        if let Some((focused, serial)) = guard.focus.as_ref() {
            if focused.same_client_as(&kbd.id()) {
                let serialized = guard.mods_state.serialized;
                let keys = serialize_pressed_keys(guard.pressed_keys.clone());
                kbd.enter((*serial).into(), focused.wl_surface().unwrap(), keys);
                // Modifiers must be send after enter event.
                kbd.modifiers(
                    (*serial).into(),
                    serialized.depressed,
                    serialized.latched,
                    serialized.locked,
                    serialized.layout_locked,
                );
            }
        }
        self.arc.known_kbds.lock().unwrap().push(kbd);
    }
}

/// User data for keyboard
pub struct KeyboardUserData<D: SeatHandler> {
    pub(crate) handle: Option<KeyboardHandle<D>>,
}

impl<D: SeatHandler> fmt::Debug for KeyboardUserData<D>
where
    <D as SeatHandler>::KeyboardFocus: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("KeyboardUserData")
            .field("handle", &self.handle)
            .finish()
    }
}

impl<D> Dispatch<WlKeyboard, KeyboardUserData<D>, D> for SeatState<D>
where
    D: 'static + Dispatch<WlKeyboard, KeyboardUserData<D>>,
    D: SeatHandler,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        _resource: &WlKeyboard,
        _request: wl_keyboard::Request,
        _data: &KeyboardUserData<D>,
        _dhandle: &DisplayHandle,
        _data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
    }

    fn destroyed(_state: &mut D, _client_id: ClientId, object_id: ObjectId, data: &KeyboardUserData<D>) {
        if let Some(ref handle) = data.handle {
            handle
                .arc
                .known_kbds
                .lock()
                .unwrap()
                .retain(|k| k.id() != object_id)
        }
    }
}

pub fn for_each_focused_kbds<D: SeatHandler + 'static>(
    seat: &Seat<D>,
    surface: &WlSurface,
    mut f: impl FnMut(WlKeyboard),
) {
    if let Some(keyboard) = seat.get_keyboard() {
        let inner = keyboard.arc.known_kbds.lock().unwrap();
        for kbd in &*inner {
            if kbd.id().same_client_as(&surface.id()) {
                f(kbd.clone())
            }
        }
    }
}

fn serialize_pressed_keys(keys: Vec<u32>) -> Vec<u8> {
    let serialized = unsafe { ::std::slice::from_raw_parts(keys.as_ptr() as *const u8, keys.len() * 4) };
    serialized.into()
}

impl<D: SeatHandler + 'static> KeyboardTarget<D> for WlSurface {
    fn enter(&self, seat: &Seat<D>, _data: &mut D, keys: Vec<KeysymHandle<'_>>, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| {
            kbd.enter(
                serial.into(),
                self,
                serialize_pressed_keys(keys.iter().map(|h| h.raw_code() - 8).collect()),
            )
        })
    }

    fn leave(&self, seat: &Seat<D>, _data: &mut D, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| kbd.leave(serial.into(), self))
    }

    fn key(
        &self,
        seat: &Seat<D>,
        _data: &mut D,
        key: KeysymHandle<'_>,
        state: KeyState,
        serial: Serial,
        time: u32,
    ) {
        for_each_focused_kbds(seat, self, |kbd| {
            kbd.key(serial.into(), time, key.raw_code() - 8, state.into())
        })
    }

    fn modifiers(&self, seat: &Seat<D>, _data: &mut D, modifiers: ModifiersState, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| {
            let modifiers = modifiers.serialized;
            kbd.modifiers(
                serial.into(),
                modifiers.depressed,
                modifiers.latched,
                modifiers.locked,
                modifiers.layout_locked,
            );
        })
    }
}

impl From<KeyState> for WlKeyState {
    fn from(state: KeyState) -> WlKeyState {
        match state {
            KeyState::Pressed => WlKeyState::Pressed,
            KeyState::Released => WlKeyState::Released,
        }
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Unknown KeyState {0:?}")]
pub struct UnknownKeyState(WlKeyState);

impl TryFrom<WlKeyState> for KeyState {
    type Error = UnknownKeyState;
    fn try_from(state: WlKeyState) -> Result<Self, Self::Error> {
        match state {
            WlKeyState::Pressed => Ok(KeyState::Pressed),
            WlKeyState::Released => Ok(KeyState::Released),
            x => Err(UnknownKeyState(x)),
        }
    }
}
<commit_msg>Make it pub crate<commit_after>use slog::{error, trace, warn};
use std::fmt;
use wayland_server::{
    backend::{ClientId, ObjectId},
    protocol::{
        wl_keyboard::{self, KeyState as WlKeyState, KeymapFormat, WlKeyboard},
        wl_surface::WlSurface,
    },
    Dispatch, DisplayHandle, Resource,
};

use super::WaylandFocus;
use crate::{
    backend::input::KeyState,
    input::{
        keyboard::{KeyboardHandle, KeyboardTarget, KeysymHandle, ModifiersState},
        Seat, SeatHandler, SeatState,
    },
    utils::Serial,
};

impl<D: SeatHandler + 'static> KeyboardHandle<D>
where
    D: SeatHandler + 'static,
    <D as SeatHandler>::KeyboardFocus: WaylandFocus,
{
    /// Check if client of given resource currently has keyboard focus
    pub fn client_of_object_has_focus(&self, id: &ObjectId) -> bool {
        self.arc
            .internal
            .lock()
            .unwrap()
            .focus
            .as_ref()
            .map(|f| f.0.same_client_as(id))
            .unwrap_or(false)
    }

    /// Register a new keyboard to this handler
    ///
    /// The keymap will automatically be sent to it
    ///
    /// This should be done first, before anything else is done with this keyboard.
    pub(crate) fn new_kbd(&self, kbd: WlKeyboard) {
        trace!(self.arc.logger, "Sending keymap to client");

        // prepare a tempfile with the keymap, to send it to the client
        let ret = self.arc.keymap.with_fd(kbd.version() >= 7, |fd, size| {
            kbd.keymap(KeymapFormat::XkbV1, fd, size as u32);
        });

        if let Err(e) = ret {
            warn!(self.arc.logger,
                "Failed write keymap to client in a tempfile";
                "err" => format!("{:?}", e)
            );
            return;
        };

        let guard = self.arc.internal.lock().unwrap();
        if kbd.version() >= 4 {
            kbd.repeat_info(guard.repeat_rate, guard.repeat_delay);
        }
        if let Some((focused, serial)) = guard.focus.as_ref() {
            if focused.same_client_as(&kbd.id()) {
                let serialized = guard.mods_state.serialized;
                let keys = serialize_pressed_keys(guard.pressed_keys.clone());
                kbd.enter((*serial).into(), focused.wl_surface().unwrap(), keys);
                // Modifiers must be send after enter event.
                kbd.modifiers(
                    (*serial).into(),
                    serialized.depressed,
                    serialized.latched,
                    serialized.locked,
                    serialized.layout_locked,
                );
            }
        }
        self.arc.known_kbds.lock().unwrap().push(kbd);
    }
}

/// User data for keyboard
pub struct KeyboardUserData<D: SeatHandler> {
    pub(crate) handle: Option<KeyboardHandle<D>>,
}

impl<D: SeatHandler> fmt::Debug for KeyboardUserData<D>
where
    <D as SeatHandler>::KeyboardFocus: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("KeyboardUserData")
            .field("handle", &self.handle)
            .finish()
    }
}

impl<D> Dispatch<WlKeyboard, KeyboardUserData<D>, D> for SeatState<D>
where
    D: 'static + Dispatch<WlKeyboard, KeyboardUserData<D>>,
    D: SeatHandler,
{
    fn request(
        _state: &mut D,
        _client: &wayland_server::Client,
        _resource: &WlKeyboard,
        _request: wl_keyboard::Request,
        _data: &KeyboardUserData<D>,
        _dhandle: &DisplayHandle,
        _data_init: &mut wayland_server::DataInit<'_, D>,
    ) {
    }

    fn destroyed(_state: &mut D, _client_id: ClientId, object_id: ObjectId, data: &KeyboardUserData<D>) {
        if let Some(ref handle) = data.handle {
            handle
                .arc
                .known_kbds
                .lock()
                .unwrap()
                .retain(|k| k.id() != object_id)
        }
    }
}

pub(crate) fn for_each_focused_kbds<D: SeatHandler + 'static>(
    seat: &Seat<D>,
    surface: &WlSurface,
    mut f: impl FnMut(WlKeyboard),
) {
    if let Some(keyboard) = seat.get_keyboard() {
        let inner = keyboard.arc.known_kbds.lock().unwrap();
        for kbd in &*inner {
            if kbd.id().same_client_as(&surface.id()) {
                f(kbd.clone())
            }
        }
    }
}

fn serialize_pressed_keys(keys: Vec<u32>) -> Vec<u8> {
    let serialized = unsafe { ::std::slice::from_raw_parts(keys.as_ptr() as *const u8, keys.len() * 4) };
    serialized.into()
}

impl<D: SeatHandler + 'static> KeyboardTarget<D> for WlSurface {
    fn enter(&self, seat: &Seat<D>, _data: &mut D, keys: Vec<KeysymHandle<'_>>, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| {
            kbd.enter(
                serial.into(),
                self,
                serialize_pressed_keys(keys.iter().map(|h| h.raw_code() - 8).collect()),
            )
        })
    }

    fn leave(&self, seat: &Seat<D>, _data: &mut D, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| kbd.leave(serial.into(), self))
    }

    fn key(
        &self,
        seat: &Seat<D>,
        _data: &mut D,
        key: KeysymHandle<'_>,
        state: KeyState,
        serial: Serial,
        time: u32,
    ) {
        for_each_focused_kbds(seat, self, |kbd| {
            kbd.key(serial.into(), time, key.raw_code() - 8, state.into())
        })
    }

    fn modifiers(&self, seat: &Seat<D>, _data: &mut D, modifiers: ModifiersState, serial: Serial) {
        for_each_focused_kbds(seat, self, |kbd| {
            let modifiers = modifiers.serialized;
            kbd.modifiers(
                serial.into(),
                modifiers.depressed,
                modifiers.latched,
                modifiers.locked,
                modifiers.layout_locked,
            );
        })
    }
}

impl From<KeyState> for WlKeyState {
    fn from(state: KeyState) -> WlKeyState {
        match state {
            KeyState::Pressed => WlKeyState::Pressed,
            KeyState::Released => WlKeyState::Released,
        }
    }
}

#[derive(Debug, thiserror::Error)]
#[error("Unknown KeyState {0:?}")]
pub struct UnknownKeyState(WlKeyState);

impl TryFrom<WlKeyState> for KeyState {
    type Error = UnknownKeyState;
    fn try_from(state: WlKeyState) -> Result<Self, Self::Error> {
        match state {
            WlKeyState::Pressed => Ok(KeyState::Pressed),
            WlKeyState::Released => Ok(KeyState::Released),
            x => Err(UnknownKeyState(x)),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use libc::O_DIRECT;
use std::ffi::CString;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{self, BufRead, BufReader, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::mpsc::sync_channel;
use std::sync::Once;
use std::thread;
use std::time::Duration;
use std::{env, process::Child};

use anyhow::{anyhow, Result};
use base::syslog;
use tempfile::TempDir;

const PREBUILT_URL: &str = "https://storage.googleapis.com/chromeos-localmirror/distfiles";

#[cfg(target_arch = "x86_64")]
const ARCH: &str = "x86_64";
#[cfg(target_arch = "arm")]
const ARCH: &str = "arm";
#[cfg(target_arch = "aarch64")]
const ARCH: &str = "aarch64";

/// Timeout for communicating with the VM. If we do not hear back, panic so we
/// do not block the tests.
const VM_COMMUNICATION_TIMEOUT: Duration = Duration::from_secs(10);

fn prebuilt_version() -> &'static str {
    include_str!("../guest_under_test/PREBUILT_VERSION").trim()
}

fn kernel_prebuilt_url() -> String {
    format!(
        "{}/crosvm-testing-bzimage-{}-{}",
        PREBUILT_URL,
        ARCH,
        prebuilt_version()
    )
}

fn rootfs_prebuilt_url() -> String {
    format!(
        "{}/crosvm-testing-rootfs-{}-{}",
        PREBUILT_URL,
        ARCH,
        prebuilt_version()
    )
}

/// The kernel bzImage is stored next to the test executable, unless overridden by
/// CROSVM_CARGO_TEST_KERNEL_BINARY
fn kernel_path() -> PathBuf {
    match env::var("CROSVM_CARGO_TEST_KERNEL_BINARY") {
        Ok(value) => PathBuf::from(value),
        Err(_) => env::current_exe()
            .unwrap()
            .parent()
            .unwrap()
            .join("bzImage"),
    }
}

/// The rootfs image is stored next to the test executable, unless overridden by
/// CROSVM_CARGO_TEST_ROOTFS_IMAGE
fn rootfs_path() -> PathBuf {
    match env::var("CROSVM_CARGO_TEST_ROOTFS_IMAGE") {
        Ok(value) => PathBuf::from(value),
        Err(_) => env::current_exe().unwrap().parent().unwrap().join("rootfs"),
    }
}

/// The crosvm binary is expected to be alongside to the integration tests
/// binary. Alternatively in the parent directory (cargo will put the
/// test binary in target/debug/deps/ but the crosvm binary in target/debug).
fn find_crosvm_binary() -> PathBuf {
    let exe_dir = env::current_exe().unwrap().parent().unwrap().to_path_buf();
    let first = exe_dir.join("crosvm");
    if first.exists() {
        return first;
    }
    let second = exe_dir.parent().unwrap().join("crosvm");
    if second.exists() {
        return second;
    }
    panic!("Cannot find ./crosvm or ../crosvm alongside test binary.");
}

/// Safe wrapper for libc::mkfifo
fn mkfifo(path: &Path) -> io::Result<()> {
    let cpath = CString::new(path.to_str().unwrap()).unwrap();
    let result = unsafe { libc::mkfifo(cpath.as_ptr(), 0o777) };
    if result == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    }
}

/// Run the provided closure, but panic if it does not complete until the timeout has passed.
/// We should panic here, as we cannot gracefully stop the closure from running.
fn panic_on_timeout<F, U>(closure: F, timeout: Duration) -> U
where
    F: FnOnce() -> U + Send + 'static,
    U: Send + 'static,
{
    let (tx, rx) = sync_channel::<()>(1);
    let handle = thread::spawn(move || {
        let result = closure();
        tx.send(()).unwrap();
        result
    });
    rx.recv_timeout(timeout)
        .expect("Operation timed out or closure paniced.");
    handle.join().unwrap()
}

fn download_file(url: &str, destination: &Path) -> Result<()> {
    let status = Command::new("curl")
        .arg("--fail")
        .arg("--location")
        .args(&["--output", destination.to_str().unwrap()])
        .arg(url)
        .status();
    match status {
        Ok(exit_code) => {
            if !exit_code.success() {
                Err(anyhow!("Cannot download {}", url))
            } else {
                Ok(())
            }
        }
        Err(error) => Err(anyhow!(error)),
    }
}

fn crosvm_command(command: &str, args: &[&str]) -> Result<()> {
    println!("$ crosvm {} {:?}", command, &args.join(" "));
    let status = Command::new(find_crosvm_binary())
        .arg(command)
        .args(args)
        .status()?;

    if !status.success() {
        Err(anyhow!("Command failed with exit code {}", status))
    } else {
        Ok(())
    }
}

/// Test fixture to spin up a VM running a guest that can be communicated with.
///
/// After creation, commands can be sent via exec_in_guest. The VM is stopped
/// when this instance is dropped.
pub struct TestVm {
    /// Maintain ownership of test_dir until the vm is destroyed.
    #[allow(dead_code)]
    test_dir: TempDir,
    from_guest_reader: BufReader<File>,
    to_guest: File,
    control_socket_path: PathBuf,
    process: Child,
    debug: bool,
}

impl TestVm {
    /// Magic line sent by the delegate binary when the guest is ready.
    const MAGIC_LINE: &'static str = "\x05Ready";

    /// Downloads prebuilts if needed.
    fn initialize_once() {
        if let Err(e) = syslog::init() {
            panic!("failed to initiailize syslog: {}", e);
        }

        // It's possible the prebuilts downloaded by crosvm-9999.ebuild differ
        // from the version that crosvm was compiled for.
        if let Ok(value) = env::var("CROSVM_CARGO_TEST_PREBUILT_VERSION") {
            if value != prebuilt_version() {
                panic!(
                    "Environment provided prebuilts are version {}, but crosvm was compiled \
                    for prebuilt version {}. Did you update PREBUILT_VERSION everywhere?",
                    value,
                    prebuilt_version()
                );
            }
        }

        let kernel_path = kernel_path();
        if env::var("CROSVM_CARGO_TEST_KERNEL_BINARY").is_err() {
            if !kernel_path.exists() {
                println!("Downloading kernel prebuilt:");
                download_file(&kernel_prebuilt_url(), &kernel_path).unwrap();
            }
        }
        assert!(kernel_path.exists(), "{:?} does not exist", kernel_path);

        let rootfs_path = rootfs_path();
        if env::var("CROSVM_CARGO_TEST_ROOTFS_IMAGE").is_err() {
            if !rootfs_path.exists() {
                println!("Downloading rootfs prebuilt:");
                download_file(&rootfs_prebuilt_url(), &rootfs_path).unwrap();
            }
        }
        assert!(rootfs_path.exists(), "{:?} does not exist", rootfs_path);

        // Check if the test file system is a known compatible one. Needs to support features like O_DIRECT.
        if let Err(e) = OpenOptions::new()
            .custom_flags(O_DIRECT)
            .write(false)
            .read(true)
            .open(rootfs_path)
        {
            panic!(
                "File open with O_DIRECT expected to work but did not: {}",
                e
            );
        }
    }

    // Adds 2 serial devices:
    // - ttyS0: Console device which prints kernel log / debug output of the
    //          delegate binary.
    // - ttyS1: Serial device attached to the named pipes.
    fn configure_serial_devices(
        command: &mut Command,
        from_guest_pipe: &Path,
        to_guest_pipe: &Path,
    ) {
        command.args(&["--serial", "type=syslog"]);

        // Setup channel for communication with the delegate.
        let serial_params = format!(
            "type=file,path={},input={},num=2",
            from_guest_pipe.display(),
            to_guest_pipe.display()
        );
        command.args(&["--serial", &serial_params]);
    }

    /// Configures the VM kernel and rootfs to load from the guest_under_test assets.
    fn configure_kernel(command: &mut Command, o_direct: bool) {
        let rootfs_and_option = format!(
            "{}{}",
            rootfs_path().to_str().unwrap(),
            if o_direct { ",o_direct=true" } else { "" }
        );
        command
            .args(&["--root", &rootfs_and_option])
            .args(&["--params", "init=/bin/delegate"])
            .arg(kernel_path());
    }

    /// Instanciate a new crosvm instance. The first call will trigger the download of prebuilt
    /// files if necessary.
    pub fn new(additional_arguments: &[&str], debug: bool, o_direct: bool) -> Result<TestVm> {
        static PREP_ONCE: Once = Once::new();
        PREP_ONCE.call_once(TestVm::initialize_once);

        // Create two named pipes to communicate with the guest.
        let test_dir = TempDir::new()?;
        let from_guest_pipe = test_dir.path().join("from_guest");
        let to_guest_pipe = test_dir.path().join("to_guest");
        mkfifo(&from_guest_pipe)?;
        mkfifo(&to_guest_pipe)?;

        let control_socket_path = test_dir.path().join("control");

        let mut command = Command::new(find_crosvm_binary());
        command.args(&["run", "--disable-sandbox"]);
        TestVm::configure_serial_devices(&mut command, &from_guest_pipe, &to_guest_pipe);
        command.args(&["--socket", control_socket_path.to_str().unwrap()]);
        command.args(additional_arguments);

        TestVm::configure_kernel(&mut command, o_direct);

        println!("$ {:?}", command);

        let process = command.spawn()?;

        // Open pipes. Panic if we cannot connect after a timeout.
        let (to_guest, from_guest) = panic_on_timeout(
            move || (File::create(to_guest_pipe), File::open(from_guest_pipe)),
            VM_COMMUNICATION_TIMEOUT,
        );

        // Wait for magic line to be received, indicating the delegate is ready.
        let mut from_guest_reader = BufReader::new(from_guest?);
        let mut magic_line = String::new();
        from_guest_reader.read_line(&mut magic_line)?;
        assert_eq!(magic_line.trim(), TestVm::MAGIC_LINE);

        Ok(TestVm {
            test_dir,
            from_guest_reader,
            to_guest: to_guest?,
            control_socket_path,
            process,
            debug,
        })
    }

    /// Executes the shell command `command` and returns the programs stdout.
    pub fn exec_in_guest(&mut self, command: &str) -> Result<String> {
        // Write command to serial port.
        writeln!(&mut self.to_guest, "{}", command)?;

        // We will receive an echo of what we have written on the pipe.
        let mut echo = String::new();
        self.from_guest_reader.read_line(&mut echo)?;
        assert_eq!(echo.trim(), command);

        // Return all remaining lines until we receive the MAGIC_LINE
        let mut output = String::new();
        loop {
            let mut line = String::new();
            self.from_guest_reader.read_line(&mut line)?;
            if line.trim() == TestVm::MAGIC_LINE {
                break;
            }
            output.push_str(&line);
        }
        let trimmed = output.trim();
        if self.debug {
            println!("<- {:?}", trimmed);
        }
        Ok(trimmed.to_string())
    }

    pub fn stop(&self) -> Result<()> {
        crosvm_command("stop", &[self.control_socket_path.to_str().unwrap()])
    }

    pub fn suspend(&self) -> Result<()> {
        crosvm_command("suspend", &[self.control_socket_path.to_str().unwrap()])
    }

    pub fn resume(&self) -> Result<()> {
        crosvm_command("resume", &[self.control_socket_path.to_str().unwrap()])
    }
}

impl Drop for TestVm {
    fn drop(&mut self) {
        self.stop().unwrap();
        self.process.wait().unwrap();
    }
}
<commit_msg>integration_tests: Print guest logs only when a test failed<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::ffi::CString;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{self, BufRead, BufReader, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str::from_utf8;
use std::sync::mpsc::sync_channel;
use std::sync::Once;
use std::thread;
use std::time::Duration;
use std::{env, process::Child};

use anyhow::{anyhow, Result};
use base::syslog;
use libc::O_DIRECT;
use tempfile::TempDir;

const PREBUILT_URL: &str = "https://storage.googleapis.com/chromeos-localmirror/distfiles";

#[cfg(target_arch = "x86_64")]
const ARCH: &str = "x86_64";
#[cfg(target_arch = "arm")]
const ARCH: &str = "arm";
#[cfg(target_arch = "aarch64")]
const ARCH: &str = "aarch64";

/// Timeout for communicating with the VM. If we do not hear back, panic so we
/// do not block the tests.
const VM_COMMUNICATION_TIMEOUT: Duration = Duration::from_secs(10);

fn prebuilt_version() -> &'static str {
    include_str!("../guest_under_test/PREBUILT_VERSION").trim()
}

fn kernel_prebuilt_url() -> String {
    format!(
        "{}/crosvm-testing-bzimage-{}-{}",
        PREBUILT_URL,
        ARCH,
        prebuilt_version()
    )
}

fn rootfs_prebuilt_url() -> String {
    format!(
        "{}/crosvm-testing-rootfs-{}-{}",
        PREBUILT_URL,
        ARCH,
        prebuilt_version()
    )
}

/// The kernel bzImage is stored next to the test executable, unless overridden by
/// CROSVM_CARGO_TEST_KERNEL_BINARY
fn kernel_path() -> PathBuf {
    match env::var("CROSVM_CARGO_TEST_KERNEL_BINARY") {
        Ok(value) => PathBuf::from(value),
        Err(_) => env::current_exe()
            .unwrap()
            .parent()
            .unwrap()
            .join("bzImage"),
    }
}

/// The rootfs image is stored next to the test executable, unless overridden by
/// CROSVM_CARGO_TEST_ROOTFS_IMAGE
fn rootfs_path() -> PathBuf {
    match env::var("CROSVM_CARGO_TEST_ROOTFS_IMAGE") {
        Ok(value) => PathBuf::from(value),
        Err(_) => env::current_exe().unwrap().parent().unwrap().join("rootfs"),
    }
}

/// The crosvm binary is expected to be alongside to the integration tests
/// binary. Alternatively in the parent directory (cargo will put the
/// test binary in target/debug/deps/ but the crosvm binary in target/debug).
fn find_crosvm_binary() -> PathBuf {
    let exe_dir = env::current_exe().unwrap().parent().unwrap().to_path_buf();
    let first = exe_dir.join("crosvm");
    if first.exists() {
        return first;
    }
    let second = exe_dir.parent().unwrap().join("crosvm");
    if second.exists() {
        return second;
    }
    panic!("Cannot find ./crosvm or ../crosvm alongside test binary.");
}

/// Safe wrapper for libc::mkfifo
fn mkfifo(path: &Path) -> io::Result<()> {
    let cpath = CString::new(path.to_str().unwrap()).unwrap();
    let result = unsafe { libc::mkfifo(cpath.as_ptr(), 0o777) };
    if result == 0 {
        Ok(())
    } else {
        Err(io::Error::last_os_error())
    }
}

/// Run the provided closure, but panic if it does not complete until the timeout has passed.
/// We should panic here, as we cannot gracefully stop the closure from running.
fn panic_on_timeout<F, U>(closure: F, timeout: Duration) -> U
where
    F: FnOnce() -> U + Send + 'static,
    U: Send + 'static,
{
    let (tx, rx) = sync_channel::<()>(1);
    let handle = thread::spawn(move || {
        let result = closure();
        tx.send(()).unwrap();
        result
    });
    rx.recv_timeout(timeout)
        .expect("Operation timed out or closure paniced.");
    handle.join().unwrap()
}

fn download_file(url: &str, destination: &Path) -> Result<()> {
    let status = Command::new("curl")
        .arg("--fail")
        .arg("--location")
        .args(&["--output", destination.to_str().unwrap()])
        .arg(url)
        .status();
    match status {
        Ok(exit_code) => {
            if !exit_code.success() {
                Err(anyhow!("Cannot download {}", url))
            } else {
                Ok(())
            }
        }
        Err(error) => Err(anyhow!(error)),
    }
}

/// Test fixture to spin up a VM running a guest that can be communicated with.
///
/// After creation, commands can be sent via exec_in_guest. The VM is stopped
/// when this instance is dropped.
pub struct TestVm {
    /// Maintain ownership of test_dir until the vm is destroyed.
    #[allow(dead_code)]
    test_dir: TempDir,
    from_guest_reader: BufReader<File>,
    to_guest: File,
    control_socket_path: PathBuf,
    process: Option<Child>, // Use `Option` to allow taking the ownership in `Drop::drop()`.
    debug: bool,
}

impl TestVm {
    /// Magic line sent by the delegate binary when the guest is ready.
    const MAGIC_LINE: &'static str = "\x05Ready";

    /// Downloads prebuilts if needed.
    fn initialize_once() {
        if let Err(e) = syslog::init() {
            panic!("failed to initiailize syslog: {}", e);
        }

        // It's possible the prebuilts downloaded by crosvm-9999.ebuild differ
        // from the version that crosvm was compiled for.
        if let Ok(value) = env::var("CROSVM_CARGO_TEST_PREBUILT_VERSION") {
            if value != prebuilt_version() {
                panic!(
                    "Environment provided prebuilts are version {}, but crosvm was compiled \
                    for prebuilt version {}. Did you update PREBUILT_VERSION everywhere?",
                    value,
                    prebuilt_version()
                );
            }
        }

        let kernel_path = kernel_path();
        if env::var("CROSVM_CARGO_TEST_KERNEL_BINARY").is_err() {
            if !kernel_path.exists() {
                println!("Downloading kernel prebuilt:");
                download_file(&kernel_prebuilt_url(), &kernel_path).unwrap();
            }
        }
        assert!(kernel_path.exists(), "{:?} does not exist", kernel_path);

        let rootfs_path = rootfs_path();
        if env::var("CROSVM_CARGO_TEST_ROOTFS_IMAGE").is_err() {
            if !rootfs_path.exists() {
                println!("Downloading rootfs prebuilt:");
                download_file(&rootfs_prebuilt_url(), &rootfs_path).unwrap();
            }
        }
        assert!(rootfs_path.exists(), "{:?} does not exist", rootfs_path);

        // Check if the test file system is a known compatible one. Needs to support features like O_DIRECT.
        if let Err(e) = OpenOptions::new()
            .custom_flags(O_DIRECT)
            .write(false)
            .read(true)
            .open(rootfs_path)
        {
            panic!(
                "File open with O_DIRECT expected to work but did not: {}",
                e
            );
        }
    }

    // Adds 2 serial devices:
    // - ttyS0: Console device which prints kernel log / debug output of the
    //          delegate binary.
    // - ttyS1: Serial device attached to the named pipes.
    fn configure_serial_devices(
        command: &mut Command,
        from_guest_pipe: &Path,
        to_guest_pipe: &Path,
    ) {
        command.args(&["--serial", "type=syslog"]);

        // Setup channel for communication with the delegate.
        let serial_params = format!(
            "type=file,path={},input={},num=2",
            from_guest_pipe.display(),
            to_guest_pipe.display()
        );
        command.args(&["--serial", &serial_params]);
    }

    /// Configures the VM kernel and rootfs to load from the guest_under_test assets.
    fn configure_kernel(command: &mut Command, o_direct: bool) {
        let rootfs_and_option = format!(
            "{}{}",
            rootfs_path().to_str().unwrap(),
            if o_direct { ",o_direct=true" } else { "" }
        );
        command
            .args(&["--root", &rootfs_and_option])
            .args(&["--params", "init=/bin/delegate"])
            .arg(kernel_path());
    }

    /// Instanciate a new crosvm instance. The first call will trigger the download of prebuilt
    /// files if necessary.
    pub fn new(additional_arguments: &[&str], debug: bool, o_direct: bool) -> Result<TestVm> {
        static PREP_ONCE: Once = Once::new();
        PREP_ONCE.call_once(TestVm::initialize_once);

        // Create two named pipes to communicate with the guest.
        let test_dir = TempDir::new()?;
        let from_guest_pipe = test_dir.path().join("from_guest");
        let to_guest_pipe = test_dir.path().join("to_guest");
        mkfifo(&from_guest_pipe)?;
        mkfifo(&to_guest_pipe)?;

        let control_socket_path = test_dir.path().join("control");

        let mut command = Command::new(find_crosvm_binary());
        command.args(&["run", "--disable-sandbox"]);
        TestVm::configure_serial_devices(&mut command, &from_guest_pipe, &to_guest_pipe);
        command.args(&["--socket", control_socket_path.to_str().unwrap()]);
        command.args(additional_arguments);

        TestVm::configure_kernel(&mut command, o_direct);

        // Set `Stdio::piped` so we can forward the outputs to stdout later.
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        println!("$ {:?}", command);

        let process = Some(command.spawn()?);

        // Open pipes. Panic if we cannot connect after a timeout.
        let (to_guest, from_guest) = panic_on_timeout(
            move || (File::create(to_guest_pipe), File::open(from_guest_pipe)),
            VM_COMMUNICATION_TIMEOUT,
        );

        // Wait for magic line to be received, indicating the delegate is ready.
        let mut from_guest_reader = BufReader::new(from_guest?);
        let mut magic_line = String::new();
        from_guest_reader.read_line(&mut magic_line)?;
        assert_eq!(magic_line.trim(), TestVm::MAGIC_LINE);

        Ok(TestVm {
            test_dir,
            from_guest_reader,
            to_guest: to_guest?,
            control_socket_path,
            process,
            debug,
        })
    }

    /// Executes the shell command `command` and returns the programs stdout.
    pub fn exec_in_guest(&mut self, command: &str) -> Result<String> {
        // Write command to serial port.
        writeln!(&mut self.to_guest, "{}", command)?;

        // We will receive an echo of what we have written on the pipe.
        let mut echo = String::new();
        self.from_guest_reader.read_line(&mut echo)?;
        assert_eq!(echo.trim(), command);

        // Return all remaining lines until we receive the MAGIC_LINE
        let mut output = String::new();
        loop {
            let mut line = String::new();
            self.from_guest_reader.read_line(&mut line)?;
            if line.trim() == TestVm::MAGIC_LINE {
                break;
            }
            output.push_str(&line);
        }
        let trimmed = output.trim();
        if self.debug {
            println!("<- {:?}", trimmed);
        }
        Ok(trimmed.to_string())
    }

    fn crosvm_command(&self, command: &str) -> Result<()> {
        let args = [self.control_socket_path.to_str().unwrap()];
        println!("$ crosvm {} {:?}", command, &args.join(" "));

        let mut cmd = Command::new(find_crosvm_binary());
        cmd.arg(command).args(args);
        cmd.stdout(Stdio::piped());
        cmd.stderr(Stdio::piped());

        let output = cmd.output()?;
        // Print both the crosvm's stdout/stderr to stdout so that they'll be shown when the test
        // is failed.
        println!(
            "`crosvm {}` stdout:\n{}",
            command,
            from_utf8(&output.stdout).unwrap()
        );
        println!(
            "`crosvm {}` stderr:\n{}",
            command,
            from_utf8(&output.stderr).unwrap()
        );

        if !output.status.success() {
            Err(anyhow!("Command failed with exit code {}", output.status))
        } else {
            Ok(())
        }
    }

    pub fn stop(&self) -> Result<()> {
        self.crosvm_command("stop")
    }

    pub fn suspend(&self) -> Result<()> {
        self.crosvm_command("suspend")
    }

    pub fn resume(&self) -> Result<()> {
        self.crosvm_command("resume")
    }
}

impl Drop for TestVm {
    fn drop(&mut self) {
        self.stop().unwrap();
        let output = self.process.take().unwrap().wait_with_output().unwrap();

        // Print both the crosvm's stdout/stderr to stdout so that they'll be shown when the test
        // is failed.
        println!(
            "TestVm stdout:\n{}",
            std::str::from_utf8(&output.stdout).unwrap()
        );
        println!(
            "TestVm stderr:\n{}",
            std::str::from_utf8(&output.stderr).unwrap()
        );
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::fmt;
use std::mem;

use llvm_sys::core;
use llvm_sys::prelude::{
  LLVMContextRef,
  LLVMTypeRef
};
use libc::c_uint;

use super::LLVMRef;
use util::HasContext;

#[derive(Clone, Eq, PartialEq)]
pub struct Ty(pub LLVMTypeRef);
impl_from_ref!(LLVMTypeRef, Ty);
impl_display!(Ty, LLVMPrintTypeToString);
impl_has_context!(Ty, LLVMGetTypeContext);

impl Ty {
  #[inline(always)]
  pub fn as_ptr(&self) -> LLVMTypeRef { self.0 }

  #[inline(always)]
  pub fn void_ty<'a>(ctx: LLVMContextRef) -> Ty
  {
  	Ty(unsafe { core::LLVMVoidTypeInContext(ctx) })
  }
}

/// A function signature type.
pub struct FunctionTy(pub LLVMTypeRef);
impl_display!(FunctionTy, LLVMPrintTypeToString);
impl_has_context!(FunctionTy, LLVMGetTypeContext);

impl FunctionTy {

  /// Returns the number of parameters this signature takes.
  pub fn num_params(&self) -> usize {
    unsafe { core::LLVMCountParamTypes(self.0) as usize }
  }

  /// Returns a vector of this signature's parameters' types.
  pub fn params(&self) -> Vec<Ty> {
  	unsafe {
    	let count = core::LLVMCountParamTypes(self.0);
      let mut types: Vec<LLVMTypeRef> = (0..count).map(|_| mem::uninitialized()).collect();
      core::LLVMGetParamTypes(self.0, types.as_mut_ptr() as *mut LLVMTypeRef);
      types.into_iter().map(|t| Ty(t)).collect::<Vec<Ty>>()
    }
  }

  /// Returns the type that this function returns.
  pub fn ret_type(&self) -> Ty {
    Ty(unsafe { core::LLVMGetReturnType(self.0)})
  }
}

pub trait LLVMTy {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty;
}

macro_rules! impl_llvm_ty (
  ($ty:ty, $func:expr) => (
    impl LLVMTy for $ty {
      fn llvm_ty(ctx: LLVMContextRef) -> Ty
      {
        Ty(unsafe{$func(ctx)})
      }
    }
  );
);

impl_llvm_ty!(i8,  core::LLVMInt8TypeInContext);
impl_llvm_ty!(u8,  core::LLVMInt8TypeInContext);
impl_llvm_ty!(i16, core::LLVMInt16TypeInContext);
impl_llvm_ty!(u16, core::LLVMInt16TypeInContext);
impl_llvm_ty!(i32, core::LLVMInt32TypeInContext);
impl_llvm_ty!(u32, core::LLVMInt32TypeInContext);
impl_llvm_ty!(i64, core::LLVMInt64TypeInContext);
impl_llvm_ty!(u64, core::LLVMInt64TypeInContext);
impl_llvm_ty!(f32, core::LLVMFloatTypeInContext);
impl_llvm_ty!(f64, core::LLVMDoubleTypeInContext);

impl LLVMTy for usize {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty
  {
    Ty(unsafe{core::LLVMIntTypeInContext(ctx, ::std::mem::size_of::<isize>() as c_uint * 8)})
  }
}

impl LLVMTy for isize {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty
  {
    Ty(unsafe{core::LLVMIntTypeInContext(ctx, ::std::mem::size_of::<isize>() as c_uint * 8)})
  }
}

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

	#[test]
	pub fn test_types()
	{
		let jit = JitCompiler::new("test1").ok().unwrap();
    let ctx = jit.context();
		assert_eq!("i8",     format!("{}", i8::llvm_ty(ctx)));
		assert_eq!("i16",    format!("{}", i16::llvm_ty(ctx)));
		assert_eq!("i32",    format!("{}", i32::llvm_ty(ctx)));
		assert_eq!("i64",    format!("{}", i64::llvm_ty(ctx)));
		assert_eq!("float",  format!("{}", f32::llvm_ty(ctx)));
		assert_eq!("double", format!("{}", f64::llvm_ty(ctx)));

		//assert_eq!("[10 x double]",  format!("{}", Type::array_ty(&Type::f64_ty(&ctx), 10)));
	}

  #[test]
  fn test_function_ty() {
    let jit = JitCompiler::new("test2").ok().unwrap();
    let ctx = jit.context();
    let prototype =
      jit.create_func_ty(&f64::llvm_ty(ctx), &[&i8::llvm_ty(ctx), &i16::llvm_ty(ctx)]);

    assert_eq!(prototype.ret_type(), f64::llvm_ty(ctx));

    assert_eq!(prototype.num_params(), 2);
    assert_eq!(prototype.params().len(), 2);
    assert_eq!(prototype.params().get(0).unwrap(), &i8::llvm_ty(ctx));
    assert_eq!(prototype.params().get(1).unwrap(), &i16::llvm_ty(ctx));
  }
}<commit_msg>Add type check APIs as follows:<commit_after>#![allow(dead_code)]
use std::fmt;
use std::mem;

use llvm_sys::{core, LLVMTypeKind};
use llvm_sys::prelude::{
  LLVMContextRef,
  LLVMTypeRef
};
use libc::c_uint;

use super::LLVMRef;
use util::HasContext;

#[derive(Clone, Eq, PartialEq)]
pub struct Ty(pub LLVMTypeRef);
impl_from_ref!(LLVMTypeRef, Ty);
impl_display!(Ty, LLVMPrintTypeToString);
impl_has_context!(Ty, LLVMGetTypeContext);

impl Ty {
  #[inline(always)]
  pub fn as_ptr(&self) -> LLVMTypeRef { self.0 }

  #[inline(always)]
  pub fn void_ty<'a>(ctx: LLVMContextRef) -> Ty
  {
  	Ty(unsafe { core::LLVMVoidTypeInContext(ctx) })
  }

  /// Returns true if the size of the type is known at compile-time.
  ///
  /// This is equivalent to the type implementing `Sized` in Rust
  #[inline(always)]
  pub fn is_sized(&self) -> bool
  {
    unsafe { core::LLVMTypeIsSized(self.into()) != 0 }
  }

  /// Returns true if this type is a function.
  #[inline(always)]
  pub fn is_func_ty(&self) -> bool
  {
    let kind = unsafe { core::LLVMGetTypeKind(self.into()) };
    kind as c_uint == LLVMTypeKind::LLVMFunctionTypeKind as c_uint
  }

  /// Returns true if this type is void.
  #[inline(always)]
  pub fn is_void(&self) -> bool
  {
    let kind = unsafe { core::LLVMGetTypeKind(self.into()) };
    kind as c_uint == LLVMTypeKind::LLVMVoidTypeKind as c_uint
  }

  /// Returns true if this type is a pointer.
  #[inline(always)]
  pub fn is_pointer(&self) -> bool
  {
    let kind = unsafe { core::LLVMGetTypeKind(self.into()) };
    kind as c_uint == LLVMTypeKind::LLVMPointerTypeKind as c_uint
  }

  /// Returns true if this type is an integer.
  #[inline(always)]
  pub fn is_integer(&self) -> bool
  {
    let kind = unsafe { core::LLVMGetTypeKind(self.into()) };
    kind as c_uint == LLVMTypeKind::LLVMIntegerTypeKind as c_uint
  }

  /// Returns true if this type is any floating-point number.
  #[inline(always)]
  pub fn is_float(&self) -> bool
  {
    let kind = unsafe { core::LLVMGetTypeKind(self.into()) } as c_uint;
    kind == LLVMTypeKind::LLVMHalfTypeKind as c_uint ||
    kind == LLVMTypeKind::LLVMFloatTypeKind as c_uint ||
    kind == LLVMTypeKind::LLVMDoubleTypeKind as c_uint
  }
}

/// A function signature type.
pub struct FunctionTy(pub LLVMTypeRef);
impl_display!(FunctionTy, LLVMPrintTypeToString);
impl_has_context!(FunctionTy, LLVMGetTypeContext);

impl FunctionTy {

  /// Returns the number of parameters this signature takes.
  pub fn num_params(&self) -> usize {
    unsafe { core::LLVMCountParamTypes(self.0) as usize }
  }

  /// Returns a vector of this signature's parameters' types.
  pub fn params(&self) -> Vec<Ty> {
  	unsafe {
    	let count = core::LLVMCountParamTypes(self.0);
      let mut types: Vec<LLVMTypeRef> = (0..count).map(|_| mem::uninitialized()).collect();
      core::LLVMGetParamTypes(self.0, types.as_mut_ptr() as *mut LLVMTypeRef);
      types.into_iter().map(|t| Ty(t)).collect::<Vec<Ty>>()
    }
  }

  /// Returns the type that this function returns.
  pub fn ret_type(&self) -> Ty {
    Ty(unsafe { core::LLVMGetReturnType(self.0)})
  }
}

pub trait LLVMTy {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty;
}

macro_rules! impl_llvm_ty (
  ($ty:ty, $func:expr) => (
    impl LLVMTy for $ty {
      fn llvm_ty(ctx: LLVMContextRef) -> Ty
      {
        Ty(unsafe{$func(ctx)})
      }
    }
  );
);

impl_llvm_ty!(i8,  core::LLVMInt8TypeInContext);
impl_llvm_ty!(u8,  core::LLVMInt8TypeInContext);
impl_llvm_ty!(i16, core::LLVMInt16TypeInContext);
impl_llvm_ty!(u16, core::LLVMInt16TypeInContext);
impl_llvm_ty!(i32, core::LLVMInt32TypeInContext);
impl_llvm_ty!(u32, core::LLVMInt32TypeInContext);
impl_llvm_ty!(i64, core::LLVMInt64TypeInContext);
impl_llvm_ty!(u64, core::LLVMInt64TypeInContext);
impl_llvm_ty!(f32, core::LLVMFloatTypeInContext);
impl_llvm_ty!(f64, core::LLVMDoubleTypeInContext);

impl LLVMTy for usize {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty
  {
    Ty(unsafe{core::LLVMIntTypeInContext(ctx, ::std::mem::size_of::<isize>() as c_uint * 8)})
  }
}

impl LLVMTy for isize {
  fn llvm_ty(ctx: LLVMContextRef) -> Ty
  {
    Ty(unsafe{core::LLVMIntTypeInContext(ctx, ::std::mem::size_of::<isize>() as c_uint * 8)})
  }
}

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

	#[test]
	pub fn test_types()
	{
		let jit = JitCompiler::new("test1").ok().unwrap();
    let ctx = jit.context();
		assert_eq!("i8",     format!("{}", i8::llvm_ty(ctx)));
		assert_eq!("i16",    format!("{}", i16::llvm_ty(ctx)));
		assert_eq!("i32",    format!("{}", i32::llvm_ty(ctx)));
		assert_eq!("i64",    format!("{}", i64::llvm_ty(ctx)));
		assert_eq!("float",  format!("{}", f32::llvm_ty(ctx)));
		assert_eq!("double", format!("{}", f64::llvm_ty(ctx)));

		//assert_eq!("[10 x double]",  format!("{}", Type::array_ty(&Type::f64_ty(&ctx), 10)));
	}

  #[test]
  fn test_function_ty() {
    let jit = JitCompiler::new("test2").ok().unwrap();
    let ctx = jit.context();
    let prototype =
      jit.create_func_ty(&f64::llvm_ty(ctx), &[&i8::llvm_ty(ctx), &i16::llvm_ty(ctx)]);

    assert_eq!(prototype.ret_type(), f64::llvm_ty(ctx));

    assert_eq!(prototype.num_params(), 2);
    assert_eq!(prototype.params().len(), 2);
    assert_eq!(prototype.params().get(0).unwrap(), &i8::llvm_ty(ctx));
    assert_eq!(prototype.params().get(1).unwrap(), &i16::llvm_ty(ctx));
  }
}<|endoftext|>


<commit_before>use schema_capnp::{brand, node, type_};
use codegen;
use codegen::{GeneratorContext};
use std::collections::hash_map::HashMap;

#[derive(Copy,Clone,PartialEq)]
pub enum Module {
    Reader(&'static str),
    Builder(&'static str),
    Owned,
    Client,
    Pipeline
}

impl ::std::fmt::Display for Module {
    fn fmt(&self, fmt:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        let display_string = match self {
            &Module::Reader(lt) => format!("Reader<{}>", lt),
            &Module::Builder(lt) => format!("Builder<{}>", lt),
            &Module::Owned => "Owned".to_string(),
            &Module::Client => "Client".to_string(),
            &Module::Pipeline => "Pipeline".to_string(),
        };
        ::std::fmt::Display::fmt(&display_string, fmt)
    }
}

impl Module {
    fn bare_name(&self) -> &'static str {
        match self {
            &Module::Reader(_) => "Reader",
            &Module::Builder(_) => "Builder",
            &Module::Owned => "Owned",
            &Module::Client => "Client",
            &Module::Pipeline => "Pipeline",
        }
    }

    fn _have_lifetime(&self) -> bool {
        match self {
            &Module::Reader(_) | &Module::Builder(_) => true,
            &Module::Owned | &Module::Client | &Module::Pipeline => false,
        }
    }
}

pub struct TypeParameterTexts {
    pub expanded_list: Vec<String>,
    pub params: String,
    pub where_clause: String,
    pub where_clause_with_send: String,
    pub phantom_data: String
}

// this is a collection of helpers acting on a "Node" (most of them are Type definitions)
pub trait RustNodeInfo {
    // in rust, we have only nestable modules, and parameterizable types...
    // so a logically child struct will be a physical sibling to its logical parent. so it must
    // keep track of all parameterization on its own
    // this function recursively parse a node definition to find out the parameters used and inherited by scoping
    // and add them to the explicit parameters
    // the result is the actual parameters for the type to be generated
    fn expand_parameters(&self, gen: &::codegen::GeneratorContext) -> Vec<String>;

    fn parameters_texts(&self, gen: &::codegen::GeneratorContext) -> TypeParameterTexts;
}

// this is a collection of helpers acting on a "Type" (someplace where a Type is used, not defined)
pub trait RustTypeInfo {

    fn is_prim(&self) -> bool;
    fn is_parameterized(&self) -> bool;
    fn is_branded(&self) -> bool;
    fn type_string(&self, gen:&codegen::GeneratorContext, module:Module) -> String;
}

impl <'a> RustNodeInfo for node::Reader<'a> {

    fn expand_parameters(&self, gen:&::codegen::GeneratorContext) -> Vec<String> {
        let mut vec:Vec<String> = self.get_parameters().unwrap().iter().map(|p| p.get_name().unwrap().to_string()).collect();
        match self.which().unwrap() {
            ::schema_capnp::node::Struct(struct_reader) => {
                let fields = struct_reader.get_fields().unwrap();
                for field in fields.iter() {
                    match field.which().unwrap() {
                        ::schema_capnp::field::Slot(slot) => {
                            let typ = slot.get_type().unwrap().which().unwrap();
                            match typ {
                                ::schema_capnp::type_::Struct(st) => {
                                    let brand = st.get_brand().unwrap();
                                    let scopes = brand.get_scopes().unwrap();
                                    for scope in scopes.iter() {
                                        match scope.which().unwrap() {
                                            ::schema_capnp::brand::scope::Inherit(_) => {
                                                let parent_node = gen.node_map[&scope.get_scope_id()];
                                                for p in parent_node.get_parameters().unwrap().iter() {
                                                    let parameter_name = p.get_name().unwrap().to_string();
                                                    if !vec.contains(&parameter_name) {
                                                        vec.push(parameter_name);
                                                    }
                                                }
                                            },
                                            _ => {}
                                        }
                                    }
                                },
                                ::schema_capnp::type_::AnyPointer(any) => {
                                    match any.which().unwrap() {
                                        ::schema_capnp::type_::any_pointer::Parameter(def) => {
                                            let the_struct = &gen.node_map[&def.get_scope_id()];
                                            let parameters = the_struct.get_parameters().unwrap();
                                            let parameter = parameters.get(def.get_parameter_index() as u32);
                                            let parameter_name = parameter.get_name().unwrap().to_string();
                                            if !vec.contains(&parameter_name) {
                                                vec.push(parameter_name);
                                            }
                                        },
                                        _ => {}
                                    }
                                },
                                _ => {} // FIXME
                            }
                        },
                        _ => {} // FIXME
                    }
                }
            },
            ::schema_capnp::node::Interface(iface_reader) => {
                let methods = iface_reader.get_methods().unwrap();
                for method in methods.iter() {
                    for brand in vec!(method.get_param_brand().unwrap(), method.get_result_brand().unwrap()) {
                        for scope in brand.get_scopes().unwrap().iter() {
                            match scope.which().unwrap() {
                                ::schema_capnp::brand::scope::Inherit(_) => {
                                    let parent_node = gen.node_map[&scope.get_scope_id()];
                                    for p in parent_node.get_parameters().unwrap().iter() {
                                        let parameter_name = p.get_name().unwrap().to_string();
                                        if !vec.contains(&parameter_name) {
                                            vec.push(parameter_name);
                                        }
                                    }
                                },
                                _ => {}
                            }
                        }
                    }
                }
            }
            _ => {} // FIXME
        }
        vec
    }

    fn parameters_texts(&self, gen:&::codegen::GeneratorContext) -> TypeParameterTexts {
        if self.get_is_generic() {
            let params = self.expand_parameters(&gen);
            let type_parameters = params.iter().map(|param| {
                format!("{}",param)
            }).collect::<Vec<String>>().connect(",");
            let where_clause = "where ".to_string() + &*(params.iter().map(|param| {
                format!("{}: for<'c> ::capnp::traits::Owned<'c>", param)
            }).collect::<Vec<String>>().connect(", ") + " ");
            let where_clause_with_send = "where ".to_string() + &*(params.iter().map(|param| {
                //format!("{}Reader:Send+FromPointerReader<'a>", param)
                format!("{}:Send", param)
            }).collect::<Vec<String>>().connect(", ") + " ") + ", "
                + &*(params.iter().map(|param| {
                //format!("{}Builder:Send+FromPointerBuilder<'a>", param)
                format!("{}:Send", param)
            }).collect::<Vec<String>>().connect(", ") + " ");
            let phantom_data = "_phantom: PhantomData,".to_string();

            TypeParameterTexts {
                expanded_list: params,
                params: type_parameters,
                where_clause: where_clause,
                where_clause_with_send: where_clause_with_send,
                phantom_data: phantom_data
            }
        } else {
            TypeParameterTexts {
                expanded_list: vec!(),
                params: "".to_string(),
                where_clause: "".to_string(),
                where_clause_with_send: "".to_string(),
                phantom_data: "".to_string(),
            }
        }
    }
}

impl <'a> RustTypeInfo for type_::Reader<'a> {

    fn type_string(&self, gen:&codegen::GeneratorContext, module:Module) -> String {
        use codegen_types::RustTypeInfo;

        let local_lifetime = match module {
            Module::Reader(lt) => lt,
            Module::Builder(lt) => lt,
            _ => "",
        };

        let lifetime_coma = if local_lifetime == "" { "".to_string() } else {
            format!("{},", local_lifetime)
        };

        match self.which().unwrap() {
            type_::Void(()) => "()".to_string(),
            type_::Bool(()) => "bool".to_string(),
            type_::Int8(()) => "i8".to_string(),
            type_::Int16(()) => "i16".to_string(),
            type_::Int32(()) => "i32".to_string(),
            type_::Int64(()) => "i64".to_string(),
            type_::Uint8(()) => "u8".to_string(),
            type_::Uint16(()) => "u16".to_string(),
            type_::Uint32(()) => "u32".to_string(),
            type_::Uint64(()) => "u64".to_string(),
            type_::Float32(()) => "f32".to_string(),
            type_::Float64(()) => "f64".to_string(),
            type_::Text(()) => format!("text::{}", module),
            type_::Data(()) => format!("data::{}", module),
            type_::Struct(st) => {
                do_branding(gen, st.get_type_id(), st.get_brand().unwrap(), module,
                            gen.scope_map[&st.get_type_id()].connect("::"), None)
            }
            type_::Interface(interface) => {
                do_branding(gen, interface.get_type_id(), interface.get_brand().unwrap(), module,
                            gen.scope_map[&interface.get_type_id()].connect("::"), None)
            }
            type_::List(ot1) => {
                match ot1.get_element_type().unwrap().which() {
                    Err(_) => { panic!("unsupported type") },
                    Ok(type_::Struct(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("struct_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Enum(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("enum_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::List(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("list_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Text(())) => {
                        format!("text_list::{}", module)
                    },
                    Ok(type_::Data(())) => {
                        format!("data_list::{}", module)
                    },
                    Ok(type_::Interface(_)) => {panic!("unimplemented") },
                    Ok(type_::AnyPointer(_)) => {panic!("List(AnyPointer) is unsupported")},
                    Ok(_) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("primitive_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                }
            },
            type_::Enum(en) => {
                let scope = &gen.scope_map[&en.get_type_id()];
                scope.connect("::").to_string()
            },
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(def) => {
                        let the_struct = &gen.node_map[&def.get_scope_id()];
                        let parameters = the_struct.get_parameters().unwrap();
                        let parameter = parameters.get(def.get_parameter_index() as u32);
                        let parameter_name = parameter.get_name().unwrap();
                        match module {
                            Module::Owned => parameter_name.to_string(),
                            Module::Reader(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Reader",
                                        parameter_name, lifetime)
                            }
                            Module::Builder(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Builder",
                                        parameter_name, lifetime)
                            }
                            Module::Pipeline => {
                                format!("<{} as ::capnp::traits::Owned<'static>>::Pipeline", parameter_name)
                            }
                            _ => { unimplemented!() }
                        }
                    },
                    _ => {
                        match module {
                            Module::Reader(lifetime) => {
                                format!("::capnp::any_pointer::Reader<{}>", lifetime)
                            }
                            Module::Builder(lifetime) => {
                                format!("::capnp::any_pointer::Builder<{}>", lifetime)
                            }
                            _ => {
                                format!("::capnp::any_pointer::{}", module)
                            }
                        }
                    }
                }
            }
        }
    }

    fn is_parameterized(&self) -> bool {
        match self.which().unwrap() {
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(_) => true,
                    _ => false
                }
            }
            _ => false
        }
    }

    fn is_branded(&self) -> bool {
        match self.which().unwrap() {
            type_::Struct(st) => {
                let brand = st.get_brand().unwrap();
                let scopes = brand.get_scopes().unwrap();
                scopes.len() > 0
            }
            _ => false
        }
    }

    #[inline(always)]
    fn is_prim(&self) -> bool {
        match self.which().unwrap() {
            type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) |
            type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) |
            type_::Float32(()) | type_::Float64(()) | type_::Void(()) | type_::Bool(()) => true,
            _ => false
        }
    }
}

///
///
pub fn do_branding(gen: &GeneratorContext,
                   node_id: u64,
                   brand: brand::Reader,
                   leaf: Module,
                   the_mod: String,
                   mut parent_scope_id: Option<u64>) -> String {
    let scopes = brand.get_scopes().unwrap();
    let mut brand_scopes = HashMap::new();
    for scope in scopes.iter() {
        brand_scopes.insert(scope.get_scope_id(), scope);
    }
    let brand_scopes = brand_scopes; // freeze
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let params = current_node.get_parameters().unwrap();
        let mut arguments: Vec<String> = Vec::new();
        match brand_scopes.get(&current_node_id) {
            None => {
                for _ in params.iter() {
                    arguments.push("::capnp::any_pointer::Owned".to_string());
                }
            },
            Some(scope) => {
                match scope.which().unwrap() {
                    brand::scope::Inherit(()) => {
                        for param in params.iter() {
                            arguments.push(param.get_name().unwrap().to_string());
                        }
                    }
                    brand::scope::Bind(bindings_list_opt) => {
                        let bindings_list = bindings_list_opt.unwrap();
                        assert_eq!(bindings_list.len(), params.len());
                        for binding in bindings_list.iter() {
                            match binding.which().unwrap() {
                                brand::binding::Unbound(()) => {
                                    arguments.push("::capnp::any_pointer::Owned".to_string());
                                }
                                brand::binding::Type(t) => {
                                    arguments.push(t.unwrap().type_string(gen, Module::Owned));
                                }
                            }
                        }
                    }
                }
            }
        }
        accumulator.push(arguments);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    // Now add a lifetime parameter if the leaf has one.
    match leaf {
        Module::Reader(lt) => accumulator.push(vec!(lt.to_string())),
        Module::Builder(lt) => accumulator.push(vec!(lt.to_string())),
        _ => (),
    }

    accumulator.reverse();

    let arguments = if accumulator.len() > 0 {
        format!("<{}>", accumulator.concat().connect(","))
    } else {
        "".to_string()
    };

    return format!("{}::{}{}", the_mod,
                   leaf.bare_name().to_string(), arguments);
}

<commit_msg>Get rid of RustNodeInfo::expand_parameters().<commit_after>use schema_capnp::{brand, node, type_};
use codegen;
use codegen::{GeneratorContext};
use std::collections::hash_map::HashMap;

#[derive(Copy,Clone,PartialEq)]
pub enum Module {
    Reader(&'static str),
    Builder(&'static str),
    Owned,
    Client,
    Pipeline
}

impl ::std::fmt::Display for Module {
    fn fmt(&self, fmt:&mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        let display_string = match self {
            &Module::Reader(lt) => format!("Reader<{}>", lt),
            &Module::Builder(lt) => format!("Builder<{}>", lt),
            &Module::Owned => "Owned".to_string(),
            &Module::Client => "Client".to_string(),
            &Module::Pipeline => "Pipeline".to_string(),
        };
        ::std::fmt::Display::fmt(&display_string, fmt)
    }
}

impl Module {
    fn bare_name(&self) -> &'static str {
        match self {
            &Module::Reader(_) => "Reader",
            &Module::Builder(_) => "Builder",
            &Module::Owned => "Owned",
            &Module::Client => "Client",
            &Module::Pipeline => "Pipeline",
        }
    }

    fn _have_lifetime(&self) -> bool {
        match self {
            &Module::Reader(_) | &Module::Builder(_) => true,
            &Module::Owned | &Module::Client | &Module::Pipeline => false,
        }
    }
}

pub struct TypeParameterTexts {
    pub expanded_list: Vec<String>,
    pub params: String,
    pub where_clause: String,
    pub where_clause_with_send: String,
    pub phantom_data: String
}

// this is a collection of helpers acting on a "Node" (most of them are Type definitions)
pub trait RustNodeInfo {
    fn parameters_texts(&self, gen: &::codegen::GeneratorContext) -> TypeParameterTexts;
}

// this is a collection of helpers acting on a "Type" (someplace where a Type is used, not defined)
pub trait RustTypeInfo {

    fn is_prim(&self) -> bool;
    fn is_parameterized(&self) -> bool;
    fn is_branded(&self) -> bool;
    fn type_string(&self, gen:&codegen::GeneratorContext, module:Module) -> String;
}

impl <'a> RustNodeInfo for node::Reader<'a> {
    fn parameters_texts(&self, gen:&::codegen::GeneratorContext) -> TypeParameterTexts {
        if self.get_is_generic() {
            let params = get_type_parameters(&gen, self.get_id(), None);
            let type_parameters = params.iter().map(|param| {
                format!("{}",param)
            }).collect::<Vec<String>>().connect(",");
            let where_clause = "where ".to_string() + &*(params.iter().map(|param| {
                format!("{}: for<'c> ::capnp::traits::Owned<'c>", param)
            }).collect::<Vec<String>>().connect(", ") + " ");
            let where_clause_with_send = "where ".to_string() + &*(params.iter().map(|param| {
                //format!("{}Reader:Send+FromPointerReader<'a>", param)
                format!("{}:Send", param)
            }).collect::<Vec<String>>().connect(", ") + " ") + ", "
                + &*(params.iter().map(|param| {
                //format!("{}Builder:Send+FromPointerBuilder<'a>", param)
                format!("{}:Send", param)
            }).collect::<Vec<String>>().connect(", ") + " ");
            let phantom_data = "_phantom: PhantomData,".to_string();

            TypeParameterTexts {
                expanded_list: params,
                params: type_parameters,
                where_clause: where_clause,
                where_clause_with_send: where_clause_with_send,
                phantom_data: phantom_data
            }
        } else {
            TypeParameterTexts {
                expanded_list: vec!(),
                params: "".to_string(),
                where_clause: "".to_string(),
                where_clause_with_send: "".to_string(),
                phantom_data: "".to_string(),
            }
        }
    }
}

impl <'a> RustTypeInfo for type_::Reader<'a> {

    fn type_string(&self, gen:&codegen::GeneratorContext, module:Module) -> String {
        use codegen_types::RustTypeInfo;

        let local_lifetime = match module {
            Module::Reader(lt) => lt,
            Module::Builder(lt) => lt,
            _ => "",
        };

        let lifetime_coma = if local_lifetime == "" { "".to_string() } else {
            format!("{},", local_lifetime)
        };

        match self.which().unwrap() {
            type_::Void(()) => "()".to_string(),
            type_::Bool(()) => "bool".to_string(),
            type_::Int8(()) => "i8".to_string(),
            type_::Int16(()) => "i16".to_string(),
            type_::Int32(()) => "i32".to_string(),
            type_::Int64(()) => "i64".to_string(),
            type_::Uint8(()) => "u8".to_string(),
            type_::Uint16(()) => "u16".to_string(),
            type_::Uint32(()) => "u32".to_string(),
            type_::Uint64(()) => "u64".to_string(),
            type_::Float32(()) => "f32".to_string(),
            type_::Float64(()) => "f64".to_string(),
            type_::Text(()) => format!("text::{}", module),
            type_::Data(()) => format!("data::{}", module),
            type_::Struct(st) => {
                do_branding(gen, st.get_type_id(), st.get_brand().unwrap(), module,
                            gen.scope_map[&st.get_type_id()].connect("::"), None)
            }
            type_::Interface(interface) => {
                do_branding(gen, interface.get_type_id(), interface.get_brand().unwrap(), module,
                            gen.scope_map[&interface.get_type_id()].connect("::"), None)
            }
            type_::List(ot1) => {
                match ot1.get_element_type().unwrap().which() {
                    Err(_) => { panic!("unsupported type") },
                    Ok(type_::Struct(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("struct_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Enum(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("enum_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::List(_)) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("list_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                    Ok(type_::Text(())) => {
                        format!("text_list::{}", module)
                    },
                    Ok(type_::Data(())) => {
                        format!("data_list::{}", module)
                    },
                    Ok(type_::Interface(_)) => {panic!("unimplemented") },
                    Ok(type_::AnyPointer(_)) => {panic!("List(AnyPointer) is unsupported")},
                    Ok(_) => {
                        let inner = ot1.get_element_type().unwrap().type_string(gen, Module::Owned);
                        format!("primitive_list::{}<{}{}>", module.bare_name(), lifetime_coma, inner)
                    },
                }
            },
            type_::Enum(en) => {
                let scope = &gen.scope_map[&en.get_type_id()];
                scope.connect("::").to_string()
            },
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(def) => {
                        let the_struct = &gen.node_map[&def.get_scope_id()];
                        let parameters = the_struct.get_parameters().unwrap();
                        let parameter = parameters.get(def.get_parameter_index() as u32);
                        let parameter_name = parameter.get_name().unwrap();
                        match module {
                            Module::Owned => parameter_name.to_string(),
                            Module::Reader(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Reader",
                                        parameter_name, lifetime)
                            }
                            Module::Builder(lifetime) => {
                                format!("<{} as ::capnp::traits::Owned<{}>>::Builder",
                                        parameter_name, lifetime)
                            }
                            Module::Pipeline => {
                                format!("<{} as ::capnp::traits::Owned<'static>>::Pipeline", parameter_name)
                            }
                            _ => { unimplemented!() }
                        }
                    },
                    _ => {
                        match module {
                            Module::Reader(lifetime) => {
                                format!("::capnp::any_pointer::Reader<{}>", lifetime)
                            }
                            Module::Builder(lifetime) => {
                                format!("::capnp::any_pointer::Builder<{}>", lifetime)
                            }
                            _ => {
                                format!("::capnp::any_pointer::{}", module)
                            }
                        }
                    }
                }
            }
        }
    }

    fn is_parameterized(&self) -> bool {
        match self.which().unwrap() {
            type_::AnyPointer(pointer) => {
                match pointer.which().unwrap() {
                    type_::any_pointer::Parameter(_) => true,
                    _ => false
                }
            }
            _ => false
        }
    }

    fn is_branded(&self) -> bool {
        match self.which().unwrap() {
            type_::Struct(st) => {
                let brand = st.get_brand().unwrap();
                let scopes = brand.get_scopes().unwrap();
                scopes.len() > 0
            }
            _ => false
        }
    }

    #[inline(always)]
    fn is_prim(&self) -> bool {
        match self.which().unwrap() {
            type_::Int8(()) | type_::Int16(()) | type_::Int32(()) | type_::Int64(()) |
            type_::Uint8(()) | type_::Uint16(()) | type_::Uint32(()) | type_::Uint64(()) |
            type_::Float32(()) | type_::Float64(()) | type_::Void(()) | type_::Bool(()) => true,
            _ => false
        }
    }
}

///
///
pub fn do_branding(gen: &GeneratorContext,
                   node_id: u64,
                   brand: brand::Reader,
                   leaf: Module,
                   the_mod: String,
                   mut parent_scope_id: Option<u64>) -> String {
    let scopes = brand.get_scopes().unwrap();
    let mut brand_scopes = HashMap::new();
    for scope in scopes.iter() {
        brand_scopes.insert(scope.get_scope_id(), scope);
    }
    let brand_scopes = brand_scopes; // freeze
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let params = current_node.get_parameters().unwrap();
        let mut arguments: Vec<String> = Vec::new();
        match brand_scopes.get(&current_node_id) {
            None => {
                for _ in params.iter() {
                    arguments.push("::capnp::any_pointer::Owned".to_string());
                }
            },
            Some(scope) => {
                match scope.which().unwrap() {
                    brand::scope::Inherit(()) => {
                        for param in params.iter() {
                            arguments.push(param.get_name().unwrap().to_string());
                        }
                    }
                    brand::scope::Bind(bindings_list_opt) => {
                        let bindings_list = bindings_list_opt.unwrap();
                        assert_eq!(bindings_list.len(), params.len());
                        for binding in bindings_list.iter() {
                            match binding.which().unwrap() {
                                brand::binding::Unbound(()) => {
                                    arguments.push("::capnp::any_pointer::Owned".to_string());
                                }
                                brand::binding::Type(t) => {
                                    arguments.push(t.unwrap().type_string(gen, Module::Owned));
                                }
                            }
                        }
                    }
                }
            }
        }
        accumulator.push(arguments);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    // Now add a lifetime parameter if the leaf has one.
    match leaf {
        Module::Reader(lt) => accumulator.push(vec!(lt.to_string())),
        Module::Builder(lt) => accumulator.push(vec!(lt.to_string())),
        _ => (),
    }

    accumulator.reverse();

    let arguments = if accumulator.len() > 0 {
        format!("<{}>", accumulator.concat().connect(","))
    } else {
        "".to_string()
    };

    return format!("{}::{}{}", the_mod,
                   leaf.bare_name().to_string(), arguments);
}

pub fn get_type_parameters(gen: &GeneratorContext,
                           node_id: u64,
                           mut parent_scope_id: Option<u64>) -> Vec<String> {
    let mut current_node_id = node_id;
    let mut accumulator: Vec<Vec<String>> = Vec::new();
    loop {
        let current_node = match gen.node_map.get(&current_node_id) {
            None => break,
            Some(node) => node,
        };
        let mut params = Vec::new();
        for param in current_node.get_parameters().unwrap().iter() {
            params.push(param.get_name().unwrap().to_string());
        }

        accumulator.push(params);
        current_node_id = current_node.get_scope_id();
        match (current_node_id, parent_scope_id) {
            (0, Some(id)) => current_node_id = id,
            _ => (),
        }
        parent_scope_id = None; // Only consider on the first time around.
    }

    accumulator.reverse();
    return accumulator.concat();
}
<|endoftext|>