<commit_before>extern crate rand;
extern crate sdl2;

mod audio;
mod cpu;
mod display;
mod memory;
mod keyboard;

use std::fs::File;
use std::{thread, time};

pub fn run(file: &mut File) {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();
    let audio_subsystem = sdl_context.audio().unwrap();
    let audio_device = audio::create_audio_device(&audio_subsystem);

    let mut display_context = display::DisplayContext::new(&video_subsystem);
    let mut event_pump = sdl_context.event_pump().unwrap();

    let display = display::Display::new(&mut display_context);
    let keyboard = keyboard::Keyboard::new(&mut event_pump);

    let mut memory = memory::BlockMemory::new();
    memory.load_rom(file);

    let mut cpu = cpu::Cpu::new(memory, display, keyboard, audio_device);
    loop {
        for _ in 0..10 {
            cpu.cycle();
        }
        thread::sleep(time::Duration::from_millis(17));
        cpu.decrement_timers();
        cpu.redraw_display();
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Minor refactoring of run()<commit_after>extern crate rand;
extern crate sdl2;

mod audio;
mod cpu;
mod display;
mod memory;
mod keyboard;

use std::fs::File;
use std::{thread, time};

pub fn run(file: &mut File) {
    let sdl_context = sdl2::init().unwrap();

    let audio_subsystem = sdl_context.audio().unwrap();
    let audio_device = audio::create_audio_device(&audio_subsystem);

    let video_subsystem = sdl_context.video().unwrap();
    let mut display_context = display::DisplayContext::new(&video_subsystem);
    let display = display::Display::new(&mut display_context);

    let mut event_pump = sdl_context.event_pump().unwrap();
    let keyboard = keyboard::Keyboard::new(&mut event_pump);

    let mut memory = memory::BlockMemory::new();
    memory.load_rom(file);

    let mut cpu = cpu::Cpu::new(memory, display, keyboard, audio_device);
    loop {
        for _ in 0..10 {
            cpu.cycle();
        }
        thread::sleep(time::Duration::from_millis(17));
        cpu.decrement_timers();
        cpu.redraw_display();
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/libffi/0.8.0")]
//! Rust bindings for [libffi](https://sourceware.org/libffi/).
//!
//! The C libffi library provides two main facilities: assembling calls
//! to functions dynamically, and creating closures that can be called
//! as ordinary C functions. In Rust, the latter means that we can turn
//! a Rust lambda (or any object implementing `Fn`/`FnMut`) into an
//! ordinary C function pointer that we can pass as a callback to C.
//!
//! The easiest way to use this library is via the
//! [`high`](high/index.html) layer module, but more flexibility (and
//! less checking) is provided by the [`middle`](middle/index.html) and
//! [`low`](low/index.html) layers.
//!
//! # Usage
//!
//! Building libffi will build lifbffi-sys, which will in turn build the
//! libffi C library [from github](https://github.com/libffi/libffi), which
//! requires that you have a working make, C compiler, automake, and
//! autoconf first. It’s [on crates.io](https://crates.io/crates/libffi), so
//! you can add
//!
//! ```toml
//! [dependencies]
//! libffi = "0.8.0"
//! ```
//!
//! This crate depends on [the `libffi-sys` crate], which by default
//! attempts to build its own version of the C libffi library. In order to
//! use your system’s C libffi instead, enable this crate’s  `system`
//! feature in your `Cargo.toml`:
//!
//! ```toml
//! [features]
//! libffi = { version = "0.8.0", features = ["system"] }
//! ```
//!
//! See [the `libffi-sys` documentation] for more information about how it
//! finds C libffi.
//!
//! This crate supports Rust version 1.32 and later.
//!
//! # Organization
//!
//! This library is organized in four layers, each of which attempts to
//! provide more safety and a simpler interface than the next layer
//! down. From top to bottom:
//!
//!   - The [`high`](high/index.html) layer provides safe(?) and
//!     automatic marshalling of Rust closures into C function pointers.
//!   - The [`middle`](middle/index.html) layer provides memory-managed
//!     abstractions for assembling calls and closures, but is unsafe
//!     because it doesn’t check argument types.
//!   - The [`low`](low/index.html) layer makes no attempts at safety,
//!     but provides a more idiomatically “Rusty” API than the underlying
//!     C library.
//!   - The [`raw`](raw/index.html) layer is a re-export of the
//!     [`libffi-sys`](https://crates.io/crates/libffi-sys) crate,
//!     a direct mapping of the C libffi library into Rust, generated by
//!     [bindgen](https://crates.io/crates/bindgen).
//!
//! It should be possible to use any layer without dipping into lower
//! layers (and it will be considered a bug to the extent that it
//! isn’t).
//!
//! # Examples
//!
//! In this example, we convert a Rust lambda containing a free variable
//! into an ordinary C code pointer. The type of `fun` below is
//! `extern "C" fn(u64, u64) -> u64`.
//!
//! ```
//! use libffi::high::Closure2;
//!
//! let x = 5u64;
//! let f = |y: u64, z: u64| x + y + z;
//!
//! let closure = Closure2::new(&f);
//! let fun     = closure.code_ptr();
//!
//! assert_eq!(18, fun(6, 7));
//! ```
//!
//! [the `libffi-sys` crate]: https://crates.io/crates/libffi-sys/
//!
//! [the `libffi-sys` documentation]: https://docs.rs/libffi-sys/#usage
//!

#![deny(missing_docs)]

#[macro_use] extern crate abort_on_panic;
extern crate libc;
extern crate libffi_sys;

/// Raw definitions imported from the C library (via bindgen).
///
/// This module is generated by bindgen and undocumented. It’s intended
/// as the basis for higher-level bindings, but you can see the [C libffi
/// documentation](http://www.atmark-techno.com/~yashi/libffi.html).
pub mod raw {
    pub use libffi_sys::*;
}

pub mod high;
pub mod middle;
pub mod low;
<commit_msg>Deny all warnings.<commit_after>#![doc(html_root_url = "https://docs.rs/libffi/0.8.0")]
//! Rust bindings for [libffi](https://sourceware.org/libffi/).
//!
//! The C libffi library provides two main facilities: assembling calls
//! to functions dynamically, and creating closures that can be called
//! as ordinary C functions. In Rust, the latter means that we can turn
//! a Rust lambda (or any object implementing `Fn`/`FnMut`) into an
//! ordinary C function pointer that we can pass as a callback to C.
//!
//! The easiest way to use this library is via the
//! [`high`](high/index.html) layer module, but more flexibility (and
//! less checking) is provided by the [`middle`](middle/index.html) and
//! [`low`](low/index.html) layers.
//!
//! # Usage
//!
//! Building libffi will build lifbffi-sys, which will in turn build the
//! libffi C library [from github](https://github.com/libffi/libffi), which
//! requires that you have a working make, C compiler, automake, and
//! autoconf first. It’s [on crates.io](https://crates.io/crates/libffi), so
//! you can add
//!
//! ```toml
//! [dependencies]
//! libffi = "0.8.0"
//! ```
//!
//! This crate depends on [the `libffi-sys` crate], which by default
//! attempts to build its own version of the C libffi library. In order to
//! use your system’s C libffi instead, enable this crate’s  `system`
//! feature in your `Cargo.toml`:
//!
//! ```toml
//! [features]
//! libffi = { version = "0.8.0", features = ["system"] }
//! ```
//!
//! See [the `libffi-sys` documentation] for more information about how it
//! finds C libffi.
//!
//! This crate supports Rust version 1.32 and later.
//!
//! # Organization
//!
//! This library is organized in four layers, each of which attempts to
//! provide more safety and a simpler interface than the next layer
//! down. From top to bottom:
//!
//!   - The [`high`](high/index.html) layer provides safe(?) and
//!     automatic marshalling of Rust closures into C function pointers.
//!   - The [`middle`](middle/index.html) layer provides memory-managed
//!     abstractions for assembling calls and closures, but is unsafe
//!     because it doesn’t check argument types.
//!   - The [`low`](low/index.html) layer makes no attempts at safety,
//!     but provides a more idiomatically “Rusty” API than the underlying
//!     C library.
//!   - The [`raw`](raw/index.html) layer is a re-export of the
//!     [`libffi-sys`](https://crates.io/crates/libffi-sys) crate,
//!     a direct mapping of the C libffi library into Rust, generated by
//!     [bindgen](https://crates.io/crates/bindgen).
//!
//! It should be possible to use any layer without dipping into lower
//! layers (and it will be considered a bug to the extent that it
//! isn’t).
//!
//! # Examples
//!
//! In this example, we convert a Rust lambda containing a free variable
//! into an ordinary C code pointer. The type of `fun` below is
//! `extern "C" fn(u64, u64) -> u64`.
//!
//! ```
//! use libffi::high::Closure2;
//!
//! let x = 5u64;
//! let f = |y: u64, z: u64| x + y + z;
//!
//! let closure = Closure2::new(&f);
//! let fun     = closure.code_ptr();
//!
//! assert_eq!(18, fun(6, 7));
//! ```
//!
//! [the `libffi-sys` crate]: https://crates.io/crates/libffi-sys/
//!
//! [the `libffi-sys` documentation]: https://docs.rs/libffi-sys/#usage
//!

#![deny(missing_docs)]
#![deny(warnings)]

#[macro_use] extern crate abort_on_panic;
extern crate libc;
extern crate libffi_sys;

/// Raw definitions imported from the C library (via bindgen).
///
/// This module is generated by bindgen and undocumented. It’s intended
/// as the basis for higher-level bindings, but you can see the [C libffi
/// documentation](http://www.atmark-techno.com/~yashi/libffi.html).
pub mod raw {
    pub use libffi_sys::*;
}

pub mod high;
pub mod middle;
pub mod low;
<|endoftext|>


<commit_before>#![allow(dead_code)]
#[allow(non_camel_case_types)]
type float = f32;
type Point = (float, float);
type Line = (Point, Point);

fn ramer_douglas_peucker(v: Vec<Point>, epsilon: float) -> Vec<Point> {
    let length = v.len();
    if length < 3 {
        return v;
    }
    let mut stack = vec![(0, length - 1)];
    let mut result = Vec::new();
    let mut last_stack_index = -1;

    while let Some((start_index, end_index)) =  stack.pop() {
        // println!("start = {}, end = {}", start_index, end_index);
        let mut max_distance = 0.0 as float;
        let mut max_index = start_index;
        for i in start_index+1..end_index {
            let distance =
            distance_point_to_line(v[i], (v[start_index], v[end_index]));
            // println!("i = {}, distance = {}", i, distance);
            if distance > max_distance {
                max_distance = distance;
                max_index = i;
            }
        }
        if max_distance > epsilon {
            stack.push((max_index, end_index));
            stack.push((start_index, max_index));
        } else {
            if last_stack_index != start_index {
                result.push(v[start_index]);
            }
            result.push(v[end_index]);
            last_stack_index = end_index;
        }
    }
    result
}

fn distance_point_to_line(p: Point, l: Line) -> float {
    if l.0 == l.1 {
        return distance_point_to_point(p, l.0);
    }
    let a = (l.0).1 - (l.1).1;
    let b = (l.1).0 - (l.0).0;
    let c = (l.0).0 * (l.1).1 - (l.1).0 * (l.0).1;
    let result = (a * p.0 + b * p.1 + c) / (a * a + b * b).sqrt();
    result.abs()
}

fn distance_point_to_point(x: Point, y: Point) -> float {
    let a = y.0 - x.0;
    let b = y.1 - x.1;
    let result = (a * a + b * b).sqrt();
    result.abs()
}

#[test]
fn reduce_vector() {
    // Minimal cases:
    assert_eq!(vec![(1.0, 1.0)], ramer_douglas_peucker(vec![(1.0, 1.0)], 0.5));
    assert_eq!(vec![(1.0, 1.0), (2.0, 2.0)], ramer_douglas_peucker(vec![(1.0, 1.0), (2.0, 2.0)], 0.5));
    assert_eq!(vec![(1.0, 1.0), (3.0, 3.0)], ramer_douglas_peucker(vec![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)], 0.5));
    // Effect of varying epsilon:
    assert_eq!(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], ramer_douglas_peucker(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], 0.1));
    assert_eq!(vec![(0.0, 2.0), (3.0, 0.0), (5.0, 1.0)], ramer_douglas_peucker(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], 0.5));
    // Tests with vertical segments:
    assert_eq!(vec![(10.0, 35.0), (20.0, 29.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], 10.0));
    assert_eq!(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], 1.0));
    // Tests with horizontal segments:
    assert_eq!(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], 1.0));
    assert_eq!(vec![(10.0, 35.0), (21.0, 30.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], 10.0));
    // Tests with vertical and horizontal segments:
    assert_eq!(vec![(10.0, 30.0), (50.0, 10.0)], ramer_douglas_peucker(vec![(10.0, 30.0), (30.0, 30.0), (30.0, 10.0), (50.0, 10.0)], 10.0));
    assert_eq!(vec![(10.0, 30.0), (50.0, 10.0)], ramer_douglas_peucker(vec![(10.0, 30.0), (30.0, 30.0), (30.0, 10.0), (50.0, 10.0)], 15.0));
    // A more complex curve:
    assert_eq!(vec![(3.5, 21.25), (23.2, 3.1), (54.6, 18.15), (71.5, 9.7), (101.3, 21.1)], ramer_douglas_peucker(vec![(3.5, 21.25), (7.3, 12.0), (23.2, 3.1), (37.2, 12.07), (54.6, 18.15), (62.2, 16.45), (71.5, 9.7), (101.3, 21.1)], 5.0));
    assert_eq!(vec![(0.0, 0.0), (0.5, 0.5), (1.25, -0.25), (1.5, 0.5)], ramer_douglas_peucker(vec![(0.0,0.0),(0.5,0.5),(1.0,0.0),(1.25,-0.25),(1.5,0.5)], 0.25));
    // Start point == end point
    assert_eq!(vec![(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)],ramer_douglas_peucker(vec![(0.0,0.0),(1.0,0.0),(2.0,0.0),(2.0,1.0),(2.0,2.0),(1.0,2.0),(0.0,2.0),(0.0,1.0),(0.0, 0.0)], 1.0));
}

