<commit_before>//! The worker module provides the Worker struct which receives instructions from
//! the master, renders and reports back its results

use std::path::PathBuf;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::iter;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encoded_size, decode};

use scene::Scene;
use film::RenderTarget;
use exec::Config;
use exec::distrib::Instructions;

pub static PORT: u16 = 63234;

#[derive(RustcEncodable, RustcDecodable)]
pub struct Frame {
    pub encoded_size: u64,
    pub frame: usize,
    pub block_size: (usize, usize),
    pub blocks: Vec<(usize, usize)>,
    pub pixels: Vec<f32>,
}

pub struct Worker {
    instructions: Instructions,
    pub render_target: RenderTarget,
    pub scene: Scene,
    pub config: Config,
    master: TcpStream,
}

impl Worker {
    /// Listen on the worker `PORT` for the master to contact us
    /// and send us instructions about the scene we should render and
    /// what parts of it we've been assigned
    pub fn listen_for_master(num_threads: u32) -> Worker {
        let (instructions, master) = get_instructions();
        let (scene, rt, spp, mut frame_info) = Scene::load_file(&instructions.scene);
        frame_info.start = instructions.frames.0;
        frame_info.end = instructions.frames.1;
        let config = Config::new(PathBuf::from("/tmp"), instructions.scene.clone(), spp,
                                 num_threads, frame_info,
                                 (instructions.block_start, instructions.block_count));
        Worker { instructions: instructions, render_target: rt, scene: scene,
                 config: config, master: master }
    }
    /// Send our blocks back to the master
    pub fn send_results(&mut self) {
        println!("Sending results to master, {:?}", self.master);
        let (block_size, blocks, pixels) = self.render_target.get_rendered_blocks();
        // TODO: A ctor that computes the encoded size for us would be cleaner and easier
        let mut frame = Frame { encoded_size: 0, frame: self.config.current_frame,
                                block_size: block_size, blocks: blocks, pixels: pixels };
        frame.encoded_size = encoded_size(&frame);
        let bytes = encode(&frame, SizeLimit::Infinite).unwrap();
        println!("worker sending {} bytes", bytes.len());
        match self.master.write_all(&bytes[..]) {
            Err(e) => println!("Failed to send frame to {:?}: {}", self.master, e),
            _ => {},
        }
    }
}

fn get_instructions() -> (Instructions, TcpStream) {
    let listener = TcpListener::bind(("0.0.0.0", PORT)).expect("Worker failed to get port");
    println!("Worker listening for master");
    match listener.accept() {
        Ok((mut stream, sock)) => {
            println!("Got connection");
            let mut buf: Vec<_> = iter::repeat(0u8).take(8).collect();
            let mut expected_size = 8;
            let mut currently_read = 0;
            // Read the size header
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            // How many bytes we expect to get from the worker for a frame
            expected_size = decode(&buf[..]).unwrap();
            println!("Read size header of {} bytes, now reading remainder", expected_size);
            buf.extend(iter::repeat(0u8).take(expected_size - 8));
            // Now read the rest
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            let instr: Instructions = decode(&buf[..]).unwrap();
            println!("Read from master {:?} instructions {:?}", sock, instr);
            (instr, stream)
        },
        Err(e) => panic!("Error accepting: {:?}", e),
    }
}

<commit_msg>Add encoded size computing ctor for Frame to clean up Worker<commit_after>//! The worker module provides the Worker struct which receives instructions from
//! the master, renders and reports back its results

use std::path::PathBuf;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::iter;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encoded_size, decode};

use scene::Scene;
use film::RenderTarget;
use exec::Config;
use exec::distrib::Instructions;

pub static PORT: u16 = 63234;

#[derive(RustcEncodable, RustcDecodable)]
pub struct Frame {
    pub encoded_size: u64,
    pub frame: usize,
    pub block_size: (usize, usize),
    pub blocks: Vec<(usize, usize)>,
    pub pixels: Vec<f32>,
}

impl Frame {
    pub fn new(frame: usize, block_size: (usize, usize), blocks: Vec<(usize, usize)>,
               pixels: Vec<f32>) -> Frame {
        let mut frame = Frame { encoded_size: 0, frame: frame, block_size: block_size,
                            blocks: blocks, pixels: pixels };
        frame.encoded_size = encoded_size(&frame);
        frame
    }
}

pub struct Worker {
    instructions: Instructions,
    pub render_target: RenderTarget,
    pub scene: Scene,
    pub config: Config,
    master: TcpStream,
}

impl Worker {
    /// Listen on the worker `PORT` for the master to contact us
    /// and send us instructions about the scene we should render and
    /// what parts of it we've been assigned
    pub fn listen_for_master(num_threads: u32) -> Worker {
        let (instructions, master) = get_instructions();
        let (scene, rt, spp, mut frame_info) = Scene::load_file(&instructions.scene);
        frame_info.start = instructions.frames.0;
        frame_info.end = instructions.frames.1;
        let config = Config::new(PathBuf::from("/tmp"), instructions.scene.clone(), spp,
                                 num_threads, frame_info,
                                 (instructions.block_start, instructions.block_count));
        Worker { instructions: instructions, render_target: rt, scene: scene,
                 config: config, master: master }
    }
    /// Send our blocks back to the master
    pub fn send_results(&mut self) {
        println!("Sending results to master, {:?}", self.master);
        let (block_size, blocks, pixels) = self.render_target.get_rendered_blocks();
        let frame = Frame::new(self.config.current_frame, block_size, blocks, pixels);
        let bytes = encode(&frame, SizeLimit::Infinite).unwrap();
        println!("worker sending {} bytes", bytes.len());
        match self.master.write_all(&bytes[..]) {
            Err(e) => println!("Failed to send frame to {:?}: {}", self.master, e),
            _ => {},
        }
    }
}

fn get_instructions() -> (Instructions, TcpStream) {
    let listener = TcpListener::bind(("0.0.0.0", PORT)).expect("Worker failed to get port");
    println!("Worker listening for master");
    match listener.accept() {
        Ok((mut stream, sock)) => {
            println!("Got connection");
            let mut buf: Vec<_> = iter::repeat(0u8).take(8).collect();
            let mut expected_size = 8;
            let mut currently_read = 0;
            // Read the size header
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            // How many bytes we expect to get from the worker for a frame
            expected_size = decode(&buf[..]).unwrap();
            println!("Read size header of {} bytes, now reading remainder", expected_size);
            buf.extend(iter::repeat(0u8).take(expected_size - 8));
            // Now read the rest
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            let instr: Instructions = decode(&buf[..]).unwrap();
            println!("Read from master {:?} instructions {:?}", sock, instr);
            (instr, stream)
        },
        Err(e) => panic!("Error accepting: {:?}", e),
    }
}

<|endoftext|>


<commit_before>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
use syn::{AttrStyle, Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,
    pub variants_from_header: Option<&'a mut Option<Attribute>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(lit) => {
                    if let Some(doc) = &mut parser.doc {
                        doc.push(lit);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("variants_from_header")
            && matches!(attr.style, AttrStyle::Inner(_))
            && cfg!(feature = "experimental")
        {
            if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
                cx.push(err);
            }
            if let Some(variants_from_header) = &mut parser.variants_from_header {
                **variants_from_header = Some(attr);
                continue;
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
    input.parse::<Token![=]>()?;
    let lit: LitStr = input.parse()?;
    Ok(lit)
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            let Attribute {
                pound_token,
                style,
                bracket_token,
                path,
                tokens: attr_tokens,
            } = attr;
            pound_token.to_tokens(tokens);
            let _ = style; // ignore; render outer and inner attrs both as outer
            bracket_token.surround(tokens, |tokens| {
                path.to_tokens(tokens);
                attr_tokens.to_tokens(tokens);
            });
        }
    }
}
<commit_msg>Ignore whether variants_from_header attr is outer or inner<commit_after>use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc, ForeignName};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::{Nothing, Parse, ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};

