summaryrefslogtreecommitdiff
path: root/libbindgen/src
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 /libbindgen/src
parent91faa76c44acb863a8cf4a5237faa75ac21a8dee (diff)
Multiple constant generation evaluation improvements.
Diffstat (limited to 'libbindgen/src')
-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
6 files changed, 159 insertions, 32 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 */