#[test]
fn calculate_point_to_line_distance() {
    assert!((distance_point_to_line((3.0, 2.0), ((-2.0, 0.0),(0.0, 2.0))) - 2.12132).abs() < 0.00001);
    assert!(distance_point_to_line((0.0, 0.0), ((-1.0, 0.0),(1.0, 0.0))).abs() < 0.00001);
}

#[test]
fn calculate_point_to_point_distance() {
    assert!((distance_point_to_point((3.0, 2.0), (5.0, -1.0)) - 3.60555).abs() < 0.00001);
    assert!(distance_point_to_point((3.0, 2.0), (3.0, 2.0)).abs() < 0.00001);
}
<commit_msg>generic types implemented<commit_after>#[allow(non_camel_case_types)]
pub type float = f32;

pub type Point = (float, float);
pub type Line = (Point, Point);

pub trait HasPoint: Copy {
    fn to_point(self) -> Point;
}

pub fn ramer_douglas_peucker<T: HasPoint>(v: Vec<T>, epsilon: float) -> Vec<T> {
    let length = v.len();
    if length < 3 {
        return v;
    }
    let mut stack = vec![(0, length - 1)];
    let mut result = Vec::new();
    let mut last_stack_index = -1;

    while let Some((start_index, end_index)) =  stack.pop() {
        // println!("start = {}, end = {}", start_index, end_index);
        let mut max_distance = 0.0 as float;
        let mut max_index = start_index;
        for i in start_index+1..end_index {
            let point = v[i].to_point();
            let line = (v[start_index].to_point(), v[end_index].to_point());
            let distance =
            distance_point_to_line(point, line);
            // println!("i = {}, distance = {}", i, distance);
            if distance > max_distance {
                max_distance = distance;
                max_index = i;
            }
        }
        if max_distance > epsilon {
            stack.push((max_index, end_index));
            stack.push((start_index, max_index));
        } else {
            if last_stack_index != start_index {
                result.push(v[start_index]);
            }
            result.push(v[end_index]);
            last_stack_index = end_index;
        }
    }
    result
}

pub fn distance_point_to_line(p: Point, l: Line) -> float {
    if l.0 == l.1 {
        return distance_point_to_point(p, l.0);
    }
    let a = (l.0).1 - (l.1).1;
    let b = (l.1).0 - (l.0).0;
    let c = (l.0).0 * (l.1).1 - (l.1).0 * (l.0).1;
    let result = (a * p.0 + b * p.1 + c) / (a * a + b * b).sqrt();
    result.abs()
}