// Intended usage:
//
//     let mut doc = Doc::new();
//     let mut cxx_name = None;
//     let mut rust_name = None;
//     /* ... */
//     let attrs = attrs::parse(
//         cx,
//         item.attrs,
//         attrs::Parser {
//             doc: Some(&mut doc),
//             cxx_name: Some(&mut cxx_name),
//             rust_name: Some(&mut rust_name),
//             /* ... */
//             ..Default::default()
//         },
//     );
//
#[derive(Default)]
pub struct Parser<'a> {
    pub doc: Option<&'a mut Doc>,
    pub derives: Option<&'a mut Vec<Derive>>,
    pub repr: Option<&'a mut Option<Atom>>,
    pub namespace: Option<&'a mut Namespace>,
    pub cxx_name: Option<&'a mut Option<ForeignName>>,
    pub rust_name: Option<&'a mut Option<Ident>>,
    pub variants_from_header: Option<&'a mut Option<Attribute>>,

    // Suppress clippy needless_update lint ("struct update has no effect, all
    // the fields in the struct have already been specified") when preemptively
    // writing `..Default::default()`.
    pub(crate) _more: (),
}

pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser) -> OtherAttrs {
    let mut passthrough_attrs = Vec::new();
    for attr in attrs {
        if attr.path.is_ident("doc") {
            match parse_doc_attribute.parse2(attr.tokens.clone()) {
                Ok(lit) => {
                    if let Some(doc) = &mut parser.doc {
                        doc.push(lit);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("derive") {
            match attr.parse_args_with(|attr: ParseStream| parse_derive_attribute(cx, attr)) {
                Ok(attr) => {
                    if let Some(derives) = &mut parser.derives {
                        derives.extend(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("repr") {
            match attr.parse_args_with(parse_repr_attribute) {
                Ok(attr) => {
                    if let Some(repr) = &mut parser.repr {
                        **repr = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("namespace") {
            match parse_namespace_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(namespace) = &mut parser.namespace {
                        **namespace = attr;
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("cxx_name") {
            match parse_cxx_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(cxx_name) = &mut parser.cxx_name {
                        **cxx_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("rust_name") {
            match parse_rust_name_attribute.parse2(attr.tokens.clone()) {
                Ok(attr) => {
                    if let Some(rust_name) = &mut parser.rust_name {
                        **rust_name = Some(attr);
                        continue;
                    }
                }
                Err(err) => {
                    cx.push(err);
                    break;
                }
            }
        } else if attr.path.is_ident("variants_from_header") && cfg!(feature = "experimental") {
            if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) {
                cx.push(err);
            }
            if let Some(variants_from_header) = &mut parser.variants_from_header {
                **variants_from_header = Some(attr);
                continue;
            }
        } else if attr.path.is_ident("allow")
            || attr.path.is_ident("warn")
            || attr.path.is_ident("deny")
            || attr.path.is_ident("forbid")
            || attr.path.is_ident("deprecated")
            || attr.path.is_ident("must_use")
        {
            // https://doc.rust-lang.org/reference/attributes/diagnostics.html
            passthrough_attrs.push(attr);
            continue;
        } else if attr.path.segments.len() > 1 {
            let tool = &attr.path.segments.first().unwrap().ident;
            if tool == "rustfmt" {
                // Skip, rustfmt only needs to find it in the pre-expansion source file.
                continue;
            } else if tool == "clippy" {
                passthrough_attrs.push(attr);
                continue;
            }
        }
        cx.error(attr, "unsupported attribute");
        break;
    }
    OtherAttrs(passthrough_attrs)
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
    input.parse::<Token![=]>()?;
    let lit: LitStr = input.parse()?;
    Ok(lit)
}

fn parse_derive_attribute(cx: &mut Errors, input: ParseStream) -> Result<Vec<Derive>> {
    let paths = input.parse_terminated::<Path, Token![,]>(Path::parse_mod_style)?;

    let mut derives = Vec::new();
    for path in paths {
        if let Some(ident) = path.get_ident() {
            if let Some(derive) = Derive::from(ident) {
                derives.push(derive);
                continue;
            }
        }
        cx.error(path, "unsupported derive");
    }
    Ok(derives)
}

fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
    let begin = input.cursor();
    let ident: Ident = input.parse()?;
    if let Some(atom) = Atom::from(&ident) {
        match atom {
            U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize if input.is_empty() => {
                return Ok(atom);
            }
            _ => {}
        }
    }
    Err(Error::new_spanned(
        begin.token_stream(),
        "unrecognized repr",
    ))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
    input.parse::<Token![=]>()?;
    let namespace = input.parse::<Namespace>()?;
    Ok(namespace)
}

fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        ForeignName::parse(&lit.value(), lit.span())
    } else {
        let ident: Ident = input.parse()?;
        ForeignName::parse(&ident.to_string(), ident.span())
    }
}

fn parse_rust_name_attribute(input: ParseStream) -> Result<Ident> {
    input.parse::<Token![=]>()?;
    if input.peek(LitStr) {
        let lit: LitStr = input.parse()?;
        lit.parse()
    } else {
        input.parse()
    }
}

pub struct OtherAttrs(Vec<Attribute>);

impl OtherAttrs {
    pub fn none() -> Self {
        OtherAttrs(Vec::new())
    }
}

impl ToTokens for OtherAttrs {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for attr in &self.0 {
            let Attribute {
                pound_token,
                style,
                bracket_token,
                path,
                tokens: attr_tokens,
            } = attr;
            pound_token.to_tokens(tokens);
            let _ = style; // ignore; render outer and inner attrs both as outer
            bracket_token.surround(tokens, |tokens| {
                path.to_tokens(tokens);
                attr_tokens.to_tokens(tokens);
            });
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>renamed InstanceExtensions::supported_by_core()<commit_after><|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{
    error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;
use syn::{Error, Result};

struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Vec<Error>,
}

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let mut errors = Vec::new();
    let mut cx = Check {
        apis,
        types,
        errors: &mut errors,
    };
    do_typecheck(&mut cx);
    combine_errors(errors)
}

fn do_typecheck(cx: &mut Check) {
    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, ident),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            _ => {}
        }
    }

    for api in cx.apis {
        if let Api::CxxFunction(efn) = api {
            check_mut_return_restriction(cx, efn);
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            check_multiple_arg_lifetimes(cx, efn);
        }
    }

    ident::check_all(cx.apis, cx.errors);
}

impl Check<'_> {
    fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.push(Error::new_spanned(sp, msg));
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        cx.error(ident, "unsupported type");
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(ident).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        if receiver.lifetime.is_some() {
            let span = span_for_receiver_error(receiver);
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn combine_errors(errors: Vec<Error>) -> Result<()> {
    let mut iter = errors.into_iter();
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if cx.types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if cx.types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<commit_msg>Reject unrecognized idents in receiver's type<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{
    error, ident, Api, ExternFn, Lang, Receiver, Ref, Slice, Struct, Ty1, Type, Types,
};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::{quote, ToTokens};
use std::fmt::Display;
use syn::{Error, Result};

struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Vec<Error>,
}

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let mut errors = Vec::new();
    let mut cx = Check {
        apis,
        types,
        errors: &mut errors,
    };
    do_typecheck(&mut cx);
    combine_errors(errors)
}

fn do_typecheck(cx: &mut Check) {
    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, ident),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Slice(ty) => check_type_slice(cx, ty),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            _ => {}
        }
    }

    for api in cx.apis {
        if let Api::CxxFunction(efn) = api {
            check_mut_return_restriction(cx, efn);
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            check_multiple_arg_lifetimes(cx, efn);
        }
    }

    ident::check_all(cx.apis, cx.errors);
}

impl Check<'_> {
    fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
        self.errors.push(Error::new_spanned(sp, msg));
    }
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        cx.error(ident, "unsupported type");
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(ident) {
            cx.error(ptr, error::BOX_CXX_TYPE.msg);
        }

        if Atom::from(ident).is_none() {
            return;
        }
    }

    cx.error(ptr, "unsupported target type of Box");
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
        }

        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    }

    cx.error(ptr, "unsupported unique_ptr target type");
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if ty.lifetime.is_some() {
        cx.error(ty, "references with explicit lifetimes are not supported");
    }

    match ty.inner {
        Type::Fn(_) | Type::Void(_) => {}
        _ => return,
    }

    cx.error(ty, "unsupported reference type");
}

fn check_type_slice(cx: &mut Check, ty: &Slice) {
    cx.error(ty, "only &[u8] is supported so far, not other slice types");
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    if strct.fields.is_empty() {
        let span = span_for_struct_error(strct);
        cx.error(span, "structs without any fields are not supported");
    }

    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            let desc = describe(cx, &field.ty);
            let msg = format!("using {} by value is not supported", desc);
            cx.error(field, msg);
        }
        if let Type::Fn(_) = field.ty {
            cx.error(
                field,
                "function pointers in a struct field are not implemented yet",
            );
        }
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    if let Some(receiver) = &efn.receiver {
        if !cx.types.structs.contains_key(&receiver.ty)
            && !cx.types.cxx.contains(&receiver.ty)
            && !cx.types.rust.contains(&receiver.ty)
        {
            let span = span_for_receiver_error(receiver);
            cx.error(span, "unrecognized receiver type");
        }

        if receiver.lifetime.is_some() {
            let span = span_for_receiver_error(receiver);
            cx.error(span, "references with explicit lifetimes are not supported");
        }
    }

    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            let desc = describe(cx, &arg.ty);
            let msg = format!("passing {} by value is not supported", desc);
            cx.error(arg, msg);
        }
        if let Type::Fn(_) = arg.ty {
            if efn.lang == Lang::Rust {
                cx.error(
                    arg,
                    "passing a function pointer from C++ to Rust is not implemented yet",
                );
            }
        }
    }

    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            let desc = describe(cx, ty);
            let msg = format!("returning {} by value is not supported", desc);
            cx.error(ty, msg);
        }
        if let Type::Fn(_) = ty {
            cx.error(ty, "returning a function pointer is not implemented yet");
        }
    }
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.error(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    );
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if efn.receiver.is_some() {
        reference_args += 1;
    }

    if reference_args != 1 {
        cx.error(
            efn,
            "functions that return a reference must take exactly one input reference",
        );
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Slice(_) | Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    quote!(#struct_token #brace_token)
}

fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
    let ampersand = receiver.ampersand;
    let lifetime = &receiver.lifetime;
    let mutability = receiver.mutability;
    if receiver.shorthand {
        let var = receiver.var;
        quote!(#ampersand #lifetime #mutability #var)
    } else {
        let ty = &receiver.ty;
        quote!(#ampersand #lifetime #mutability #ty)
    }
}

fn combine_errors(errors: Vec<Error>) -> Result<()> {
    let mut iter = errors.into_iter();
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn describe(cx: &mut Check, ty: &Type) -> String {
    match ty {
        Type::Ident(ident) => {
            if cx.types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if cx.types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if cx.types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::Slice(_) => "slice".to_owned(),
        Type::SliceRefU8(_) => "&[u8]".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type};
use proc_macro2::Ident;
use quote::quote;
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
use syn::{Error, Result};

pub struct Types<'a> {
    pub all: Set<'a, Type>,
    pub structs: Map<Ident, &'a Struct>,
    pub enums: Map<Ident, &'a Enum>,
    pub cxx: Set<'a, Ident>,
    pub rust: Set<'a, Ident>,
}

impl<'a> Types<'a> {
    pub fn collect(apis: &'a [Api]) -> Result<Self> {
        let mut all = Set::new();
        let mut structs = Map::new();
        let mut enums = Map::new();
        let mut cxx = Set::new();
        let mut rust = Set::new();

        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
            all.insert(ty);
            match ty {
                Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
                Type::RustBox(ty)
                | Type::UniquePtr(ty)
                | Type::CxxVector(ty)
                | Type::RustVec(ty) => visit(all, &ty.inner),
                Type::Ref(r) => visit(all, &r.inner),
                Type::Slice(s) => visit(all, &s.inner),
                Type::Fn(f) => {
                    if let Some(ret) = &f.ret {
                        visit(all, ret);
                    }
                    for arg in &f.args {
                        visit(all, &arg.ty);
                    }
                }
            }
        }

        let mut type_names = UnorderedSet::new();
        for api in apis {
            match api {
                Api::Include(_) => {}
                Api::Struct(strct) => {
                    if !type_names.insert(&strct.ident) {
                        return Err(duplicate_struct(strct));
                    }
                    structs.insert(strct.ident.clone(), strct);
                    for field in &strct.fields {
                        visit(&mut all, &field.ty);
                    }
                }
                Api::Enum(enm) => {
                    if !type_names.insert(&enm.ident) {
                        return Err(duplicate_enum(enm));
                    }
                    enums.insert(enm.ident.clone(), enm);
                }
                Api::CxxType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    cxx.insert(&ety.ident);
                }
                Api::RustType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    rust.insert(&ety.ident);
                }
                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                    for arg in &efn.args {
                        visit(&mut all, &arg.ty);
                    }
                    if let Some(ret) = &efn.ret {
                        visit(&mut all, ret);
                    }
                }
            }
        }

        Ok(Types {
            all,
            structs,
            enums,
            cxx,
            rust,
        })
    }

    pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
        match ty {
            Type::Ident(ident) => {
                if let Some(strct) = self.structs.get(ident) {
                    !self.is_pod(strct)
                } else {
                    Atom::from(ident) == Some(RustString)
                }
            }
            Type::RustVec(_) => true,
            _ => false,
        }
    }

    pub fn is_pod(&self, strct: &Struct) -> bool {
        for derive in &strct.derives {
            if *derive == Derive::Copy {
                return true;
            }
        }
        false
    }
}

