summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <ecoal95@gmail.com>2016-11-15 14:29:46 +0100
committerEmilio Cobos Álvarez <ecoal95@gmail.com>2016-11-16 00:47:11 +0100
commit824f99a67721584b43544ed561236e6bbec24fed (patch)
tree0810cc8c131c5054b26551b93e548fc76a7d6b2b
parent91faa76c44acb863a8cf4a5237faa75ac21a8dee (diff)
Multiple constant generation evaluation improvements.
-rw-r--r--libbindgen/src/clang.rs22
-rw-r--r--libbindgen/src/codegen/helpers.rs33
-rw-r--r--libbindgen/src/codegen/mod.rs43
-rw-r--r--libbindgen/src/ir/item.rs6
-rw-r--r--libbindgen/src/ir/ty.rs8
-rw-r--r--libbindgen/src/ir/var.rs79
-rw-r--r--libbindgen/tests/expectations/tests/constant-evaluate.rs (renamed from tests/expectations/tests/constant-evaluate.rs)3
-rw-r--r--libbindgen/tests/expectations/tests/macro_const.rs12
-rw-r--r--libbindgen/tests/expectations/tests/type_alias_partial_template_especialization.rs (renamed from tests/expectations/tests/type_alias_partial_template_especialization.rs)0
-rw-r--r--libbindgen/tests/expectations/tests/type_alias_template_specialized.rs (renamed from tests/expectations/tests/type_alias_template_specialized.rs)0
-rw-r--r--libbindgen/tests/headers/constant-evaluate.h11
-rw-r--r--libbindgen/tests/headers/macro_const.h7
-rw-r--r--libbindgen/tests/headers/type_alias_partial_template_especialization.hpp (renamed from tests/headers/type_alias_partial_template_especialization.hpp)0
-rw-r--r--libbindgen/tests/headers/type_alias_template_specialized.hpp (renamed from tests/headers/type_alias_template_specialized.hpp)0
-rw-r--r--libbindgen/tests/tests.rs60
-rw-r--r--tests/headers/constant-evaluate.h7
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;