pub fn distance_point_to_point(x: Point, y: Point) -> float {
    let a = y.0 - x.0;
    let b = y.1 - x.1;
    let result = (a * a + b * b).sqrt();
    result.abs()
}

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

    impl HasPoint for Point {
        fn to_point(self) -> Point {self}
    }

    impl HasPoint for (f32, f32, i32) {
        fn to_point(self) -> Point {(self.0, self.1)}
    }

    #[test]
    fn reduce_vector() {
        // Minimal cases:
        assert_eq!(vec![(1.0, 1.0)], ramer_douglas_peucker(vec![(1.0, 1.0)], 0.5));
        assert_eq!(vec![(1.0, 1.0), (2.0, 2.0)], ramer_douglas_peucker(vec![(1.0, 1.0), (2.0, 2.0)], 0.5));
        assert_eq!(vec![(1.0, 1.0), (3.0, 3.0)], ramer_douglas_peucker(vec![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)], 0.5));
        // Effect of varying epsilon:
        assert_eq!(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], ramer_douglas_peucker(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], 0.1));
        assert_eq!(vec![(0.0, 2.0), (3.0, 0.0), (5.0, 1.0)], ramer_douglas_peucker(vec![(0.0, 2.0), (1.0, 1.0), (3.0, 0.0), (5.0, 1.0)], 0.5));
        // Tests with vertical segments:
        assert_eq!(vec![(10.0, 35.0), (20.0, 29.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], 10.0));
        assert_eq!(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 34.0), (15.0, 30.0), (20.0, 29.0)], 1.0));
        // Tests with horizontal segments:
        assert_eq!(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], 1.0));
        assert_eq!(vec![(10.0, 35.0), (21.0, 30.0)], ramer_douglas_peucker(vec![(10.0, 35.0), (15.0, 35.0), (16.0, 30.0), (21.0, 30.0)], 10.0));
        // Tests with vertical and horizontal segments:
        assert_eq!(vec![(10.0, 30.0), (50.0, 10.0)], ramer_douglas_peucker(vec![(10.0, 30.0), (30.0, 30.0), (30.0, 10.0), (50.0, 10.0)], 10.0));
        assert_eq!(vec![(10.0, 30.0), (50.0, 10.0)], ramer_douglas_peucker(vec![(10.0, 30.0), (30.0, 30.0), (30.0, 10.0), (50.0, 10.0)], 15.0));
        // A more complex curve:
        assert_eq!(vec![(3.5, 21.25), (23.2, 3.1), (54.6, 18.15), (71.5, 9.7), (101.3, 21.1)], ramer_douglas_peucker(vec![(3.5, 21.25), (7.3, 12.0), (23.2, 3.1), (37.2, 12.07), (54.6, 18.15), (62.2, 16.45), (71.5, 9.7), (101.3, 21.1)], 5.0));
        assert_eq!(vec![(0.0, 0.0), (0.5, 0.5), (1.25, -0.25), (1.5, 0.5)], ramer_douglas_peucker(vec![(0.0,0.0),(0.5,0.5),(1.0,0.0),(1.25,-0.25),(1.5,0.5)], 0.25));
        assert_eq!(vec![(1.0, 1.0, 1), (3.0, 3.0, 3)], ramer_douglas_peucker(vec![(1.0, 1.0, 1), (2.0, 2.0, 2), (3.0, 3.0, 3)], 0.5));
        // Start point == end point
        assert_eq!(vec![(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)],ramer_douglas_peucker(vec![(0.0,0.0),(1.0,0.0),(2.0,0.0),(2.0,1.0),(2.0,2.0),(1.0,2.0),(0.0,2.0),(0.0,1.0),(0.0, 0.0)], 1.0));
    }

    #[test]
    fn calculate_point_to_line_distance() {
        assert!((distance_point_to_line((3.0, 2.0), ((-2.0, 0.0),(0.0, 2.0))) - 2.12132).abs() < 0.00001);
        assert!(distance_point_to_line((0.0, 0.0), ((-1.0, 0.0),(1.0, 0.0))).abs() < 0.00001);
    }

    #[test]
    fn calculate_point_to_point_distance() {
        assert!((distance_point_to_point((3.0, 2.0), (5.0, -1.0)) - 3.60555).abs() < 0.00001);
        assert!(distance_point_to_point((3.0, 2.0), (3.0, 2.0)).abs() < 0.00001);
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
  fn klee_int(name: *const raw::c_char) -> raw::c_int;
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
}

pub fn i32(name: &str) -> i32 {
  let result;
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    result = klee_int(name_cstr.as_ptr());
  }
  result
}

pub fn sym<T>(data: &T, name: &str) {
  unsafe{ any(transmute(data as *const T), size_of::<T>(), name); }
}
<commit_msg>Update lib.rs<commit_after>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
  fn klee_int(name: *const raw::c_char) -> raw::c_int;
  fn klee_warning(name: *const raw::c_char);
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  klee_make_symbolic(data, length as libc::size_t, CString::new(name).unwrap().as_ptr());
}

pub fn warning(name: &str) {
  unsafe { klee_warning(CString::new(name).unwrap().as_ptr()); }
}

pub fn i32(name: &str) -> i32 {
  let result;
  unsafe {
    result = klee_int(CString::new(name).unwrap().as_ptr());
  }
  result
}

pub fn sym<T>(data: &T, name: &str) {
  unsafe{ any(transmute(data as *const T), size_of::<T>(), name); }
}
<|endoftext|>


<commit_before>#![feature(io)]
#![feature(path)]
#![feature(fs)]
#![feature(collections)]
#![feature(core)]

use std::fs::{File};
use std::string::String;
use std::str::StrExt;
use std::path::AsPath;
use std::error::FromError;
use std::io::{Read, BufReader, Write};
use std::vec::Vec;
use std::num::ParseIntError;
use std::mem;

#[derive(Debug)]
pub enum Error {
    IO,
    Parse,
    DataType,
    FieldType,
    Malformed,
    NotImplemented
}

impl FromError<std::io::Error> for Error {
    fn from_error(_: std::io::Error) -> Error {
        Error::IO
    }
}

impl FromError<ParseIntError> for Error {
    fn from_error(_: ParseIntError) -> Error {
        Error::Parse
    }
}

#[derive(Debug)]
pub enum DataType {
    XDRFloat,
    FloatLE,
}

impl DataType {
    fn from_str(s: &str) -> Result<DataType, Error> {
        match s {
            "float_le" => Ok(DataType::FloatLE),
//            "xdr_float" => Ok(DataType::XDRFloat),
            _ => Err(Error::DataType)
        }
    }
}

#[derive(Debug)]
pub enum FieldType {
    Uniform
}

impl FieldType {
    fn from_str(s: &str) -> Result<FieldType, Error> {
        match s {
            "uniform" => Ok(FieldType::Uniform),
            _ => Err(Error::FieldType)
        }
    }
}

pub struct AVSFile<'a> {
    pub ndim: usize,
    pub sizes: Vec<usize>,
    pub data_type: DataType,
    pub field_type: FieldType,
    reader: Box<Read + 'a>
}

impl<'a> AVSFile<'a> {
    pub fn write<W: Write, T>(
                writer: &mut W, ndim: usize, dims: &[usize], data: &[T])
                    -> Result<(), Error> {
        // header
        try!(writer.write_fmt(format_args!("# AVS FLD file (written by avsfldrs github.com/greyhill/avsfldrs)\n")));
        try!(writer.write_fmt(format_args!("ndim={}\n", ndim)));
        try!(writer.write_fmt(format_args!("veclen=1\n")));
        try!(writer.write_fmt(format_args!("nspace={}\n", ndim)));
        try!(writer.write_fmt(format_args!("field=uniform\n")));
        try!(writer.write_fmt(format_args!("data=float_le\n"))); // TODO
        for (id, size) in dims.iter().enumerate() {
            try!(writer.write_fmt(format_args!("dim{}={}\n", id+1, size)));
        }
        try!(writer.write_fmt(format_args!("{}{}", 12 as char, 12 as char)));
        let b: &[u8] = unsafe {
            std::slice::from_raw_parts(data.as_ptr() as *const u8,
                                       data.len()*mem::size_of::<T>())
        };
        try!(writer.write_all(b));
        Ok(())
    }

    pub fn read<T>(self: &mut Self) -> Result<Vec<T>, Error> {
        let size = self.sizes.iter().fold(1 as usize, |l, r| l * *r);
        let mut buf_u8 = Vec::<u8>::with_capacity(mem::size_of::<T>()*size);
        try!(self.reader.read_to_end(&mut buf_u8));
        let buf: Vec<T> = unsafe {
            let ptr = buf_u8.as_mut_ptr();
            let cap = buf_u8.capacity();
            mem::forget(buf_u8);
            Vec::<T>::from_raw_parts(
                mem::transmute(ptr),
                size,
                cap / mem::size_of::<T>())
        };
        Ok(buf)
    }

