diff options
author | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-11-15 14:29:46 +0100 |
---|---|---|
committer | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-11-16 00:47:11 +0100 |
commit | 824f99a67721584b43544ed561236e6bbec24fed (patch) | |
tree | 0810cc8c131c5054b26551b93e548fc76a7d6b2b /libbindgen/src | |
parent | 91faa76c44acb863a8cf4a5237faa75ac21a8dee (diff) |
Multiple constant generation evaluation improvements.
Diffstat (limited to 'libbindgen/src')
-rw-r--r-- | libbindgen/src/clang.rs | 22 | ||||
-rw-r--r-- | libbindgen/src/codegen/helpers.rs | 33 | ||||
-rw-r--r-- | libbindgen/src/codegen/mod.rs | 43 | ||||
-rw-r--r-- | libbindgen/src/ir/item.rs | 6 | ||||
-rw-r--r-- | libbindgen/src/ir/ty.rs | 8 | ||||
-rw-r--r-- | libbindgen/src/ir/var.rs | 79 |
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 */ |