Test some functions!

This commit is contained in:
Alex Crichton 2015-09-10 19:59:23 -07:00
parent d7a1547044
commit 2237882ddc
3 changed files with 159 additions and 97 deletions

View File

@ -6,7 +6,6 @@ use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::iter;
use std::path::{Path, PathBuf};
use syntax::ast;
@ -79,6 +78,8 @@ impl<'a> TestGenerator<'a> {
base.push("windows.h");
base.push("sys/utime.h");
} else {
base.push("ctype.h");
base.push("dirent.h");
base.push("glob.h");
base.push("ifaddrs.h");
base.push("net/if.h");
@ -89,13 +90,16 @@ impl<'a> TestGenerator<'a> {
base.push("pthread.h");
base.push("signal.h");
base.push("stdalign.h");
base.push("sys/file.h");
base.push("sys/ioctl.h");
base.push("sys/mman.h");
base.push("sys/resource.h");
base.push("sys/socket.h");
base.push("sys/time.h");
base.push("sys/un.h");
base.push("utime.h");
base.push("sys/wait.h");
base.push("unistd.h");
base.push("utime.h");
}
return base
@ -113,7 +117,10 @@ impl<'a> TestGenerator<'a> {
}
}
"ip6_mreq" => "struct ipv6_mreq".to_string(),
"glob_t" => "glob_t".to_string(),
"glob_t" |
"FILE" |
"DIR" |
"fpos_t" => ty.to_string(),
t if t.starts_with("pthread") => t.to_string(),
t if self.structs.contains(t) => {
@ -253,12 +260,12 @@ impl<'a> TestGenerator<'a> {
"sighandler_t" => return,
_ => {}
}
let c = self.rust2c(ty);
let c = self.rust_ty_to_c_ty(ty);
self.test_size_align(ty, &c);
}
fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
let cty = self.rust2c(ty);
let cty = self.rust_ty_to_c_ty(ty);
self.test_size_align(ty, &cty);
t!(writeln!(self.rust, r#"
@ -326,12 +333,23 @@ impl<'a> TestGenerator<'a> {
"#, ty = rust));
}
fn rust_ty_to_c_ty(&self, mut rust_ty: &str) -> String {
let mut cty = self.rust2c(&rust_ty.replace("*mut ", "")
.replace("*const ", ""));
while rust_ty.starts_with("*") {
if rust_ty.starts_with("*const") {
cty = format!("const {}*", cty);
rust_ty = &rust_ty[7..];
} else {
cty = format!("{}*", cty);
rust_ty = &rust_ty[5..];
}
}
return cty
}
fn test_const(&mut self, name: &str, rust_ty: &str) {
let cty = self.rust2c(&rust_ty.replace("*mut ", "")
.replace("*const ", ""));
let ptrs = rust_ty.matches("*").count();
let cty = format!("{}{}", cty,
iter::repeat("*").take(ptrs).collect::<String>());
let cty = self.rust_ty_to_c_ty(rust_ty);
let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
t!(writeln!(self.c, r#"
int __test_const_{name}({cty} *outptr) {{
@ -357,10 +375,49 @@ impl<'a> TestGenerator<'a> {
"#, ty = rust_ty, name = name));
}
fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
assert!(generics.lifetimes.len() == 0);
assert!(generics.ty_params.len() == 0);
assert!(generics.where_clause.predicates.len() == 0);
fn test_extern_fn(&mut self, name: &str, args: &[String], ret: &str,
variadic: bool) {
match name {
// manually verified
"execv" |
"execve" |
"execvp" |
"glob" |
"getrlimit" |
"setrlimit" |
"getopt" => return,
_ => {}
}
let args = if args.len() == 0 && !variadic {
"void".to_string()
} else {
args.iter().map(|a| self.rust_ty_to_c_ty(a)).collect::<Vec<_>>()
.connect(", ") + if variadic {", ..."} else {""}
};
let cret = self.rust_ty_to_c_ty(ret);
t!(writeln!(self.c, r#"
{ret} (*__test_fn_{name}(void))({args}) {{
return {name};
}}
"#, name = name, args = args, ret = cret));
t!(writeln!(self.rust, r#"
#[test]
fn fn_{name}() {{
extern {{
fn __test_fn_{name}() -> size_t;
}}
unsafe {{
same({name} as usize,
__test_fn_{name}() as usize, "function pointer");
}}
}}
"#, name = name));
}
fn assert_no_generics(&self, _i: ast::Ident, generics: &ast::Generics) {
assert!(generics.lifetimes.len() == 0);
assert!(generics.ty_params.len() == 0);
assert!(generics.where_clause.predicates.len() == 0);
}
fn ty2name(&self, ty: &ast::Ty) -> String {
@ -374,52 +431,90 @@ impl<'a> TestGenerator<'a> {
ast::MutMutable => "mut",
}, self.ty2name(&t.ty))
}
ast::TyBareFn(ref t) => {
assert!(t.lifetimes.len() == 0);
let (ret, mut args, variadic) = self.decl2rust(&t.decl);
assert!(!variadic);
if args.len() == 0 {
args.push("void".to_string());
}
format!("{}(*)({})", ret, args.connect(", "))
}
_ => panic!("unknown ty {:?}", ty),
}
}
fn decl2rust(&self, decl: &ast::FnDecl) -> (String, Vec<String>, bool) {
let args = decl.inputs.iter().map(|arg| {
self.ty2name(&arg.ty)
}).collect::<Vec<_>>();
let ret = match decl.output {
ast::NoReturn(..) |
ast::DefaultReturn(..) => "void".to_string(),
ast::Return(ref t) => self.ty2name(t),
};
(ret, args, decl.variadic)
}
}
impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemTy(_, ref generics) => {
self.assert_no_generics(i, generics);
self.test_type(&i.ident.to_string());
}
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemTy(_, ref generics) => {
self.assert_no_generics(i.ident, generics);
self.test_type(&i.ident.to_string());
}
ast::ItemStruct(ref s, ref generics) => {
self.assert_no_generics(i, generics);
let is_c = i.attrs.iter().any(|a| {
ast::ItemStruct(ref s, ref generics) => {
self.assert_no_generics(i.ident, generics);
let is_c = i.attrs.iter().any(|a| {
attr::find_repr_attrs(self.sh, a).iter().any(|a| {
*a == ReprAttr::ReprExtern
})
});
if !is_c {
panic!("{} is not marked #[repr(C)]", i.ident);
}
self.test_struct(&i.ident.to_string(), s);
}
});
if !is_c {
panic!("{} is not marked #[repr(C)]", i.ident);
}
self.test_struct(&i.ident.to_string(), s);
}
ast::ItemConst(ref ty, _) => {
let ty = self.ty2name(ty);
self.test_const(&i.ident.to_string(), &ty);
}
ast::ItemConst(ref ty, _) => {
let ty = self.ty2name(ty);
self.test_const(&i.ident.to_string(), &ty);
}
_ => {}
}
visit::walk_item(self, i)
}
_ => {}
}
visit::walk_item(self, i)
}
fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
match i.node {
ast::ForeignItemFn(ref decl, ref generics) => {
self.assert_no_generics(i.ident, generics);
let (ret, args, variadic) = self.decl2rust(decl);
self.test_extern_fn(&i.ident.to_string(), &args, &ret,
variadic);
}
ast::ForeignItemStatic(_, _) => {
}
}
visit::walk_foreign_item(self, i)
}
}
impl<'v> Visitor<'v> for StructFinder {
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemStruct(..) => {
self.structs.insert(i.ident.to_string());
}
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemStruct(..) => {
self.structs.insert(i.ident.to_string());
}
ast::ItemEnum(..) => {
self.structs.insert(i.ident.to_string());
}
_ => {}
}
visit::walk_item(self, i)
}
_ => {}
}
visit::walk_item(self, i)
}
}