    pub fn open<P: AsPath>(path: &P) -> Result<AVSFile, Error> {
        let mut reader = BufReader::new(try!(File::open(path)));

        let mut ndim: Option<usize> = None;
        let mut sizes = Vec::<Option<usize>>::new();
        let mut data_type: Option<DataType> = None;
        let mut field_type: Option<FieldType> = None;
        let mut external: Option<String> = None;

        let mut line = String::new();
        let mut last_char: u8 = 0;
        loop {
            let mut new_char_buf: [u8;1] = [ 0u8 ];
            try!(reader.read(&mut new_char_buf));

            // break on two chr 14s
            let new_char = new_char_buf[0];
            if (new_char, last_char) == (12u8, 12u8) {
                break;
            }
            last_char = new_char;

            line.push(new_char as char);

            // new line; process the line and discard
            if new_char == 10 {
                let tokens: Vec<&str> = line.split('=')
                    .map(|s| s.trim()).collect();
                match &tokens[..] {
                    ["ndim", s] => {
                        let nd = try!(s.parse::<usize>());
                        ndim = Some(nd);
                        for _ in 0..nd {
                            sizes.push(None);
                        }
                    },
                    ["dim1", s] => sizes[0] = Some(try!(s.parse::<usize>())),
                    ["dim2", s] => sizes[1] = Some(try!(s.parse::<usize>())),
                    ["dim3", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["dim4", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["dim5", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["dim6", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["dim7", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["data", s] =>
                        data_type = Some(try!(DataType::from_str(s))),
                    ["field", s] =>
                        field_type = Some(try!(FieldType::from_str(s))),
                    ["variable 1 file", s] =>
                        external = Some(String::from_str(s)),
                    _ => {}
                }
            }
            // hack?  code smell?  need borrow in previous block to expire
            if new_char == 10 {
                line.clear();
            }
        }

        match external {
            None => {
                let mut tr = AVSFile {
                    ndim: try!(ndim.ok_or(Error::Malformed)),
                    sizes: Vec::<usize>::new(),
                    data_type: try!(data_type.ok_or(Error::Malformed)),
                    field_type: try!(field_type.ok_or(Error::Malformed)),
                    reader: Box::new(reader),
                };
                for idx in 0..ndim.unwrap() {
                    tr.sizes.push(
                        try!(sizes[idx].ok_or(Error::Malformed)));
                }
                Ok(tr)
            },
            Some(path) => {
                let new_reader = BufReader::new(try!(File::open(&path)));
                let mut tr = AVSFile {
                    ndim: try!(ndim.ok_or(Error::Malformed)),
                    sizes: Vec::<usize>::new(),
                    data_type: try!(data_type.ok_or(Error::Malformed)),
                    field_type: try!(field_type.ok_or(Error::Malformed)),
                    reader: Box::new(new_reader),
                };
                for idx in 0..ndim.unwrap() {
                    tr.sizes.push(
                        try!(sizes[idx].ok_or(Error::Malformed)));
                }
                Ok(tr)
            },
        }
    }
}

<commit_msg>fixed for dimensions > 3<commit_after>#![feature(io)]
#![feature(path)]
#![feature(fs)]
#![feature(collections)]
#![feature(core)]

use std::fs::{File};
use std::string::String;
use std::str::StrExt;
use std::path::AsPath;
use std::error::FromError;
use std::io::{Read, BufReader, Write};
use std::vec::Vec;
use std::num::ParseIntError;
use std::mem;

#[derive(Debug)]
pub enum Error {
    IO,
    Parse,
    DataType,
    FieldType,
    Malformed,
    NotImplemented
}

impl FromError<std::io::Error> for Error {
    fn from_error(_: std::io::Error) -> Error {
        Error::IO
    }
}

impl FromError<ParseIntError> for Error {
    fn from_error(_: ParseIntError) -> Error {
        Error::Parse
    }
}

#[derive(Debug)]
pub enum DataType {
    XDRFloat,
    FloatLE,
}

impl DataType {
    fn from_str(s: &str) -> Result<DataType, Error> {
        match s {
            "float_le" => Ok(DataType::FloatLE),
//            "xdr_float" => Ok(DataType::XDRFloat),
            _ => Err(Error::DataType)
        }
    }
}

#[derive(Debug)]
pub enum FieldType {
    Uniform
}

impl FieldType {
    fn from_str(s: &str) -> Result<FieldType, Error> {
        match s {
            "uniform" => Ok(FieldType::Uniform),
            _ => Err(Error::FieldType)
        }
    }
}

pub struct AVSFile<'a> {
    pub ndim: usize,
    pub sizes: Vec<usize>,
    pub data_type: DataType,
    pub field_type: FieldType,
    reader: Box<Read + 'a>
}

impl<'a> AVSFile<'a> {
    pub fn write<W: Write, T>(
                writer: &mut W, ndim: usize, dims: &[usize], data: &[T])
                    -> Result<(), Error> {
        // header
        try!(writer.write_fmt(format_args!("# AVS FLD file (written by avsfldrs github.com/greyhill/avsfldrs)\n")));
        try!(writer.write_fmt(format_args!("ndim={}\n", ndim)));
        try!(writer.write_fmt(format_args!("veclen=1\n")));
        try!(writer.write_fmt(format_args!("nspace={}\n", ndim)));
        try!(writer.write_fmt(format_args!("field=uniform\n")));
        try!(writer.write_fmt(format_args!("data=float_le\n"))); // TODO
        for (id, size) in dims.iter().enumerate() {
            try!(writer.write_fmt(format_args!("dim{}={}\n", id+1, size)));
        }
        try!(writer.write_fmt(format_args!("{}{}", 12 as char, 12 as char)));
        let b: &[u8] = unsafe {
            std::slice::from_raw_parts(data.as_ptr() as *const u8,
                                       data.len()*mem::size_of::<T>())
        };
        try!(writer.write_all(b));
        Ok(())
    }

    pub fn read<T>(self: &mut Self) -> Result<Vec<T>, Error> {
        let size = self.sizes.iter().fold(1 as usize, |l, r| l * *r);
        let mut buf_u8 = Vec::<u8>::with_capacity(mem::size_of::<T>()*size);
        try!(self.reader.read_to_end(&mut buf_u8));
        let buf: Vec<T> = unsafe {
            let ptr = buf_u8.as_mut_ptr();
            let cap = buf_u8.capacity();
            mem::forget(buf_u8);
            Vec::<T>::from_raw_parts(
                mem::transmute(ptr),
                size,
                cap / mem::size_of::<T>())
        };
        Ok(buf)
    }

    pub fn open<P: AsPath>(path: &P) -> Result<AVSFile, Error> {
        let mut reader = BufReader::new(try!(File::open(path)));

        let mut ndim: Option<usize> = None;
        let mut sizes = Vec::<Option<usize>>::new();
        let mut data_type: Option<DataType> = None;
        let mut field_type: Option<FieldType> = None;
        let mut external: Option<String> = None;

        let mut line = String::new();
        let mut last_char: u8 = 0;
        loop {
            let mut new_char_buf: [u8;1] = [ 0u8 ];
            try!(reader.read(&mut new_char_buf));

            // break on two chr 14s
            let new_char = new_char_buf[0];
            if (new_char, last_char) == (12u8, 12u8) {
                break;
            }
            last_char = new_char;

            line.push(new_char as char);

            // new line; process the line and discard
            if new_char == 10 {
                let tokens: Vec<&str> = line.split('=')
                    .map(|s| s.trim()).collect();
                match &tokens[..] {
                    ["ndim", s] => {
                        let nd = try!(s.parse::<usize>());
                        ndim = Some(nd);
                        for _ in 0..nd {
                            sizes.push(None);
                        }
                    },
                    ["dim1", s] => sizes[0] = Some(try!(s.parse::<usize>())),
                    ["dim2", s] => sizes[1] = Some(try!(s.parse::<usize>())),
                    ["dim3", s] => sizes[2] = Some(try!(s.parse::<usize>())),
                    ["dim4", s] => sizes[3] = Some(try!(s.parse::<usize>())),
                    ["dim5", s] => sizes[4] = Some(try!(s.parse::<usize>())),
                    ["dim6", s] => sizes[5] = Some(try!(s.parse::<usize>())),
                    ["dim7", s] => sizes[6] = Some(try!(s.parse::<usize>())),
                    ["data", s] =>
                        data_type = Some(try!(DataType::from_str(s))),
                    ["field", s] =>
                        field_type = Some(try!(FieldType::from_str(s))),
                    ["variable 1 file", s] =>
                        external = Some(String::from_str(s)),
                    _ => {}
                }
            }
            // hack?  code smell?  need borrow in previous block to expire
            if new_char == 10 {
                line.clear();
            }
        }

        match external {
            None => {
                let mut tr = AVSFile {
                    ndim: try!(ndim.ok_or(Error::Malformed)),
                    sizes: Vec::<usize>::new(),
                    data_type: try!(data_type.ok_or(Error::Malformed)),
                    field_type: try!(field_type.ok_or(Error::Malformed)),
                    reader: Box::new(reader),
                };
                for idx in 0..ndim.unwrap() {
                    tr.sizes.push(
                        try!(sizes[idx].ok_or(Error::Malformed)));
                }
                Ok(tr)
            },
            Some(path) => {
                let new_reader = BufReader::new(try!(File::open(&path)));
                let mut tr = AVSFile {
                    ndim: try!(ndim.ok_or(Error::Malformed)),
                    sizes: Vec::<usize>::new(),
                    data_type: try!(data_type.ok_or(Error::Malformed)),
                    field_type: try!(field_type.ok_or(Error::Malformed)),
                    reader: Box::new(new_reader),
                };
                for idx in 0..ndim.unwrap() {
                    tr.sizes.push(
                        try!(sizes[idx].ok_or(Error::Malformed)));
                }
                Ok(tr)
            },
        }
    }
}

<|endoftext|>


<commit_before>#![feature(optin_builtin_traits)]
#![feature(unsafe_destructor)]
#![feature(core)]
#![feature(std_misc)]
extern crate time;

use std::cell::UnsafeCell;
use std::sync::{
    TryLockError,
    LockResult,
    TryLockResult,
    PoisonError,
};
use std::thread;
use std::ops::{Deref, DerefMut};

use std::sync::atomic::{Ordering, AtomicUsize};

// Panic guards, taken from std::sync

// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Flag { failed: UnsafeCell<bool> }
unsafe impl Send for Flag {}
unsafe impl Sync for Flag {}

impl Flag {
    #[inline]
    fn new() -> Flag {
        Flag { failed : UnsafeCell { value : false }}
    }

    #[inline]
    fn borrow(&self) -> LockResult<Guard> {
        let ret = Guard { panicking: thread::panicking() };
        if unsafe { *self.failed.get() } {
            Err(PoisonError::new(ret))
        } else {
            Ok(ret)
        }
    }

    #[inline]
    fn done(&self, guard: &Guard) {
        if !guard.panicking && thread::panicking() {
            unsafe { *self.failed.get() = true; }
        }
    }

    #[inline]
    fn get(&self) -> bool {
        unsafe { *self.failed.get() }
    }
}

struct Guard {
    panicking: bool,
}

fn map_result<T, U, F>(result: LockResult<T>, f: F)
                           -> LockResult<U>
                           where F: FnOnce(T) -> U {
    match result {
        Ok(t) => Ok(f(t)),
        Err(err) => Err(PoisonError::new(f(err.into_inner())))
    }
}

// SpinLock implementation
//
const SHARED : usize = 0x80000000;
const EXCLWAIT : usize = 0x00100000;
const MAXWAIT : u64 = 2_000_000_000;
const SPIN_COUNT : u32 = 0xFF;

pub struct SpinLock<T> {
    count : AtomicUsize,
    data : UnsafeCell<T>,
    poison : Flag,
}

unsafe impl<T: Send + Sync> Send for SpinLock<T> {}
unsafe impl<T: Send + Sync> Sync for SpinLock<T> {}

impl<T : Send + Sync> SpinLock<T> {
    pub fn new(data : T) -> SpinLock<T> {
        SpinLock {
            count : AtomicUsize::new(0),
            data : UnsafeCell::new(data),
            poison : Flag::new(),
        }
    }

    #[inline]
    pub fn write(&self) -> LockResult<SpinLockWriteGuard<T>> {
        if self.count.fetch_add(1, Ordering::AcqRel) != 0 {
            return self.write_contested()
        }
        SpinLockWriteGuard::new(self, &self.data)
    }

    pub fn write_contested(&self) -> LockResult<SpinLockWriteGuard<T>> {
        let mut base_time : u64 = 0;
        let mut time : u64;
        let mut i = 0u32;

        self.count.fetch_add(EXCLWAIT - 1, Ordering::AcqRel);
        self.count.fetch_and(! SHARED, Ordering::AcqRel);
        loop {
            let count = self.count.load(Ordering::Acquire);
            if count & (EXCLWAIT - 1) == 0 &&
                self.count.compare_and_swap(count, (count & !EXCLWAIT) | 1, Ordering::AcqRel) == count {
                    break;
                }

            if i >= SPIN_COUNT  {
                time = time::precise_time_ns();
                if base_time == 0 {
                    base_time = time;
                } else if time - base_time > MAXWAIT {
                    // FIXME : not 100% sure about what happens if there are multiple EXCL requests pending
                    self.count.fetch_and(! EXCLWAIT, Ordering::AcqRel);
                    panic!("Spinning on a spin lock for too long")
                }
                thread::yield_now();
            } else {
                i += 1;
            }
        }
        SpinLockWriteGuard::new(self, &self.data)
    }

    #[inline]
    pub fn read(&self) -> LockResult<SpinLockReadGuard<T>> {
        if self.count.compare_and_swap(0, SHARED | 1, Ordering::AcqRel) == 0 {
            return self.read_contested()
        }
        SpinLockReadGuard::new(self, &self.data)
    }

    pub fn read_contested(&self) -> LockResult<SpinLockReadGuard<T>> {
        let mut i = 0u32;
        let mut base_time : u64 = 0;
        let mut time : u64;
        loop {
            let count = self.count.load(Ordering::Acquire);
            if count == 0 {
                if self.count.compare_and_swap(0, SHARED | 1, Ordering::AcqRel) == 0 {
                    break
                }
            } else if count & SHARED == SHARED {
                if self.count.compare_and_swap(count, count + 1, Ordering::AcqRel) == count {
                    break
                }
            }
            if i >= SPIN_COUNT  {
                time = time::precise_time_ns();
                if base_time == 0 {
                    base_time = time;
                } else if time - base_time > MAXWAIT {
                    panic!("Spinning on a spin lock for too long")
                }
                thread::yield_now();
            } else {
                i+=1;
            }
        }
        SpinLockReadGuard::new(self, &self.data)
    }

    #[inline]
    pub fn try_read(&self) -> TryLockResult<SpinLockReadGuard<T>> {
        if self.count.compare_and_swap(0, 1 | SHARED, Ordering::AcqRel) == 0 {
            return Ok(try!(SpinLockReadGuard::new(self, &self.data)))
        } else {
            let count = self.count.load(Ordering::Acquire);
            if self.count.compare_and_swap(count, count + 1, Ordering::AcqRel) == count {
                return Ok(try!(SpinLockReadGuard::new(self, &self.data)))
            }
            return Err(TryLockError::WouldBlock)
        }
    }

    pub fn try_write(&self) -> TryLockResult<SpinLockWriteGuard<T>> {
        if self.count.compare_and_swap(0, 1, Ordering::AcqRel) == 0 {
            Ok(try!(SpinLockWriteGuard::new(self, &self.data)))
        } else {
            Err(TryLockError::WouldBlock)
        }
    }

    #[inline]
    pub fn is_poisonned(&self) -> bool {
        self.poison.get()
    }

}

impl<T> SpinLock<T> {

    #[inline]
    fn read_unlock(&self) {
        self.count.fetch_sub(1, Ordering::AcqRel);

        while self.count.load(Ordering::Acquire) == SHARED {
            if self.count.compare_and_swap(SHARED, 0, Ordering::AcqRel) == SHARED {
                break
            }
        }
    }

    fn write_unlock(&self) {
        self.count.fetch_sub(1, Ordering::AcqRel);
    }
}

#[must_use]
pub struct SpinLockReadGuard<'a, T : 'a> {
    lock : &'a SpinLock<T>,
    data : &'a UnsafeCell<T>,
}

impl<'a, T> !Send for SpinLockReadGuard<'a, T> {}

impl<'a, T> SpinLockReadGuard<'a, T> {
    fn new(lock : &'a SpinLock<T>, data : &'a UnsafeCell<T>) -> LockResult<SpinLockReadGuard<'a, T>> {
        map_result(lock.poison.borrow(), |_| {
            SpinLockReadGuard {
                lock : lock,
                data : data,
            }
        })
    }
}

impl<'a, T> Deref for SpinLockReadGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T { unsafe { &*self.data.get() } }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinLockReadGuard<'a, T> {
    fn drop(&mut self) {
        self.lock.read_unlock();
    }
}

#[must_use]
pub struct SpinLockWriteGuard<'a, T : 'a> {
    lock : &'a SpinLock<T>,
    data : &'a UnsafeCell<T>,
    poison :  Guard,
}

impl<'a, T> !Send for SpinLockWriteGuard<'a, T> {}

impl<'a, T> SpinLockWriteGuard<'a, T> {
    fn new(lock : &'a SpinLock<T>, data : &'a UnsafeCell<T>) -> LockResult<SpinLockWriteGuard<'a, T>> {
        map_result(lock.poison.borrow(), |guard| {
            SpinLockWriteGuard {
                lock : lock,
                data : data,
                poison : guard,
            }
        })
    }
}

impl<'a, T> Deref for SpinLockWriteGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T { unsafe { &*self.data.get() } }
}

impl<'a, T> DerefMut for SpinLockWriteGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.data.get() } }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinLockWriteGuard<'a, T> {
    fn drop(&mut self) {
        self.lock.poison.done(&self.poison);
        self.lock.write_unlock();
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Re-export TryLockError<commit_after>#![feature(optin_builtin_traits)]
#![feature(unsafe_destructor)]
#![feature(core)]
#![feature(std_misc)]
extern crate time;

use std::cell::UnsafeCell;
pub use std::sync::{
    TryLockError,
    LockResult,
    TryLockResult,
    PoisonError,
};
use std::thread;
use std::ops::{Deref, DerefMut};

use std::sync::atomic::{Ordering, AtomicUsize};

// Panic guards, taken from std::sync

// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Flag { failed: UnsafeCell<bool> }
unsafe impl Send for Flag {}
unsafe impl Sync for Flag {}

impl Flag {
    #[inline]
    fn new() -> Flag {
        Flag { failed : UnsafeCell { value : false }}
    }

    #[inline]
    fn borrow(&self) -> LockResult<Guard> {
        let ret = Guard { panicking: thread::panicking() };
        if unsafe { *self.failed.get() } {
            Err(PoisonError::new(ret))
        } else {
            Ok(ret)
        }
    }

    #[inline]
    fn done(&self, guard: &Guard) {
        if !guard.panicking && thread::panicking() {
            unsafe { *self.failed.get() = true; }
        }
    }

    #[inline]
    fn get(&self) -> bool {
        unsafe { *self.failed.get() }
    }
}

struct Guard {
    panicking: bool,
}

fn map_result<T, U, F>(result: LockResult<T>, f: F)
                           -> LockResult<U>
                           where F: FnOnce(T) -> U {
    match result {
        Ok(t) => Ok(f(t)),
        Err(err) => Err(PoisonError::new(f(err.into_inner())))
    }
}

// SpinLock implementation
//
const SHARED : usize = 0x80000000;
const EXCLWAIT : usize = 0x00100000;
const MAXWAIT : u64 = 2_000_000_000;
const SPIN_COUNT : u32 = 0xFF;

pub struct SpinLock<T> {
    count : AtomicUsize,
    data : UnsafeCell<T>,
    poison : Flag,
}

unsafe impl<T: Send + Sync> Send for SpinLock<T> {}
unsafe impl<T: Send + Sync> Sync for SpinLock<T> {}

impl<T : Send + Sync> SpinLock<T> {
    pub fn new(data : T) -> SpinLock<T> {
        SpinLock {
            count : AtomicUsize::new(0),
            data : UnsafeCell::new(data),
            poison : Flag::new(),
        }
    }

    #[inline]
    pub fn write(&self) -> LockResult<SpinLockWriteGuard<T>> {
        if self.count.fetch_add(1, Ordering::AcqRel) != 0 {
            return self.write_contested()
        }
        SpinLockWriteGuard::new(self, &self.data)
    }

    pub fn write_contested(&self) -> LockResult<SpinLockWriteGuard<T>> {
        let mut base_time : u64 = 0;
        let mut time : u64;
        let mut i = 0u32;

        self.count.fetch_add(EXCLWAIT - 1, Ordering::AcqRel);
        self.count.fetch_and(! SHARED, Ordering::AcqRel);
        loop {
            let count = self.count.load(Ordering::Acquire);
            if count & (EXCLWAIT - 1) == 0 &&
                self.count.compare_and_swap(count, (count & !EXCLWAIT) | 1, Ordering::AcqRel) == count {
                    break;
                }

            if i >= SPIN_COUNT  {
                time = time::precise_time_ns();
                if base_time == 0 {
                    base_time = time;
                } else if time - base_time > MAXWAIT {
                    // FIXME : not 100% sure about what happens if there are multiple EXCL requests pending
                    self.count.fetch_and(! EXCLWAIT, Ordering::AcqRel);
                    panic!("Spinning on a spin lock for too long")
                }
                thread::yield_now();
            } else {
                i += 1;
            }
        }
        SpinLockWriteGuard::new(self, &self.data)
    }

    #[inline]
    pub fn read(&self) -> LockResult<SpinLockReadGuard<T>> {
        if self.count.compare_and_swap(0, SHARED | 1, Ordering::AcqRel) == 0 {
            return self.read_contested()
        }
        SpinLockReadGuard::new(self, &self.data)
    }

    pub fn read_contested(&self) -> LockResult<SpinLockReadGuard<T>> {
        let mut i = 0u32;
        let mut base_time : u64 = 0;
        let mut time : u64;
        loop {
            let count = self.count.load(Ordering::Acquire);
            if count == 0 {
                if self.count.compare_and_swap(0, SHARED | 1, Ordering::AcqRel) == 0 {
                    break
                }
            } else if count & SHARED == SHARED {
                if self.count.compare_and_swap(count, count + 1, Ordering::AcqRel) == count {
                    break
                }
            }
            if i >= SPIN_COUNT  {
                time = time::precise_time_ns();
                if base_time == 0 {
                    base_time = time;
                } else if time - base_time > MAXWAIT {
                    panic!("Spinning on a spin lock for too long")
                }
                thread::yield_now();
            } else {
                i+=1;
            }
        }
        SpinLockReadGuard::new(self, &self.data)
    }

    #[inline]
    pub fn try_read(&self) -> TryLockResult<SpinLockReadGuard<T>> {
        if self.count.compare_and_swap(0, 1 | SHARED, Ordering::AcqRel) == 0 {
            return Ok(try!(SpinLockReadGuard::new(self, &self.data)))
        } else {
            let count = self.count.load(Ordering::Acquire);
            if self.count.compare_and_swap(count, count + 1, Ordering::AcqRel) == count {
                return Ok(try!(SpinLockReadGuard::new(self, &self.data)))
            }
            return Err(TryLockError::WouldBlock)
        }
    }

    pub fn try_write(&self) -> TryLockResult<SpinLockWriteGuard<T>> {
        if self.count.compare_and_swap(0, 1, Ordering::AcqRel) == 0 {
            Ok(try!(SpinLockWriteGuard::new(self, &self.data)))
        } else {
            Err(TryLockError::WouldBlock)
        }
    }

    #[inline]
    pub fn is_poisonned(&self) -> bool {
        self.poison.get()
    }

}

impl<T> SpinLock<T> {

    #[inline]
    fn read_unlock(&self) {
        self.count.fetch_sub(1, Ordering::AcqRel);

        while self.count.load(Ordering::Acquire) == SHARED {
            if self.count.compare_and_swap(SHARED, 0, Ordering::AcqRel) == SHARED {
                break
            }
        }
    }

    fn write_unlock(&self) {
        self.count.fetch_sub(1, Ordering::AcqRel);
    }
}

#[must_use]
pub struct SpinLockReadGuard<'a, T : 'a> {
    lock : &'a SpinLock<T>,
    data : &'a UnsafeCell<T>,
}

impl<'a, T> !Send for SpinLockReadGuard<'a, T> {}

impl<'a, T> SpinLockReadGuard<'a, T> {
    fn new(lock : &'a SpinLock<T>, data : &'a UnsafeCell<T>) -> LockResult<SpinLockReadGuard<'a, T>> {
        map_result(lock.poison.borrow(), |_| {
            SpinLockReadGuard {
                lock : lock,
                data : data,
            }
        })
    }
}

impl<'a, T> Deref for SpinLockReadGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T { unsafe { &*self.data.get() } }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinLockReadGuard<'a, T> {
    fn drop(&mut self) {
        self.lock.read_unlock();
    }
}

#[must_use]
pub struct SpinLockWriteGuard<'a, T : 'a> {
    lock : &'a SpinLock<T>,
    data : &'a UnsafeCell<T>,
    poison :  Guard,
}

impl<'a, T> !Send for SpinLockWriteGuard<'a, T> {}

impl<'a, T> SpinLockWriteGuard<'a, T> {
    fn new(lock : &'a SpinLock<T>, data : &'a UnsafeCell<T>) -> LockResult<SpinLockWriteGuard<'a, T>> {
        map_result(lock.poison.borrow(), |guard| {
            SpinLockWriteGuard {
                lock : lock,
                data : data,
                poison : guard,
            }
        })
    }
}

impl<'a, T> Deref for SpinLockWriteGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T { unsafe { &*self.data.get() } }
}