impl<'t, 'a> IntoIterator for &'t Types<'a> {
    type Item = &'a Type;
    type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
    fn into_iter(self) -> Self::IntoIter {
        self.all.into_iter()
    }
}

fn duplicate_struct(strct: &Struct) -> Error {
    let struct_token = strct.struct_token;
    let ident = &strct.ident;
    let range = quote!(#struct_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_enum(enm: &Enum) -> Error {
    let enum_token = enm.enum_token;
    let ident = &enm.ident;
    let range = quote!(#enum_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_type(ety: &ExternType) -> Error {
    let type_token = ety.type_token;
    let ident = &ety.ident;
    let range = quote!(#type_token #ident);
    Error::new_spanned(range, "duplicate type")
}
<commit_msg>Catch function name collisions<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Struct, Type};
use proc_macro2::Ident;
use quote::quote;
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
use syn::{Error, Result};

pub struct Types<'a> {
    pub all: Set<'a, Type>,
    pub structs: Map<Ident, &'a Struct>,
    pub enums: Map<Ident, &'a Enum>,
    pub cxx: Set<'a, Ident>,
    pub rust: Set<'a, Ident>,
}

impl<'a> Types<'a> {
    pub fn collect(apis: &'a [Api]) -> Result<Self> {
        let mut all = Set::new();
        let mut structs = Map::new();
        let mut enums = Map::new();
        let mut cxx = Set::new();
        let mut rust = Set::new();

        fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) {
            all.insert(ty);
            match ty {
                Type::Ident(_) | Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
                Type::RustBox(ty)
                | Type::UniquePtr(ty)
                | Type::CxxVector(ty)
                | Type::RustVec(ty) => visit(all, &ty.inner),
                Type::Ref(r) => visit(all, &r.inner),
                Type::Slice(s) => visit(all, &s.inner),
                Type::Fn(f) => {
                    if let Some(ret) = &f.ret {
                        visit(all, ret);
                    }
                    for arg in &f.args {
                        visit(all, &arg.ty);
                    }
                }
            }
        }

        let mut type_names = UnorderedSet::new();
        let mut function_names = UnorderedSet::new();
        for api in apis {
            match api {
                Api::Include(_) => {}
                Api::Struct(strct) => {
                    if !type_names.insert(&strct.ident) {
                        return Err(duplicate_struct(strct));
                    }
                    structs.insert(strct.ident.clone(), strct);
                    for field in &strct.fields {
                        visit(&mut all, &field.ty);
                    }
                }
                Api::Enum(enm) => {
                    if !type_names.insert(&enm.ident) {
                        return Err(duplicate_enum(enm));
                    }
                    enums.insert(enm.ident.clone(), enm);
                }
                Api::CxxType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    cxx.insert(&ety.ident);
                }
                Api::RustType(ety) => {
                    if !type_names.insert(&ety.ident) {
                        return Err(duplicate_type(ety));
                    }
                    rust.insert(&ety.ident);
                }
                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
                    if !function_names.insert((&efn.receiver, &efn.ident)) {
                        return Err(duplicate_function(efn));
                    }
                    for arg in &efn.args {
                        visit(&mut all, &arg.ty);
                    }
                    if let Some(ret) = &efn.ret {
                        visit(&mut all, ret);
                    }
                }
            }
        }

        Ok(Types {
            all,
            structs,
            enums,
            cxx,
            rust,
        })
    }

    pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
        match ty {
            Type::Ident(ident) => {
                if let Some(strct) = self.structs.get(ident) {
                    !self.is_pod(strct)
                } else {
                    Atom::from(ident) == Some(RustString)
                }
            }
            Type::RustVec(_) => true,
            _ => false,
        }
    }

    pub fn is_pod(&self, strct: &Struct) -> bool {
        for derive in &strct.derives {
            if *derive == Derive::Copy {
                return true;
            }
        }
        false
    }
}

