diff options
16 files changed, 237 insertions, 54 deletions
diff --git a/libbindgen/src/clang.rs b/libbindgen/src/clang.rs index 32424d40..da4d3e4b 100644 --- a/libbindgen/src/clang.rs +++ b/libbindgen/src/clang.rs @@ -1267,6 +1267,7 @@ pub fn extract_clang_version() -> String { unsafe { clang_getClangVersion().into() } } +/// A wrapper for the result of evaluating an expression. #[derive(Debug)] pub struct EvalResult { x: CXEvalResult, @@ -1274,12 +1275,19 @@ pub struct EvalResult { #[cfg(feature = "llvm_stable")] impl EvalResult { + /// Create a dummy EvalResult. pub fn new(_: Cursor) -> Self { EvalResult { x: ::std::ptr::null_mut(), } } + /// Not useful in llvm 3.8. + pub fn as_double(&self) -> Option<f64> { + None + } + + /// Not useful in llvm 3.8. pub fn as_int(&self) -> Option<i32> { None } @@ -1287,16 +1295,28 @@ impl EvalResult { #[cfg(not(feature = "llvm_stable"))] impl EvalResult { + /// Evaluate `cursor` and return the result. pub fn new(cursor: Cursor) -> Self { EvalResult { x: unsafe { clang_Cursor_Evaluate(cursor.x) }, } } - pub fn kind(&self) -> Enum_CXEvalResultKind { + fn kind(&self) -> Enum_CXEvalResultKind { unsafe { clang_EvalResult_getKind(self.x) } } + /// Try to get back the result as a double. + pub fn as_double(&self) -> Option<f64> { + match self.kind() { + CXEval_Float => { + Some(unsafe { clang_EvalResult_getAsDouble(self.x) } as f64) + } + _ => None, + } + } + + /// Try to get back the result as an integer. pub fn as_int(&self) -> Option<i32> { match self.kind() { CXEval_Int => { diff --git a/libbindgen/src/codegen/helpers.rs b/libbindgen/src/codegen/helpers.rs index 6e5a6f0e..8c3d3cea 100644 --- a/libbindgen/src/codegen/helpers.rs +++ b/libbindgen/src/codegen/helpers.rs @@ -132,4 +132,37 @@ pub mod ast_ty { expr.int(val) } } + + pub fn byte_array_expr(bytes: &[u8]) -> P<ast::Expr> { + let mut vec = Vec::with_capacity(bytes.len() + 1); + for byte in bytes { + vec.push(int_expr(*byte as i64)); + } + vec.push(int_expr(0)); + + let kind = ast::ExprKind::Vec(vec); + + aster::AstBuilder::new().expr().build_expr_kind(kind) + } + + pub fn cstr_expr(mut string: String) -> P<ast::Expr> { + string.push('\0'); + aster::AstBuilder::new() + .expr() + .build_lit(aster::AstBuilder::new().lit().byte_str(string)) + } + + pub fn float_expr(f: f64) -> P<ast::Expr> { + use aster::str::ToInternedString; + let mut string = f.to_string(); + + // So it gets properly recognised as a floating point constant. + if !string.contains('.') { + string.push('.'); + } + + let interned_str = string.as_str().to_interned_string(); + let kind = ast::LitKind::FloatUnsuffixed(interned_str); + aster::AstBuilder::new().expr().lit().build_lit(kind) + } } diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs index 99ec56f4..ceb023f7 100644 --- a/libbindgen/src/codegen/mod.rs +++ b/libbindgen/src/codegen/mod.rs @@ -304,6 +304,7 @@ impl CodeGenerator for Var { ctx: &BindgenContext, result: &mut CodegenResult, item: &Item) { + use ir::var::VarType; debug!("<Var as CodeGenerator>::codegen: item = {:?}", item); let canonical_name = item.canonical_name(ctx); @@ -320,10 +321,44 @@ impl CodeGenerator for Var { .item() .pub_() .const_(canonical_name) - .expr() - .build(helpers::ast_ty::int_expr(val)) - .build(ty); - result.push(const_item) + .expr(); + let item = match *val { + VarType::Int(val) => { + const_item.build(helpers::ast_ty::int_expr(val)) + .build(ty) + } + VarType::String(ref bytes) => { + // Account the trailing zero. + // + // TODO: Here we ignore the type we just made up, probably + // we should refactor how the variable type and ty id work. + let len = bytes.len() + 1; + let ty = quote_ty!(ctx.ext_cx(), [u8; $len]); + + match String::from_utf8(bytes.clone()) { + Ok(string) => { + const_item.build(helpers::ast_ty::cstr_expr(string)) + .build(quote_ty!(ctx.ext_cx(), &'static $ty)) + } + Err(..) => { + const_item + .build(helpers::ast_ty::byte_array_expr(bytes)) + .build(ty) + } + } + } + VarType::Float(f) => { + const_item.build(helpers::ast_ty::float_expr(f)) + .build(ty) + } + VarType::Char(c) => { + const_item + .build(aster::AstBuilder::new().expr().lit().byte(c)) + .build(ty) + } + }; + + result.push(item); } else { let mut attrs = vec![]; if let Some(mangled) = self.mangled_name() { diff --git a/libbindgen/src/ir/item.rs b/libbindgen/src/ir/item.rs index 1f05f92f..253db8c0 100644 --- a/libbindgen/src/ir/item.rs +++ b/libbindgen/src/ir/item.rs @@ -840,11 +840,7 @@ impl ClangItemParser for Item { ctx: &mut BindgenContext) -> ItemId { let id = ctx.next_item_id(); - Self::from_ty_or_ref_with_id(id, - ty, - location, - parent_id, - ctx) + Self::from_ty_or_ref_with_id(id, ty, location, parent_id, ctx) } /// Parse a C++ type. If we find a reference to a type that has not been diff --git a/libbindgen/src/ir/ty.rs b/libbindgen/src/ir/ty.rs index 254568bf..48fd65bb 100644 --- a/libbindgen/src/ir/ty.rs +++ b/libbindgen/src/ir/ty.rs @@ -123,6 +123,14 @@ impl Type { Self::new(Some(name), None, kind, false) } + /// Is this an floating point type? + pub fn is_float(&self) -> bool { + match self.kind { + TypeKind::Float(..) => true, + _ => false, + } + } + /// Is this an integer type? pub fn is_integer(&self) -> bool { match self.kind { diff --git a/libbindgen/src/ir/var.rs b/libbindgen/src/ir/var.rs index c4f1f750..51100514 100644 --- a/libbindgen/src/ir/var.rs +++ b/libbindgen/src/ir/var.rs @@ -8,7 +8,15 @@ use super::context::{BindgenContext, ItemId}; use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; -use super::ty::TypeKind; +use super::ty::{FloatKind, TypeKind}; + +#[derive(Debug)] +pub enum VarType { + Int(i64), + Float(f64), + Char(u8), + String(Vec<u8>), +} /// A `Var` is our intermediate representation of a variable. #[derive(Debug)] @@ -19,9 +27,8 @@ pub struct Var { mangled_name: Option<String>, /// The type of the variable. ty: ItemId, - /// TODO: support non-integer constants? - /// The integer value of the variable. - val: Option<i64>, + /// The value of the variable, that needs to be suitable for `ty`. + val: Option<VarType>, /// Whether this variable is const. is_const: bool, } @@ -31,7 +38,7 @@ impl Var { pub fn new(name: String, mangled: Option<String>, ty: ItemId, - val: Option<i64>, + val: Option<VarType>, is_const: bool) -> Var { assert!(!name.is_empty()); @@ -50,8 +57,8 @@ impl Var { } /// The value of this constant variable, if any. - pub fn val(&self) -> Option<i64> { - self.val + pub fn val(&self) -> Option<&VarType> { + self.val.as_ref() } /// Get this variable's type. @@ -76,6 +83,7 @@ impl ClangSubItemParser for Var { -> Result<ParseResult<Self>, ParseError> { use clangll::*; use cexpr::expr::EvalResult; + use cexpr::literal::CChar; match cursor.kind() { CXCursor_MacroDefinition => { let value = parse_macro(ctx, &cursor, ctx.translation_unit()); @@ -105,13 +113,32 @@ impl ClangSubItemParser for Var { // enforce utf8 there, so we should have already panicked at // this point. let name = String::from_utf8(id).unwrap(); - let (int_kind, val) = match value { - // TODO(emilio): Handle the non-invalid ones! - EvalResult::Float(..) | - EvalResult::Char(..) | - EvalResult::Str(..) | + let (type_kind, val) = match value { EvalResult::Invalid => return Err(ParseError::Continue), - + EvalResult::Float(f) => { + (TypeKind::Float(FloatKind::Float), VarType::Float(f)) + } + EvalResult::Char(c) => { + let c = match c { + CChar::Char(c) => { + assert_eq!(c.len_utf8(), 1); + c as u8 + } + CChar::Raw(c) => { + assert!(c <= ::std::u8::MAX as u64); + c as u8 + } + }; + + (TypeKind::Int(IntKind::U8), VarType::Char(c)) + } + EvalResult::Str(val) => { + let char_ty = + Item::builtin_type(TypeKind::Int(IntKind::U8), + true, + ctx); + (TypeKind::Pointer(char_ty), VarType::String(val)) + } EvalResult::Int(Wrapping(value)) => { let kind = ctx.options() .type_chooser @@ -131,11 +158,11 @@ impl ClangSubItemParser for Var { } }); - (kind, value) + (TypeKind::Int(kind), VarType::Int(value)) } }; - let ty = Item::builtin_type(TypeKind::Int(int_kind), true, ctx); + let ty = Item::builtin_type(type_kind, true, ctx); Ok(ParseResult::New(Var::new(name, None, ty, Some(val), true), Some(cursor))) @@ -159,11 +186,16 @@ impl ClangSubItemParser for Var { // tests/headers/inner_const.hpp // // That's fine because in that case we know it's not a literal. - let is_integer = ctx.safe_resolve_type(ty) - .and_then(|t| t.safe_canonical_type(ctx)) - .map(|t| t.is_integer()) - .unwrap_or(false); + let canonical_ty = ctx.safe_resolve_type(ty) + .and_then(|t| t.safe_canonical_type(ctx)); + let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); + let is_float = canonical_ty.map_or(false, |t| t.is_float()); + + // TODO: We could handle `char` more gracefully. + // TODO: Strings, though the lookup is a bit more hard (we need + // to look at the canonical type of the pointee too, and check + // is char, u8, or i8 I guess). let value = if is_integer { cursor.evaluate() .as_int() @@ -172,16 +204,19 @@ impl ClangSubItemParser for Var { let tu = ctx.translation_unit(); get_integer_literal_from_cursor(&cursor, tu) }) + .map(VarType::Int) + } else if is_float { + cursor.evaluate() + .as_double() + .map(VarType::Float) } else { None }; - let mangling = cursor_mangling(&cursor); - let var = Var::new(name, mangling, ty, value, is_const); - Ok(ParseResult::New(var, Some(cursor))) + Ok(ParseResult::New(var, Some(cursor))) } _ => { /* TODO */ diff --git a/tests/expectations/tests/constant-evaluate.rs b/libbindgen/tests/expectations/tests/constant-evaluate.rs index ec953018..27b8d829 100644 --- a/tests/expectations/tests/constant-evaluate.rs +++ b/libbindgen/tests/expectations/tests/constant-evaluate.rs @@ -10,3 +10,6 @@ pub const bar: _bindgen_ty_1 = _bindgen_ty_1::bar; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum _bindgen_ty_1 { foo = 4, bar = 8, } pub const BAZ: ::std::os::raw::c_longlong = 24; +pub const fuzz: f64 = 51.; +pub const BAZZ: ::std::os::raw::c_char = 53; +pub const WAT: ::std::os::raw::c_char = 0; diff --git a/libbindgen/tests/expectations/tests/macro_const.rs b/libbindgen/tests/expectations/tests/macro_const.rs new file mode 100644 index 00000000..9e7eb420 --- /dev/null +++ b/libbindgen/tests/expectations/tests/macro_const.rs @@ -0,0 +1,12 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub const foo: &'static [u8; 4usize] = b"bar\x00"; +pub const CHAR: u8 = b'b'; +pub const CHARR: u8 = b'\x00'; +pub const FLOAT: f32 = 5.09; +pub const FLOAT_EXPR: f32 = 0.005; +pub const INVALID_UTF8: [u8; 5usize] = [240, 40, 140, 40, 0]; diff --git a/tests/expectations/tests/type_alias_partial_template_especialization.rs b/libbindgen/tests/expectations/tests/type_alias_partial_template_especialization.rs index 70b5f66c..70b5f66c 100644 --- a/tests/expectations/tests/type_alias_partial_template_especialization.rs +++ b/libbindgen/tests/expectations/tests/type_alias_partial_template_especialization.rs diff --git a/tests/expectations/tests/type_alias_template_specialized.rs b/libbindgen/tests/expectations/tests/type_alias_template_specialized.rs index 989f2015..989f2015 100644 --- a/tests/expectations/tests/type_alias_template_specialized.rs +++ b/libbindgen/tests/expectations/tests/type_alias_template_specialized.rs diff --git a/libbindgen/tests/headers/constant-evaluate.h b/libbindgen/tests/headers/constant-evaluate.h new file mode 100644 index 00000000..2790d603 --- /dev/null +++ b/libbindgen/tests/headers/constant-evaluate.h @@ -0,0 +1,11 @@ +// bindgen-unstable + +enum { + foo = 4, + bar = 8, +}; + +const long long BAZ = (1 << foo) | bar; +const double fuzz = (1 + 50.0f); +const char BAZZ = '5'; +const char WAT = '\0'; diff --git a/libbindgen/tests/headers/macro_const.h b/libbindgen/tests/headers/macro_const.h new file mode 100644 index 00000000..c28a3f6b --- /dev/null +++ b/libbindgen/tests/headers/macro_const.h @@ -0,0 +1,7 @@ +#define foo "bar" +#define CHAR 'b' +#define CHARR '\0' +#define FLOAT 5.09f +#define FLOAT_EXPR (5 / 1000.0f) + +#define INVALID_UTF8 "\xf0\x28\x8c\x28" diff --git a/tests/headers/type_alias_partial_template_especialization.hpp b/libbindgen/tests/headers/type_alias_partial_template_especialization.hpp index dfc36786..dfc36786 100644 --- a/tests/headers/type_alias_partial_template_especialization.hpp +++ b/libbindgen/tests/headers/type_alias_partial_template_especialization.hpp diff --git a/tests/headers/type_alias_template_specialized.hpp b/libbindgen/tests/headers/type_alias_template_specialized.hpp index a2d32b56..a2d32b56 100644 --- a/tests/headers/type_alias_template_specialized.hpp +++ b/libbindgen/tests/headers/type_alias_template_specialized.hpp diff --git a/libbindgen/tests/tests.rs b/libbindgen/tests/tests.rs index c0786406..cb26c88a 100644 --- a/libbindgen/tests/tests.rs +++ b/libbindgen/tests/tests.rs @@ -7,7 +7,7 @@ extern crate log; extern crate shlex; use std::fs; -use std::io::{BufRead, BufReader, Error, ErrorKind, Read}; +use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write}; use std::path::PathBuf; #[path="../../src/options.rs"] @@ -35,11 +35,18 @@ fn compare_generated_header(header: &PathBuf, }; let mut buffer = String::new(); - let f = try!(fs::File::open(&expected)); - let _ = try!(BufReader::new(f).read_to_string(&mut buffer)); + { + if let Ok(expected_file) = fs::File::open(&expected) { + try!(BufReader::new(expected_file).read_to_string(&mut buffer)); + } + } if output == buffer { - return Ok(()); + if !output.is_empty() { + return Ok(()); + } + return Err(Error::new(ErrorKind::Other, + "Something's gone really wrong!")) } println!("diff expected generated"); @@ -53,21 +60,34 @@ fn compare_generated_header(header: &PathBuf, diff::Result::Right(r) => println!("+{}", r), } } + + // Override the diff. + { + let mut expected_file = try!(fs::File::create(&expected)); + try!(expected_file.write_all(output.as_bytes())); + } + Err(Error::new(ErrorKind::Other, "Header and binding differ!")) } fn create_bindgen_builder(header: &PathBuf) - -> Result<libbindgen::Builder, Error> { + -> Result<Option<libbindgen::Builder>, Error> { let source = try!(fs::File::open(header)); let reader = BufReader::new(source); // Scoop up bindgen-flags from test header - let line: String = try!(reader.lines().take(1).collect()); - let flags: Vec<String> = if line.contains("bindgen-flags:") { - line.split("bindgen-flags:").last().and_then(shlex::split) - } else { - None - }.unwrap_or(Vec::with_capacity(2)); + let mut flags = Vec::with_capacity(2); + + for line in reader.lines().take(2) { + let line = try!(line); + if line.contains("bindgen-flags: ") { + let extra_flags = line.split("bindgen-flags: ") + .last().and_then(shlex::split).unwrap(); + flags.extend(extra_flags.into_iter()); + } else if line.contains("bindgen-unstable") && cfg!(feature = "llvm_stable") { + return Ok(None) + } + } // Fool builder_from_flags() into believing it has real env::args_os... // - add "bindgen" as executable name 0th element @@ -89,7 +109,8 @@ fn create_bindgen_builder(header: &PathBuf) .map(ToString::to_string) .chain(flags.into_iter()); - builder_from_flags(args).map(|(builder, _)| builder.no_unstable_rust()) + builder_from_flags(args) + .map(|(builder, _)| Some(builder.no_unstable_rust())) } macro_rules! test_header { @@ -97,9 +118,18 @@ macro_rules! test_header { #[test] fn $function() { let header = PathBuf::from($header); - let _ = create_bindgen_builder(&header) - .and_then(|builder| compare_generated_header(&header, builder)) - .map_err(|err| panic!(format!("{}", err)) ); + let result = create_bindgen_builder(&header) + .and_then(|builder| { + if let Some(builder) = builder { + compare_generated_header(&header, builder) + } else { + Ok(()) + } + }); + + if let Err(err) = result { + panic!("{}", err); + } } ) } diff --git a/tests/headers/constant-evaluate.h b/tests/headers/constant-evaluate.h deleted file mode 100644 index 1c6747a6..00000000 --- a/tests/headers/constant-evaluate.h +++ /dev/null @@ -1,7 +0,0 @@ - -enum { - foo = 4, - bar = 8, -}; - -const long long BAZ = (1 << foo) | bar; |