impl<'a, T> DerefMut for SpinLockWriteGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.data.get() } }
}

#[unsafe_destructor]
impl<'a, T> Drop for SpinLockWriteGuard<'a, T> {
    fn drop(&mut self) {
        self.lock.poison.done(&self.poison);
        self.lock.write_unlock();
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                scope.transfer_stack(self.curr_scope_mut(), false)?
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let scope = self.closures.remove(old).unwrap();
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        self.curr_scope_mut().push_var(var, ptr)?;
        Ok(binding)
    }

    /// Try to load the variable behind a binding
    fn load(&self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<commit_msg>Update ScopeTag's binding when renaming closures<commit_after>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                scope.transfer_stack(self.curr_scope_mut(), false)?
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let mut scope = self.closures.remove(old).unwrap();
            scope.tag = ScopeTag::Closure(new.clone());
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        self.curr_scope_mut().push_var(var, ptr)?;
        Ok(binding)
    }

    /// Try to load the variable behind a binding
    fn load(&self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<|endoftext|>


<commit_before>//! A library for build scripts to compile custom C code
//!
//! This library is intended to be used as a `build-dependencies` entry in
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! gcc = "0.2"
//! ```
//!
//! The purpose of this crate is to provide the utility functions necessary to
//! compile C code into a static archive which is then linked into a Rust crate.
//! The top-level `compile_library` function serves as a convenience and more
//! advanced configuration is available through the `Config` builder.
//!
//! This crate will automatically detect situations such as cross compilation or
//! other environment variables set by Cargo and will build code appropriately.
//!
//! # Examples
//!
//! Use the default configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::compile_library("libfoo.a", &["src/foo.c"]);
//! }
//! ```
//!
//! Use more advanced configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::Config::new()
//!                 .file("src/foo.c")
//!                 .define("FOO", Some("bar"))
//!                 .include("src")
//!                 .compile("libfoo.a");
//! }
//! ```

#![doc(html_root_url = "http://alexcrichton.com/gcc-rs")]
#![feature(io, path)]
#![cfg_attr(test, deny(warnings))]

use std::env;
use std::fs;
use std::io;
use std::path::{PathBuf, AsPath};
use std::process::{Command, Stdio};

/// Extra configuration to pass to gcc.
pub struct Config {
    include_directories: Vec<PathBuf>,
    definitions: Vec<(String, Option<String>)>,
    objects: Vec<PathBuf>,
    flags: Vec<String>,
    files: Vec<PathBuf>,
}

fn getenv(v: &str) -> Option<String> {
    let r = env::var(v).ok();
    println!("{} = {:?}", v, r);
    r
}

fn getenv_unwrap(v: &str) -> String {
    match getenv(v) {
        Some(s) => s,
        None => fail(&format!("environment variable `{}` not defined", v)),
    }
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// This function will also print all metadata on standard output for Cargo.
///
/// # Example
///
/// ```no_run
/// gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    let mut c = Config::new();
    for f in files.iter() {
        c.file(*f);
    }
    c.compile(output)
}

impl Config {
    /// Construct a new instance of a blank set of configuration.
    ///
    /// This builder is finished with the `compile` function.
    pub fn new() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
            flags: Vec::new(),
            files: Vec::new(),
        }
    }