impl<'t, 'a> IntoIterator for &'t Types<'a> {
    type Item = &'a Type;
    type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
    fn into_iter(self) -> Self::IntoIter {
        self.all.into_iter()
    }
}

fn duplicate_struct(strct: &Struct) -> Error {
    let struct_token = strct.struct_token;
    let ident = &strct.ident;
    let range = quote!(#struct_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_enum(enm: &Enum) -> Error {
    let enum_token = enm.enum_token;
    let ident = &enm.ident;
    let range = quote!(#enum_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_type(ety: &ExternType) -> Error {
    let type_token = ety.type_token;
    let ident = &ety.ident;
    let range = quote!(#type_token #ident);
    Error::new_spanned(range, "duplicate type")
}

fn duplicate_function(efn: &ExternFn) -> Error {
    Error::new_spanned(efn, "duplicate function")
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

use core::prelude::*;

use driver::session;
use metadata::cstore;
use metadata::filesearch;

use core::hashmap::HashSet;
use core::os;
use core::uint;
use core::util;
use core::vec;

fn not_win32(os: session::os) -> bool {
  match os {
      session::os_win32 => false,
      _ => true
  }
}

pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
                    -> ~[~str] {
    let os = sess.targ_cfg.os;

    // No rpath on windows
    if os == session::os_win32 {
        return ~[];
    }

    debug!("preparing the RPATH!");

    let sysroot = sess.filesearch.sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.cstore);
    // We don't currently rpath extern libraries, but we know
    // where rustrt is and we know every rust program needs it
    let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess));

    let rpaths = get_rpaths(os, sysroot, output, libs,
                            sess.opts.target_triple);
    rpaths_to_flags(rpaths)
}

fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
    let r = filesearch::relative_target_lib_path(sess.opts.target_triple);
    sess.filesearch.sysroot().push_rel(&r).push(os::dll_filename("rustrt"))
}

pub fn rpaths_to_flags(rpaths: &[Path]) -> ~[~str] {
    vec::map(rpaths, |rpath| fmt!("-Wl,-rpath,%s",rpath.to_str()))
}

fn get_rpaths(os: session::os,
              sysroot: &Path,
              output: &Path,
              libs: &[Path],
              target_triple: &str) -> ~[Path] {
    debug!("sysroot: %s", sysroot.to_str());
    debug!("output: %s", output.to_str());
    debug!("libs:");
    for libs.each |libpath| {
        debug!("    %s", libpath.to_str());
    }
    debug!("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(os, output, libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: &str, rpaths: &[Path]) {
        debug!("%s rpaths:", desc);
        for rpaths.each |rpath| {
            debug!("    %s", rpath.to_str());
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.push_all(fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    return rpaths;
}

fn get_rpaths_relative_to_output(os: session::os,
                                 output: &Path,
                                 libs: &[Path]) -> ~[Path] {
    vec::map(libs, |a| {
        get_rpath_relative_to_output(os, output, a)
    })
}

pub fn get_rpath_relative_to_output(os: session::os,
                                    output: &Path,
                                    lib: &Path)
                                 -> Path {
    use core::os;

    assert!(not_win32(os));

    // Mac doesn't appear to support $ORIGIN
    let prefix = match os {
        session::os_android |session::os_linux | session::os_freebsd
                          => "$ORIGIN",
        session::os_macos => "@executable_path",
        session::os_win32 => util::unreachable()
    };

    Path(prefix).push_rel(&get_relative_to(&os::make_absolute(output),
                                           &os::make_absolute(lib)))
}

// Find the relative path from one file to another
pub fn get_relative_to(abs1: &Path, abs2: &Path) -> Path {
    assert!(abs1.is_absolute);
    assert!(abs2.is_absolute);
    let abs1 = abs1.normalize();
    let abs2 = abs2.normalize();
    debug!("finding relative path from %s to %s",
           abs1.to_str(), abs2.to_str());
    let split1: &[~str] = abs1.components;
    let split2: &[~str] = abs2.components;
    let len1 = split1.len();
    let len2 = split2.len();
    assert!(len1 > 0);
    assert!(len2 > 0);

    let max_common_path = uint::min(len1, len2) - 1;
    let mut start_idx = 0;
    while start_idx < max_common_path
        && split1[start_idx] == split2[start_idx] {
        start_idx += 1;
    }

    let mut path = ~[];
    for uint::range(start_idx, len1 - 1) |_i| { path.push(~".."); };

    path.push_all(vec::slice(split2, start_idx, len2 - 1));

    if !path.is_empty() {
        return Path("").push_many(path);
    } else {
        return Path(".");
    }
}

#[cfg(stage0)]
pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
    let install_prefix = env!("CFG_PREFIX");

    if install_prefix.is_empty() {
        fail!("rustc compiled without CFG_PREFIX environment variable");
    }

    let tlib = filesearch::relative_target_lib_path(target_triple);
    os::make_absolute(&Path(install_prefix).push_rel(&tlib))
}

#[cfg(not(stage0))]
pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
    let install_prefix = env!("CFG_PREFIX");

    if install_prefix == "" {
        fail!("rustc compiled without CFG_PREFIX environment variable");
    }

    let tlib = filesearch::relative_target_lib_path(target_triple);
    os::make_absolute(&Path(install_prefix).push_rel(&tlib))
}

pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] {
    let mut set = HashSet::new();
    let mut minimized = ~[];
    for rpaths.each |rpath| {
        if set.insert(rpath.to_str()) {
            minimized.push(copy *rpath);
        }
    }
    minimized
}