View File

@ -7,8 +7,12 @@ use std::any::{Any, TypeId};
use std::mem;
use libc::*;
#[cfg(not(windows))]
use libc::types::os::common::bsd43::*;
#[cfg(unix)] use libc::types::os::common::bsd43::*;
#[cfg(unix)] use libc::funcs::bsd44::*;
#[cfg(unix)] use libc::funcs::posix01::mman::*;
#[cfg(unix)] use libc::funcs::posix01::glob::*;
#[cfg(unix)] use libc::funcs::posix01::signal::*;
#[cfg(unix)] use libc::funcs::posix88::signal::*;
trait Pretty {
fn pretty(&self) -> String;

View File

@ -201,7 +201,7 @@ pub mod types {
}
pub mod posix88 {
pub enum DIR {}
pub enum dirent_t {}
pub enum dirent {}
}
pub mod posix01 {}
pub mod posix08 {}
@ -5306,7 +5306,7 @@ pub mod funcs {
pub mod c95 {
pub mod ctype {
use types::os::arch::c95::{c_char, c_int};
use types::os::arch::c95::c_int;
extern {
pub fn isalnum(c: c_int) -> c_int;
@ -5320,8 +5320,8 @@ pub mod funcs {
pub fn isspace(c: c_int) -> c_int;
pub fn isupper(c: c_int) -> c_int;
pub fn isxdigit(c: c_int) -> c_int;
pub fn tolower(c: c_char) -> c_char;
pub fn toupper(c: c_char) -> c_char;
pub fn tolower(c: c_int) -> c_int;
pub fn toupper(c: c_int) -> c_int;
}
}
@ -5376,7 +5376,7 @@ pub mod funcs {
pub fn ftell(stream: *mut FILE) -> c_long;
pub fn rewind(stream: *mut FILE);
pub fn fgetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
pub fn fsetpos(stream: *mut FILE, ptr: *mut fpos_t) -> c_int;
pub fn fsetpos(stream: *mut FILE, ptr: *const fpos_t) -> c_int;
pub fn feof(stream: *mut FILE) -> c_int;
pub fn ferror(stream: *mut FILE) -> c_int;
pub fn perror(s: *const c_char);
@ -5659,58 +5659,21 @@ pub mod funcs {
use types::os::arch::c95::{c_char, c_int};
use types::os::arch::posix88::mode_t;
mod open_shim {
extern {
#[cfg(any(target_os = "macos",
target_os = "ios"))]
pub fn open(path: *const ::c_char, oflag: ::c_int, ...)
-> ::c_int;
#[cfg(not(any(target_os = "macos",
target_os = "ios")))]
pub fn open(path: *const ::c_char, oflag: ::c_int, mode: ::mode_t)
-> ::c_int;
}
}
#[cfg(any(target_os = "macos",
target_os = "ios"))]
#[inline]
pub unsafe extern fn open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
use types::os::arch::c95::c_uint;
open_shim::open(path, oflag, mode as c_uint)
}
#[cfg(not(any(target_os = "macos",
target_os = "ios")))]
#[inline]
pub unsafe extern fn open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
open_shim::open(path, oflag, mode)
}
extern {
pub fn open(path: *const c_char, oflag: c_int, ...) -> c_int;
pub fn creat(path: *const c_char, mode: mode_t) -> c_int;
pub fn fcntl(fd: c_int, cmd: c_int, ...) -> c_int;
}
}
pub mod dirent {
use types::common::posix88::{DIR, dirent_t};
use types::common::posix88::{DIR, dirent};
use types::os::arch::c95::{c_char, c_int, c_long};
// NB: On OS X opendir and readdir have two versions,
// one for 32-bit kernelspace and one for 64.
// We should be linking to the 64-bit ones, called
// opendir$INODE64, etc. but for some reason rustc
// doesn't link it correctly on i686, so we're going
// through a C function that mysteriously does work.
extern {
#[link_name="rust_opendir"]
pub fn opendir(dirname: *const c_char) -> *mut DIR;
#[link_name="rust_readdir_r"]
pub fn readdir_r(dirp: *mut DIR, entry: *mut dirent_t,
result: *mut *mut dirent_t) -> c_int;
pub fn readdir_r(dirp: *mut DIR, entry: *mut dirent,
result: *mut *mut dirent) -> c_int;
}
extern {
@ -5774,7 +5737,7 @@ pub mod funcs {
pub fn link(src: *const c_char, dst: *const c_char) -> c_int;
pub fn lseek(fd: c_int, offset: off_t, whence: c_int)
-> off_t;
pub fn pathconf(path: *mut c_char, name: c_int) -> c_long;
pub fn pathconf(path: *const c_char, name: c_int) -> c_long;
pub fn pause() -> c_int;
pub fn pipe(fds: *mut c_int) -> c_int;
pub fn read(fd: c_int, buf: *mut c_void, count: size_t)
@ -5792,8 +5755,8 @@ pub mod funcs {
pub fn tcgetpgrp(fd: c_int) -> pid_t;
pub fn ttyname(fd: c_int) -> *mut c_char;
pub fn unlink(c: *const c_char) -> c_int;
pub fn wait(status: *const c_int) -> pid_t;
pub fn waitpid(pid: pid_t, status: *const c_int, options: c_int)
pub fn wait(status: *mut c_int) -> pid_t;
pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int)
-> pid_t;
pub fn write(fd: c_int, buf: *const c_void, count: size_t)
-> ssize_t;