    /// Add a directory to the `-I` or include path for headers
    pub fn include<P: AsPath + ?Sized>(&mut self, dir: &P) -> &mut Config {
        self.include_directories.push(dir.as_path().to_path_buf());
        self
    }

    /// Specify a `-D` variable with an optional value.
    pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Config {
        self.definitions.push((var.to_string(), val.map(|s| s.to_string())));
        self
    }

    /// Add an arbitrary object file to link in
    pub fn object<P: AsPath + ?Sized>(&mut self, obj: &P) -> &mut Config {
        self.objects.push(obj.as_path().to_path_buf());
        self
    }

    /// Add an arbitrary flag to the invocation of the compiler
    pub fn flag(&mut self, flag: &str) -> &mut Config {
        self.flags.push(flag.to_string());
        self
    }

    /// Add a file which will be compiled
    pub fn file<P: AsPath + ?Sized>(&mut self, p: &P) -> &mut Config {
        self.files.push(p.as_path().to_path_buf());
        self
    }

    /// Run the compiler, generating the file `output`
    ///
    /// The name `output` must begin with `lib` and end with `.a`
    pub fn compile(&self, output: &str) {
        assert!(output.starts_with("lib"));
        assert!(output.ends_with(".a"));

        let target = getenv_unwrap("TARGET");
        let src = PathBuf::new(&getenv_unwrap("CARGO_MANIFEST_DIR"));
        let dst = PathBuf::new(&getenv_unwrap("OUT_DIR"));
        let mut objects = Vec::new();
        for file in self.files.iter() {
            let mut cmd = self.gcc();
            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            run(cmd.arg(&src.join(file)).arg("-o").arg(&obj),
                &gcc(&target));
            objects.push(obj);
        }

        run(Command::new(&ar(&target)).arg("crus")
                                      .arg(&dst.join(output))
                                      .args(&objects)
                                      .args(&self.objects),
            &ar(&target));
        println!("cargo:rustc-flags=-L native={} -l static={}",
                 dst.display(), &output[3..output.len() - 2]);
    }