#[cfg(unix, test)]
mod test {
    use core::prelude::*;

    // FIXME(#2119): the outer attribute should be #[cfg(unix, test)], then
    // these redundant #[cfg(test)] blocks can be removed
    #[cfg(test)]
    #[cfg(test)]
    use back::rpath::{get_install_prefix_rpath};
    use back::rpath::{get_relative_to, get_rpath_relative_to_output};
    use back::rpath::{minimize_rpaths, rpaths_to_flags};
    use driver::session;

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags([Path("path1"),
                                     Path("path2")]);
        assert_eq!(flags, ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        let d = Path(env!("CFG_PREFIX"))
            .push_rel(&Path("lib/rustc/triple/lib"));
        debug!("test_prefix_path: %s vs. %s",
               res.to_str(),
               d.to_str());
        assert!(res.to_str().ends_with(d.to_str()));
    }

    #[test]
    fn test_prefix_rpath_abs() {
        let res = get_install_prefix_rpath("triple");
        assert!(res.is_absolute);
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths([Path("rpath1"),
                                   Path("rpath2"),
                                   Path("rpath1")]);
        assert_eq!(res, ~[Path("rpath1"), Path("rpath2")]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths([Path("1a"), Path("2"), Path("2"),
                                   Path("1a"), Path("4a"),Path("1a"),
                                   Path("2"), Path("3"), Path("4a"),
                                   Path("3")]);
        assert_eq!(res, ~[Path("1a"), Path("2"), Path("4a"), Path("3")]);
    }

    #[test]
    fn test_relative_to1() {
        let p1 = Path("/usr/bin/rustc");
        let p2 = Path("/usr/lib/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to2() {
        let p1 = Path("/usr/bin/rustc");
        let p2 = Path("/usr/bin/../lib/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to3() {
        let p1 = Path("/usr/bin/whatever/rustc");
        let p2 = Path("/usr/lib/whatever/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../../lib/whatever"));
    }

    #[test]
    fn test_relative_to4() {
        let p1 = Path("/usr/bin/whatever/../rustc");
        let p2 = Path("/usr/lib/whatever/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib/whatever"));
    }

    #[test]
    fn test_relative_to5() {
        let p1 = Path("/usr/bin/whatever/../rustc");
        let p2 = Path("/usr/lib/whatever/../mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to6() {
        let p1 = Path("/1");
        let p2 = Path("/2/3");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("2"));
    }

    #[test]
    fn test_relative_to7() {
        let p1 = Path("/1/2");
        let p2 = Path("/3");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path(".."));
    }