    fn gcc(&self) -> Command {
        let target = getenv_unwrap("TARGET");
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

        let mut cmd = Command::new(&gcc(&target));
        cmd.arg(&format!("-O{}", opt_level));
        cmd.arg("-c");
        cmd.arg("-ffunction-sections").arg("-fdata-sections");
        cmd.args(&cflags());

        if target.contains("-ios") {
            cmd.args(&ios_flags(&target));
        } else {
            if target.contains("windows") {
                cmd.arg("-mwin32");
            }

            if target.contains("i686") {
                cmd.arg("-m32");
            } else if target.contains("x86_64") {
                cmd.arg("-m64");
            }

            if !target.contains("i686") {
                cmd.arg("-fPIC");
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg("-I").arg(directory);
        }

        for flag in self.flags.iter() {
            cmd.arg(flag);
        }

        for &(ref key, ref value) in self.definitions.iter() {
            if let &Some(ref value) = value {
                cmd.arg(&format!("-D{}={}", key, value));
            } else {
                cmd.arg(&format!("-D{}", key));
            }
        }
        return cmd;
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == io::ErrorKind::FileNotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = getenv_unwrap("TARGET");
    let host = getenv_unwrap("HOST");
    let kind = if host == target {"HOST"} else {"TARGET"};
    let target_u = target.replace("-", "_");
    let res = getenv(&format!("{}_{}", var_base, target))
        .or_else(|| getenv(&format!("{}_{}", var_base, target_u)))
        .or_else(|| getenv(&format!("{}_{}", kind, var_base)))
        .or_else(|| getenv(var_base));

    match res {
        Some(res) => Ok(res),
        None => Err("Could not get environment variable".to_string()),
    }
}

fn gcc(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CC").unwrap_or(if cfg!(windows) {
        "gcc".to_string()
    } else if is_android {
        format!("{}-gcc", target)
    } else {
        "cc".to_string()
    })
}

fn ar(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("AR").unwrap_or(if is_android {
        format!("{}-ar", target)
    } else {
        "ar".to_string()
    })
}

fn cflags() -> Vec<String> {
    get_var("CFLAGS").unwrap_or(String::new())
       .split(|c: char| c.is_whitespace()).filter(|s| !s.is_empty())
       .map(|s| s.to_string())
       .collect()
}