    #[test]
    fn test_relative_to8() {
        let p1 = Path("/home/brian/Dev/rust/build/").push_rel(
            &Path("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
        let p2 = Path("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
            &Path("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
        let res = get_relative_to(&p1, &p2);
        debug!("test_relative_tu8: %s vs. %s",
               res.to_str(),
               Path(".").to_str());
        assert_eq!(res, Path("."));
    }

    #[test]
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "andorid")]
    fn test_rpath_relative() {
      let o = session::os_linux;
      let res = get_rpath_relative_to_output(o,
            &Path("bin/rustc"), &Path("lib/libstd.so"));
      assert_eq!(res.to_str(), ~"$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "freebsd")]
    fn test_rpath_relative() {
        let o = session::os_freebsd;
        let res = get_rpath_relative_to_output(o,
            &Path("bin/rustc"), &Path("lib/libstd.so"));
        assert_eq!(res.to_str(), ~"$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn test_rpath_relative() {
        // this is why refinements would be nice
        let o = session::os_macos;
        let res = get_rpath_relative_to_output(o,
                                               &Path("bin/rustc"),
                                               &Path("lib/libstd.so"));
        assert_eq!(res.to_str(), ~"@executable_path/../lib");
    }
}
<commit_msg>auto merge of #7192 : brson/rust/rpath, r=thestinger<commit_after>// Copyright 2012 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.

use core::prelude::*;

use driver::session;
use metadata::cstore;
use metadata::filesearch;

use core::hashmap::HashSet;
use core::os;
use core::uint;
use core::util;
use core::vec;

fn not_win32(os: session::os) -> bool {
  match os {
      session::os_win32 => false,
      _ => true
  }
}

pub fn get_rpath_flags(sess: session::Session, out_filename: &Path)
                    -> ~[~str] {
    let os = sess.targ_cfg.os;

    // No rpath on windows
    if os == session::os_win32 {
        return ~[];
    }

    debug!("preparing the RPATH!");

    let sysroot = sess.filesearch.sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.cstore);
    // We don't currently rpath extern libraries, but we know
    // where rustrt is and we know every rust program needs it
    let libs = vec::append_one(libs, get_sysroot_absolute_rt_lib(sess));

    let rpaths = get_rpaths(os, sysroot, output, libs,
                            sess.opts.target_triple);
    rpaths_to_flags(rpaths)
}

fn get_sysroot_absolute_rt_lib(sess: session::Session) -> Path {
    let r = filesearch::relative_target_lib_path(sess.opts.target_triple);
    sess.filesearch.sysroot().push_rel(&r).push(os::dll_filename("rustrt"))
}

pub fn rpaths_to_flags(rpaths: &[Path]) -> ~[~str] {
    vec::map(rpaths, |rpath| fmt!("-Wl,-rpath,%s",rpath.to_str()))
}

fn get_rpaths(os: session::os,
              sysroot: &Path,
              output: &Path,
              libs: &[Path],
              target_triple: &str) -> ~[Path] {
    debug!("sysroot: %s", sysroot.to_str());
    debug!("output: %s", output.to_str());
    debug!("libs:");
    for libs.each |libpath| {
        debug!("    %s", libpath.to_str());
    }
    debug!("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(os, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    let abs_rpaths = get_absolute_rpaths(libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = ~[get_install_prefix_rpath(target_triple)];

    fn log_rpaths(desc: &str, rpaths: &[Path]) {
        debug!("%s rpaths:", desc);
        for rpaths.each |rpath| {
            debug!("    %s", rpath.to_str());
        }
    }

    log_rpaths("relative", rel_rpaths);
    log_rpaths("absolute", abs_rpaths);
    log_rpaths("fallback", fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.push_all(abs_rpaths);
    rpaths.push_all(fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    return rpaths;
}

fn get_rpaths_relative_to_output(os: session::os,
                                 output: &Path,
                                 libs: &[Path]) -> ~[Path] {
    vec::map(libs, |a| {
        get_rpath_relative_to_output(os, output, a)
    })
}

pub fn get_rpath_relative_to_output(os: session::os,
                                    output: &Path,
                                    lib: &Path)
                                 -> Path {
    use core::os;

    assert!(not_win32(os));

    // Mac doesn't appear to support $ORIGIN
    let prefix = match os {
        session::os_android |session::os_linux | session::os_freebsd
                          => "$ORIGIN",
        session::os_macos => "@executable_path",
        session::os_win32 => util::unreachable()
    };

    Path(prefix).push_rel(&get_relative_to(&os::make_absolute(output),
                                           &os::make_absolute(lib)))
}

// Find the relative path from one file to another
pub fn get_relative_to(abs1: &Path, abs2: &Path) -> Path {
    assert!(abs1.is_absolute);
    assert!(abs2.is_absolute);
    let abs1 = abs1.normalize();
    let abs2 = abs2.normalize();
    debug!("finding relative path from %s to %s",
           abs1.to_str(), abs2.to_str());
    let split1: &[~str] = abs1.components;
    let split2: &[~str] = abs2.components;
    let len1 = split1.len();
    let len2 = split2.len();
    assert!(len1 > 0);
    assert!(len2 > 0);

    let max_common_path = uint::min(len1, len2) - 1;
    let mut start_idx = 0;
    while start_idx < max_common_path
        && split1[start_idx] == split2[start_idx] {
        start_idx += 1;
    }

    let mut path = ~[];
    for uint::range(start_idx, len1 - 1) |_i| { path.push(~".."); };

    path.push_all(vec::slice(split2, start_idx, len2 - 1));

    if !path.is_empty() {
        return Path("").push_many(path);
    } else {
        return Path(".");
    }
}

fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] {
    vec::map(libs, |a| get_absolute_rpath(a) )
}

pub fn get_absolute_rpath(lib: &Path) -> Path {
    os::make_absolute(lib).dir_path()
}

#[cfg(stage0)]
pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
    let install_prefix = env!("CFG_PREFIX");

    if install_prefix.is_empty() {
        fail!("rustc compiled without CFG_PREFIX environment variable");
    }

    let tlib = filesearch::relative_target_lib_path(target_triple);
    os::make_absolute(&Path(install_prefix).push_rel(&tlib))
}

#[cfg(not(stage0))]
pub fn get_install_prefix_rpath(target_triple: &str) -> Path {
    let install_prefix = env!("CFG_PREFIX");

    if install_prefix == "" {
        fail!("rustc compiled without CFG_PREFIX environment variable");
    }

    let tlib = filesearch::relative_target_lib_path(target_triple);
    os::make_absolute(&Path(install_prefix).push_rel(&tlib))
}

pub fn minimize_rpaths(rpaths: &[Path]) -> ~[Path] {
    let mut set = HashSet::new();
    let mut minimized = ~[];
    for rpaths.each |rpath| {
        if set.insert(rpath.to_str()) {
            minimized.push(copy *rpath);
        }
    }
    minimized
}

#[cfg(unix, test)]
mod test {
    use core::prelude::*;
    use core::os;

    // FIXME(#2119): the outer attribute should be #[cfg(unix, test)], then
    // these redundant #[cfg(test)] blocks can be removed
    #[cfg(test)]
    #[cfg(test)]
    use back::rpath::{get_absolute_rpath, get_install_prefix_rpath};
    use back::rpath::{get_relative_to, get_rpath_relative_to_output};
    use back::rpath::{minimize_rpaths, rpaths_to_flags};
    use driver::session;

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags([Path("path1"),
                                     Path("path2")]);
        assert_eq!(flags, ~[~"-Wl,-rpath,path1", ~"-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        let d = Path(env!("CFG_PREFIX"))
            .push_rel(&Path("lib/rustc/triple/lib"));
        debug!("test_prefix_path: %s vs. %s",
               res.to_str(),
               d.to_str());
        assert!(res.to_str().ends_with(d.to_str()));
    }

    #[test]
    fn test_prefix_rpath_abs() {
        let res = get_install_prefix_rpath("triple");
        assert!(res.is_absolute);
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths([Path("rpath1"),
                                   Path("rpath2"),
                                   Path("rpath1")]);
        assert_eq!(res, ~[Path("rpath1"), Path("rpath2")]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths([Path("1a"), Path("2"), Path("2"),
                                   Path("1a"), Path("4a"),Path("1a"),
                                   Path("2"), Path("3"), Path("4a"),
                                   Path("3")]);
        assert_eq!(res, ~[Path("1a"), Path("2"), Path("4a"), Path("3")]);
    }

    #[test]
    fn test_relative_to1() {
        let p1 = Path("/usr/bin/rustc");
        let p2 = Path("/usr/lib/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to2() {
        let p1 = Path("/usr/bin/rustc");
        let p2 = Path("/usr/bin/../lib/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to3() {
        let p1 = Path("/usr/bin/whatever/rustc");
        let p2 = Path("/usr/lib/whatever/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../../lib/whatever"));
    }

    #[test]
    fn test_relative_to4() {
        let p1 = Path("/usr/bin/whatever/../rustc");
        let p2 = Path("/usr/lib/whatever/mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib/whatever"));
    }

    #[test]
    fn test_relative_to5() {
        let p1 = Path("/usr/bin/whatever/../rustc");
        let p2 = Path("/usr/lib/whatever/../mylib");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("../lib"));
    }

    #[test]
    fn test_relative_to6() {
        let p1 = Path("/1");
        let p2 = Path("/2/3");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path("2"));
    }

    #[test]
    fn test_relative_to7() {
        let p1 = Path("/1/2");
        let p2 = Path("/3");
        let res = get_relative_to(&p1, &p2);
        assert_eq!(res, Path(".."));
    }

    #[test]
    fn test_relative_to8() {
        let p1 = Path("/home/brian/Dev/rust/build/").push_rel(
            &Path("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"));
        let p2 = Path("/home/brian/Dev/rust/build/stage2/bin/..").push_rel(
            &Path("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"));
        let res = get_relative_to(&p1, &p2);
        debug!("test_relative_tu8: %s vs. %s",
               res.to_str(),
               Path(".").to_str());
        assert_eq!(res, Path("."));
    }

    #[test]
    #[cfg(target_os = "linux")]
    #[cfg(target_os = "andorid")]
    fn test_rpath_relative() {
      let o = session::os_linux;
      let res = get_rpath_relative_to_output(o,
            &Path("bin/rustc"), &Path("lib/libstd.so"));
      assert_eq!(res.to_str(), ~"$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "freebsd")]
    fn test_rpath_relative() {
        let o = session::os_freebsd;
        let res = get_rpath_relative_to_output(o,
            &Path("bin/rustc"), &Path("lib/libstd.so"));
        assert_eq!(res.to_str(), ~"$ORIGIN/../lib");
    }

    #[test]
    #[cfg(target_os = "macos")]
    fn test_rpath_relative() {
        // this is why refinements would be nice
        let o = session::os_macos;
        let res = get_rpath_relative_to_output(o,
                                               &Path("bin/rustc"),
                                               &Path("lib/libstd.so"));
        assert_eq!(res.to_str(), ~"@executable_path/../lib");
    }

    #[test]
    fn test_get_absolute_rpath() {
        let res = get_absolute_rpath(&Path("lib/libstd.so"));
        debug!("test_get_absolute_rpath: %s vs. %s",
               res.to_str(),
               os::make_absolute(&Path("lib")).to_str());

        assert_eq!(res, os::make_absolute(&Path("lib")));
    }
}
<|endoftext|>


<commit_before>// -*- rust -*-

use std;
import std.deque;

fn test_simple() {
  let deque.t[int] d = deque.create[int]();
  check (d.size() == 0u);
  d.add_front(17);
  d.add_front(42);
  d.add_back(137);
  check (d.size() == 3u);
  d.add_back(137);
  check (d.size() == 4u);

  log d.peek_front();
  check (d.peek_front() == 42);

  log d.peek_back();
  check (d.peek_back() == 137);

  let int i = d.pop_front();
  log i;
  check (i == 42);

  i = d.pop_back();
  log i;
  check (i == 137);

  i = d.pop_back();
  log i;
  check (i == 137);

  i = d.pop_back();
  log i;
  check (i == 17);

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

fn test_boxes(@int a, @int b, @int c, @int d) {
  let deque.t[@int] deq = deque.create[@int]();
  check (deq.size() == 0u);
  deq.add_front(a);
  deq.add_front(b);
  deq.add_back(c);
  check (deq.size() == 3u);
  deq.add_back(d);
  check (deq.size() == 4u);

  check (deq.peek_front() == b);
  check (deq.peek_back() == d);

  check (deq.pop_front() == b);
  check (deq.pop_back() == d);
  check (deq.pop_back() == c);
  check (deq.pop_back() == a);

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

type eqfn[T] = fn(&T a, &T b) -> bool;

fn test_parameterized[T](eqfn[T] e, &T a, &T b, &T c, &T d) {
  let deque.t[T] deq = deque.create[T]();
  check (deq.size() == 0u);
  deq.add_front(a);
  deq.add_front(b);
  deq.add_back(c);
  check (deq.size() == 3u);
  deq.add_back(d);
  check (deq.size() == 4u);

  check (e(deq.peek_front(), b));
  check (e(deq.peek_back(), d));

  check (e(deq.pop_front(), b));
  check (e(deq.pop_back(), d));
  check (e(deq.pop_back(), c));
  check (e(deq.pop_back(), a));

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

type taggy = tag(one(int), two(int, int), three(int, int, int));

type taggypar[T] = tag(onepar(int),
                       twopar(int, int),
                       threepar(int, int, int));

fn main() {
  fn inteq(&int a, &int b) -> bool {
    ret a == b;
  }

  fn intboxeq(&@int a, &@int b) -> bool {
    ret a == b;
  }

  fn taggyeq(&taggy a, &taggy b) -> bool {
    alt (a) {
      case (one(a1)) {
        alt (b) {
          case (one(b1)) { ret a1 == b1; }
          case (_) { ret false; }
        }
      }
      case (two(a1, a2)) {
        alt (b) {
          case (two(b1, b2)) { ret (a1 == b1 && a2 == b2); }
          case (_) { ret false; }
        }
      }
      case (three(a1, a2, a3)) {
        alt (b) {
          case (three(b1, b2, b3)) { ret (a1 == b1 && a2 == b2 && a3 == b3); }
          case (_) { ret false; }
        }
      }
    }
  }

  fn taggypareq[T](&taggypar[T] a, &taggypar[T] b) -> bool {
    alt (a) {
      case (onepar[T](a1)) {
        alt (b) {
          case (onepar[T](b1)) { ret a1 == b1; }
          case (_) { ret false; }
        }
      }
      case (twopar[T](a1, a2)) {
        alt (b) {
          case (twopar[T](b1, b2)) { ret (a1 == b1 && a2 == b2); }
          case (_) { ret false; }
        }
      }
      case (threepar[T](a1, a2, a3)) {
        alt (b) {
          case (threepar[T](b1, b2, b3)) {
            ret (a1 == b1 && a2 == b2 && a3 == b3);
          }
          case (_) { ret false; }
        }
      }
    }
  }

  log "test simple";
  test_simple();

  /*
   * FIXME: Causes "Invalid read of size 4" under valgrind.

  log "test boxes";
  test_boxes(@5, @72, @64, @175);

   */

  log "test parameterized: int";
  let eqfn[int] eq1 = bind inteq(_, _);
  test_parameterized[int](eq1, 5, 72, 64, 175);

  /*
   * FIXME: Appears to segfault after an upcall_grow_task

  log "test parameterized: @int";
  let eqfn[@int] eq2 = bind intboxeq(_, _);
  test_parameterized[@int](eq2, @5, @72, @64, @175);

   */

  log "test parameterized: taggy";
  let eqfn[taggy] eq3 = bind taggyeq(_, _);
  test_parameterized[taggy](eq3,
                            one(1), two(1, 2), three(1, 2, 3), two(17, 42));

  /*
   * FIXME: Segfault.

  log "test parameterized: taggypar[int]";
  let eqfn[taggypar[int]] eq4 = bind taggypareq[int](_, _);
  test_parameterized[taggypar[int]](eq4,
                                    onepar[int](1),
                                    twopar[int](1, 2),
                                    threepar[int](1, 2, 3),
                                    twopar[int](17, 42));

   */

  log "done";
}
<commit_msg>Another deque test commented out.<commit_after>// -*- rust -*-

use std;
import std.deque;

fn test_simple() {
  let deque.t[int] d = deque.create[int]();
  check (d.size() == 0u);
  d.add_front(17);
  d.add_front(42);
  d.add_back(137);
  check (d.size() == 3u);
  d.add_back(137);
  check (d.size() == 4u);

  log d.peek_front();
  check (d.peek_front() == 42);

  log d.peek_back();
  check (d.peek_back() == 137);

  let int i = d.pop_front();
  log i;
  check (i == 42);

  i = d.pop_back();
  log i;
  check (i == 137);

  i = d.pop_back();
  log i;
  check (i == 137);

  i = d.pop_back();
  log i;
  check (i == 17);

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

fn test_boxes(@int a, @int b, @int c, @int d) {
  let deque.t[@int] deq = deque.create[@int]();
  check (deq.size() == 0u);
  deq.add_front(a);
  deq.add_front(b);
  deq.add_back(c);
  check (deq.size() == 3u);
  deq.add_back(d);
  check (deq.size() == 4u);

  check (deq.peek_front() == b);
  check (deq.peek_back() == d);

  check (deq.pop_front() == b);
  check (deq.pop_back() == d);
  check (deq.pop_back() == c);
  check (deq.pop_back() == a);

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

type eqfn[T] = fn(&T a, &T b) -> bool;

fn test_parameterized[T](eqfn[T] e, &T a, &T b, &T c, &T d) {
  let deque.t[T] deq = deque.create[T]();
  check (deq.size() == 0u);
  deq.add_front(a);
  deq.add_front(b);
  deq.add_back(c);
  check (deq.size() == 3u);
  deq.add_back(d);
  check (deq.size() == 4u);

  check (e(deq.peek_front(), b));
  check (e(deq.peek_back(), d));

  check (e(deq.pop_front(), b));
  check (e(deq.pop_back(), d));
  check (e(deq.pop_back(), c));
  check (e(deq.pop_back(), a));

  /* FIXME (issue #138):  Test d.get() once it no longer causes
   * segfault. */
}

type taggy = tag(one(int), two(int, int), three(int, int, int));

type taggypar[T] = tag(onepar(int),
                       twopar(int, int),
                       threepar(int, int, int));

type reccy = rec(int x, int y, taggy t);

fn main() {
  fn inteq(&int a, &int b) -> bool {
    ret a == b;
  }

  fn intboxeq(&@int a, &@int b) -> bool {
    ret a == b;
  }

  fn taggyeq(&taggy a, &taggy b) -> bool {
    alt (a) {
      case (one(a1)) {
        alt (b) {
          case (one(b1)) { ret a1 == b1; }
          case (_) { ret false; }
        }
      }
      case (two(a1, a2)) {
        alt (b) {
          case (two(b1, b2)) { ret (a1 == b1 && a2 == b2); }
          case (_) { ret false; }
        }
      }
      case (three(a1, a2, a3)) {
        alt (b) {
          case (three(b1, b2, b3)) { ret (a1 == b1 && a2 == b2 && a3 == b3); }
          case (_) { ret false; }
        }
      }
    }
  }

  fn taggypareq[T](&taggypar[T] a, &taggypar[T] b) -> bool {
    alt (a) {
      case (onepar[T](a1)) {
        alt (b) {
          case (onepar[T](b1)) { ret a1 == b1; }
          case (_) { ret false; }
        }
      }
      case (twopar[T](a1, a2)) {
        alt (b) {
          case (twopar[T](b1, b2)) { ret (a1 == b1 && a2 == b2); }
          case (_) { ret false; }
        }
      }
      case (threepar[T](a1, a2, a3)) {
        alt (b) {
          case (threepar[T](b1, b2, b3)) {
            ret (a1 == b1 && a2 == b2 && a3 == b3);
          }
          case (_) { ret false; }
        }
      }
    }
  }

  fn reccyeq(&reccy a, &reccy b) -> bool {
    ret (a.x == b.x && a.y == b.y && taggyeq(a.t, b.t));
  }

  log "test simple";
  test_simple();

  /*
   * FIXME: Causes "Invalid read of size 4" under valgrind.

  log "test boxes";
  test_boxes(@5, @72, @64, @175);

   */

  log "test parameterized: int";
  let eqfn[int] eq1 = bind inteq(_, _);
  test_parameterized[int](eq1, 5, 72, 64, 175);

  /*
   * FIXME: Appears to segfault after an upcall_grow_task

  log "test parameterized: @int";
  let eqfn[@int] eq2 = bind intboxeq(_, _);
  test_parameterized[@int](eq2, @5, @72, @64, @175);

   */

  log "test parameterized: taggy";
  let eqfn[taggy] eq3 = bind taggyeq(_, _);
  test_parameterized[taggy](eq3,
                            one(1), two(1, 2), three(1, 2, 3), two(17, 42));

  /*
   * FIXME: Segfault.

  log "test parameterized: taggypar[int]";
  let eqfn[taggypar[int]] eq4 = bind taggypareq[int](_, _);
  test_parameterized[taggypar[int]](eq4,
                                    onepar[int](1),
                                    twopar[int](1, 2),
                                    threepar[int](1, 2, 3),
                                    twopar[int](17, 42));

   */

  /*
   * FIXME: Segfault.

  log "test parameterized: reccy";
  let reccy reccy1 = rec(x=1, y=2, t=one(1));
  let reccy reccy2 = rec(x=345, y=2, t=two(1, 2));
  let reccy reccy3 = rec(x=1, y=777, t=three(1, 2, 3));
  let reccy reccy4 = rec(x=19, y=252, t=two(17, 42));
  let eqfn[reccy] eq5 = bind reccyeq(_, _);
  test_parameterized[reccy](eq5,
                            reccy1, reccy2, reccy3, reccy4);

   */

  log "done";
}
<|endoftext|>


<commit_before>extern crate centralgithook;
extern crate tempdir;
#[macro_use]
extern crate log;
extern crate env_logger;

// mod helpers;

use centralgithook::RepoHost;
use centralgithook::Gerrit;
use centralgithook::Shell;
use tempdir::TempDir;

fn sorted(mut v: Vec<String>) -> Vec<String>
{
    v.sort();
    v
}

#[test]
fn test_gerrit()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/central.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!("central", gerrit.central());
    assert_eq!(vec!["central"], gerrit.projects());
    assert_eq!(td.path().join("git/bsw/central.git").to_str().unwrap(),
               gerrit.fetch_url("central"));
    assert_eq!("ssh://auto@gerrit-test-git:123/bsw/central.git",
               gerrit.remote_url("central"));
}

#[test]
fn test_gerrit_takes_topmost_central()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    shell.command("mkdir -p git/bsw/bla/central.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/bla/central.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!(vec!["bla/central", "central"], gerrit.projects());
}

#[test]
fn test_gerrit_sufix_stripping()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    shell.command("mkdir -p git/bsw/module.git.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/module.git.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!(vec!["central", "module.git"], sorted(gerrit.projects()));

    println!("{}", shell.command("tree"));
}
<commit_msg>fix test<commit_after>extern crate centralgithook;
extern crate tempdir;
#[macro_use]
extern crate log;
extern crate env_logger;

// mod helpers;

use centralgithook::RepoHost;
use centralgithook::Gerrit;
use centralgithook::Shell;
use tempdir::TempDir;

fn sorted(mut v: Vec<String>) -> Vec<String>
{
    v.sort();
    v
}

#[test]
fn test_gerrit()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/central.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!("central", gerrit.central());
    assert_eq!(vec!["central"], gerrit.projects());
    assert_eq!(td.path().join("git/bsw/central.git").to_str().unwrap(),
               gerrit.fetch_url("central"));
    assert_eq!("ssh://auto@gerrit-test-git:123/bsw/central.git",
               gerrit.remote_url("central"));
}

#[test]
fn test_gerrit_takes_topmost_central()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    shell.command("mkdir -p git/bsw/bla/central.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/bla/central.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!(vec!["bla/central", "central"], sorted(gerrit.projects()));
}

#[test]
fn test_gerrit_sufix_stripping()
{
    let td = TempDir::new("gerrit_test").expect("folder gerrit_test should be created");
    let shell = Shell { cwd: td.path().to_path_buf() };
    shell.command("mkdir -p bin/gerrit.sh");

    shell.command("mkdir -p git/bsw/central.git");
    shell.command("mkdir -p git/bsw/module.git.git");
    let gerrit = Gerrit::new(&td.path().join("git/bsw/module.git.git"),
                             "central",
                             "auto",
                             "123");
    assert_eq!(vec!["central", "module.git"], sorted(gerrit.projects()));

    println!("{}", shell.command("tree"));
}
<|endoftext|>