fn ios_flags(target: &str) -> Vec<String> {
    enum ArchSpec {
        Device(&'static str),
        Simulator(&'static str),
    }

    let mut res = Vec::new();

    let arch = target.split('-').nth(0).expect("expected target in format `arch-vendor-os`");
    let arch = match arch {
        "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
        "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
        "arm64" | "aarch64" => ArchSpec::Device("arm64"),
        "i386" | "i686" => ArchSpec::Simulator("-m32"),
        "x86_64" => ArchSpec::Simulator("-m64"),
        _ => unreachable!("Unknown arch for iOS target")
    };

    let sdk = match arch {
        ArchSpec::Device(arch) => {
            res.push("-arch".to_string());
            res.push(arch.to_string());
            "iphoneos"
        },
        ArchSpec::Simulator(arch) => {
            res.push(arch.to_string());
            "iphonesimulator"
        }
    };

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(Stdio::inherit())
        .output()
        .unwrap()
        .stdout;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot".to_string());
    res.push(sdk_path.trim().to_string());

    res
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    panic!()
}
<commit_msg>Update to rust master<commit_after>//! A library for build scripts to compile custom C code
//!
//! This library is intended to be used as a `build-dependencies` entry in
//! `Cargo.toml`:
//!
//! ```toml
//! [build-dependencies]
//! gcc = "0.2"
//! ```
//!
//! The purpose of this crate is to provide the utility functions necessary to
//! compile C code into a static archive which is then linked into a Rust crate.
//! The top-level `compile_library` function serves as a convenience and more
//! advanced configuration is available through the `Config` builder.
//!
//! This crate will automatically detect situations such as cross compilation or
//! other environment variables set by Cargo and will build code appropriately.
//!
//! # Examples
//!
//! Use the default configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::compile_library("libfoo.a", &["src/foo.c"]);
//! }
//! ```
//!
//! Use more advanced configuration:
//!
//! ```no_run
//! extern crate gcc;
//!
//! fn main() {
//!     gcc::Config::new()
//!                 .file("src/foo.c")
//!                 .define("FOO", Some("bar"))
//!                 .include("src")
//!                 .compile("libfoo.a");
//! }
//! ```

#![doc(html_root_url = "http://alexcrichton.com/gcc-rs")]
#![feature(io, std_misc)]
#![cfg_attr(test, deny(warnings))]

use std::env;
use std::fs;
use std::io;
use std::path::{PathBuf, AsPath};
use std::process::{Command, Stdio};

/// Extra configuration to pass to gcc.
pub struct Config {
    include_directories: Vec<PathBuf>,
    definitions: Vec<(String, Option<String>)>,
    objects: Vec<PathBuf>,
    flags: Vec<String>,
    files: Vec<PathBuf>,
}

fn getenv(v: &str) -> Option<String> {
    let r = env::var(v).ok();
    println!("{} = {:?}", v, r);
    r
}

fn getenv_unwrap(v: &str) -> String {
    match getenv(v) {
        Some(s) => s,
        None => fail(&format!("environment variable `{}` not defined", v)),
    }
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// This function will also print all metadata on standard output for Cargo.
///
/// # Example
///
/// ```no_run
/// gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
/// ```
pub fn compile_library(output: &str, files: &[&str]) {
    let mut c = Config::new();
    for f in files.iter() {
        c.file(*f);
    }
    c.compile(output)
}

impl Config {
    /// Construct a new instance of a blank set of configuration.
    ///
    /// This builder is finished with the `compile` function.
    pub fn new() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
            flags: Vec::new(),
            files: Vec::new(),
        }
    }

    /// Add a directory to the `-I` or include path for headers
    pub fn include<P: AsPath>(&mut self, dir: P) -> &mut Config {
        self.include_directories.push(dir.as_path().to_path_buf());
        self
    }

    /// Specify a `-D` variable with an optional value.
    pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Config {
        self.definitions.push((var.to_string(), val.map(|s| s.to_string())));
        self
    }

    /// Add an arbitrary object file to link in
    pub fn object<P: AsPath>(&mut self, obj: P) -> &mut Config {
        self.objects.push(obj.as_path().to_path_buf());
        self
    }

    /// Add an arbitrary flag to the invocation of the compiler
    pub fn flag(&mut self, flag: &str) -> &mut Config {
        self.flags.push(flag.to_string());
        self
    }

    /// Add a file which will be compiled
    pub fn file<P: AsPath>(&mut self, p: P) -> &mut Config {
        self.files.push(p.as_path().to_path_buf());
        self
    }

    /// Run the compiler, generating the file `output`
    ///
    /// The name `output` must begin with `lib` and end with `.a`
    pub fn compile(&self, output: &str) {
        assert!(output.starts_with("lib"));
        assert!(output.ends_with(".a"));

        let target = getenv_unwrap("TARGET");
        let src = PathBuf::new(&getenv_unwrap("CARGO_MANIFEST_DIR"));
        let dst = PathBuf::new(&getenv_unwrap("OUT_DIR"));
        let mut objects = Vec::new();
        for file in self.files.iter() {
            let mut cmd = self.gcc();
            let obj = dst.join(file).with_extension("o");
            fs::create_dir_all(&obj.parent().unwrap()).unwrap();
            run(cmd.arg(&src.join(file)).arg("-o").arg(&obj),
                &gcc(&target));
            objects.push(obj);
        }

        run(Command::new(&ar(&target)).arg("crus")
                                      .arg(&dst.join(output))
                                      .args(&objects)
                                      .args(&self.objects),
            &ar(&target));
        println!("cargo:rustc-flags=-L native={} -l static={}",
                 dst.display(), &output[3..output.len() - 2]);
    }

    fn gcc(&self) -> Command {
        let target = getenv_unwrap("TARGET");
        let opt_level = getenv_unwrap("OPT_LEVEL");
        let profile = getenv_unwrap("PROFILE");
        println!("{} {}", profile, opt_level);

        let mut cmd = Command::new(&gcc(&target));
        cmd.arg(&format!("-O{}", opt_level));
        cmd.arg("-c");
        cmd.arg("-ffunction-sections").arg("-fdata-sections");
        cmd.args(&cflags());

        if target.contains("-ios") {
            cmd.args(&ios_flags(&target));
        } else {
            if target.contains("windows") {
                cmd.arg("-mwin32");
            }

            if target.contains("i686") {
                cmd.arg("-m32");
            } else if target.contains("x86_64") {
                cmd.arg("-m64");
            }

            if !target.contains("i686") {
                cmd.arg("-fPIC");
            }
        }

        for directory in self.include_directories.iter() {
            cmd.arg("-I").arg(directory);
        }

        for flag in self.flags.iter() {
            cmd.arg(flag);
        }

        for &(ref key, ref value) in self.definitions.iter() {
            if let &Some(ref value) = value {
                cmd.arg(&format!("-D{}={}", key, value));
            } else {
                cmd.arg(&format!("-D{}", key));
            }
        }
        return cmd;
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == io::ErrorKind::FileNotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = getenv_unwrap("TARGET");
    let host = getenv_unwrap("HOST");
    let kind = if host == target {"HOST"} else {"TARGET"};
    let target_u = target.replace("-", "_");
    let res = getenv(&format!("{}_{}", var_base, target))
        .or_else(|| getenv(&format!("{}_{}", var_base, target_u)))
        .or_else(|| getenv(&format!("{}_{}", kind, var_base)))
        .or_else(|| getenv(var_base));

    match res {
        Some(res) => Ok(res),
        None => Err("Could not get environment variable".to_string()),
    }
}

fn gcc(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("CC").unwrap_or(if cfg!(windows) {
        "gcc".to_string()
    } else if is_android {
        format!("{}-gcc", target)
    } else {
        "cc".to_string()
    })
}

fn ar(target: &str) -> String {
    let is_android = target.find("android").is_some();

    get_var("AR").unwrap_or(if is_android {
        format!("{}-ar", target)
    } else {
        "ar".to_string()
    })
}

fn cflags() -> Vec<String> {
    get_var("CFLAGS").unwrap_or(String::new())
       .split(|c: char| c.is_whitespace()).filter(|s| !s.is_empty())
       .map(|s| s.to_string())
       .collect()
}

fn ios_flags(target: &str) -> Vec<String> {
    enum ArchSpec {
        Device(&'static str),
        Simulator(&'static str),
    }

    let mut res = Vec::new();

    let arch = target.split('-').nth(0).expect("expected target in format `arch-vendor-os`");
    let arch = match arch {
        "arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"),
        "armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"),
        "arm64" | "aarch64" => ArchSpec::Device("arm64"),
        "i386" | "i686" => ArchSpec::Simulator("-m32"),
        "x86_64" => ArchSpec::Simulator("-m64"),
        _ => unreachable!("Unknown arch for iOS target")
    };

    let sdk = match arch {
        ArchSpec::Device(arch) => {
            res.push("-arch".to_string());
            res.push(arch.to_string());
            "iphoneos"
        },
        ArchSpec::Simulator(arch) => {
            res.push(arch.to_string());
            "iphonesimulator"
        }
    };

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(Stdio::inherit())
        .output()
        .unwrap()
        .stdout;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot".to_string());
    res.push(sdk_path.trim().to_string());

    res
}

fn fail(s: &str) -> ! {
    println!("\n\n{}\n\n", s);
    panic!()
}
<|endoftext|>