1 //! Intermediate representation of variables.
2 
3 use super::context::{BindgenContext, TypeId};
4 use super::dot::DotAttributes;
5 use super::function::cursor_mangling;
6 use super::int::IntKind;
7 use super::item::Item;
8 use super::ty::{FloatKind, TypeKind};
9 use crate::callbacks::MacroParsingBehavior;
10 use crate::clang;
11 use crate::parse::{
12     ClangItemParser, ClangSubItemParser, ParseError, ParseResult,
13 };
14 use cexpr;
15 use std::io;
16 use std::num::Wrapping;
17 
18 /// The type for a constant variable.
19 #[derive(Debug)]
20 pub enum VarType {
21     /// A boolean.
22     Bool(bool),
23     /// An integer.
24     Int(i64),
25     /// A floating point number.
26     Float(f64),
27     /// A character.
28     Char(u8),
29     /// A string, not necessarily well-formed utf-8.
30     String(Vec<u8>),
31 }
32 
33 /// A `Var` is our intermediate representation of a variable.
34 #[derive(Debug)]
35 pub struct Var {
36     /// The name of the variable.
37     name: String,
38     /// The mangled name of the variable.
39     mangled_name: Option<String>,
40     /// The type of the variable.
41     ty: TypeId,
42     /// The value of the variable, that needs to be suitable for `ty`.
43     val: Option<VarType>,
44     /// Whether this variable is const.
45     is_const: bool,
46 }
47 
48 impl Var {
49     /// Construct a new `Var`.
new( name: String, mangled_name: Option<String>, ty: TypeId, val: Option<VarType>, is_const: bool, ) -> Var50     pub fn new(
51         name: String,
52         mangled_name: Option<String>,
53         ty: TypeId,
54         val: Option<VarType>,
55         is_const: bool,
56     ) -> Var {
57         assert!(!name.is_empty());
58         Var {
59             name,
60             mangled_name,
61             ty,
62             val,
63             is_const,
64         }
65     }
66 
67     /// Is this variable `const` qualified?
is_const(&self) -> bool68     pub fn is_const(&self) -> bool {
69         self.is_const
70     }
71 
72     /// The value of this constant variable, if any.
val(&self) -> Option<&VarType>73     pub fn val(&self) -> Option<&VarType> {
74         self.val.as_ref()
75     }
76 
77     /// Get this variable's type.
ty(&self) -> TypeId78     pub fn ty(&self) -> TypeId {
79         self.ty
80     }
81 
82     /// Get this variable's name.
name(&self) -> &str83     pub fn name(&self) -> &str {
84         &self.name
85     }
86 
87     /// Get this variable's mangled name.
mangled_name(&self) -> Option<&str>88     pub fn mangled_name(&self) -> Option<&str> {
89         self.mangled_name.as_ref().map(|n| &**n)
90     }
91 }
92 
93 impl DotAttributes for Var {
dot_attributes<W>( &self, _ctx: &BindgenContext, out: &mut W, ) -> io::Result<()> where W: io::Write,94     fn dot_attributes<W>(
95         &self,
96         _ctx: &BindgenContext,
97         out: &mut W,
98     ) -> io::Result<()>
99     where
100         W: io::Write,
101     {
102         if self.is_const {
103             writeln!(out, "<tr><td>const</td><td>true</td></tr>")?;
104         }
105 
106         if let Some(ref mangled) = self.mangled_name {
107             writeln!(
108                 out,
109                 "<tr><td>mangled name</td><td>{}</td></tr>",
110                 mangled
111             )?;
112         }
113 
114         Ok(())
115     }
116 }
117 
118 // TODO(emilio): we could make this more (or less) granular, I guess.
default_macro_constant_type(value: i64) -> IntKind119 fn default_macro_constant_type(value: i64) -> IntKind {
120     if value < 0 {
121         if value < i32::min_value() as i64 {
122             IntKind::I64
123         } else {
124             IntKind::I32
125         }
126     } else if value > u32::max_value() as i64 {
127         IntKind::U64
128     } else {
129         IntKind::U32
130     }
131 }
132 
133 impl ClangSubItemParser for Var {
parse( cursor: clang::Cursor, ctx: &mut BindgenContext, ) -> Result<ParseResult<Self>, ParseError>134     fn parse(
135         cursor: clang::Cursor,
136         ctx: &mut BindgenContext,
137     ) -> Result<ParseResult<Self>, ParseError> {
138         use cexpr::expr::EvalResult;
139         use cexpr::literal::CChar;
140         use clang_sys::*;
141         match cursor.kind() {
142             CXCursor_MacroDefinition => {
143                 if let Some(callbacks) = ctx.parse_callbacks() {
144                     match callbacks.will_parse_macro(&cursor.spelling()) {
145                         MacroParsingBehavior::Ignore => {
146                             return Err(ParseError::Continue);
147                         }
148                         MacroParsingBehavior::Default => {}
149                     }
150                 }
151 
152                 let value = parse_macro(ctx, &cursor);
153 
154                 let (id, value) = match value {
155                     Some(v) => v,
156                     None => return Err(ParseError::Continue),
157                 };
158 
159                 assert!(!id.is_empty(), "Empty macro name?");
160 
161                 let previously_defined = ctx.parsed_macro(&id);
162 
163                 // NB: It's important to "note" the macro even if the result is
164                 // not an integer, otherwise we might loose other kind of
165                 // derived macros.
166                 ctx.note_parsed_macro(id.clone(), value.clone());
167 
168                 if previously_defined {
169                     let name = String::from_utf8(id).unwrap();
170                     warn!("Duplicated macro definition: {}", name);
171                     return Err(ParseError::Continue);
172                 }
173 
174                 // NOTE: Unwrapping, here and above, is safe, because the
175                 // identifier of a token comes straight from clang, and we
176                 // enforce utf8 there, so we should have already panicked at
177                 // this point.
178                 let name = String::from_utf8(id).unwrap();
179                 let (type_kind, val) = match value {
180                     EvalResult::Invalid => return Err(ParseError::Continue),
181                     EvalResult::Float(f) => {
182                         (TypeKind::Float(FloatKind::Double), VarType::Float(f))
183                     }
184                     EvalResult::Char(c) => {
185                         let c = match c {
186                             CChar::Char(c) => {
187                                 assert_eq!(c.len_utf8(), 1);
188                                 c as u8
189                             }
190                             CChar::Raw(c) => {
191                                 assert!(c <= ::std::u8::MAX as u64);
192                                 c as u8
193                             }
194                         };
195 
196                         (TypeKind::Int(IntKind::U8), VarType::Char(c))
197                     }
198                     EvalResult::Str(val) => {
199                         let char_ty = Item::builtin_type(
200                             TypeKind::Int(IntKind::U8),
201                             true,
202                             ctx,
203                         );
204                         if let Some(callbacks) = ctx.parse_callbacks() {
205                             callbacks.str_macro(&name, &val);
206                         }
207                         (TypeKind::Pointer(char_ty), VarType::String(val))
208                     }
209                     EvalResult::Int(Wrapping(value)) => {
210                         let kind = ctx
211                             .parse_callbacks()
212                             .and_then(|c| c.int_macro(&name, value))
213                             .unwrap_or_else(|| {
214                                 default_macro_constant_type(value)
215                             });
216 
217                         (TypeKind::Int(kind), VarType::Int(value))
218                     }
219                 };
220 
221                 let ty = Item::builtin_type(type_kind, true, ctx);
222 
223                 Ok(ParseResult::New(
224                     Var::new(name, None, ty, Some(val), true),
225                     Some(cursor),
226                 ))
227             }
228             CXCursor_VarDecl => {
229                 let name = cursor.spelling();
230                 if name.is_empty() {
231                     warn!("Empty constant name?");
232                     return Err(ParseError::Continue);
233                 }
234 
235                 let ty = cursor.cur_type();
236 
237                 // TODO(emilio): do we have to special-case constant arrays in
238                 // some other places?
239                 let is_const = ty.is_const() ||
240                     (ty.kind() == CXType_ConstantArray &&
241                         ty.elem_type()
242                             .map_or(false, |element| element.is_const()));
243 
244                 let ty = match Item::from_ty(&ty, cursor, None, ctx) {
245                     Ok(ty) => ty,
246                     Err(e) => {
247                         assert_eq!(
248                             ty.kind(),
249                             CXType_Auto,
250                             "Couldn't resolve constant type, and it \
251                              wasn't an nondeductible auto type!"
252                         );
253                         return Err(e);
254                     }
255                 };
256 
257                 // Note: Ty might not be totally resolved yet, see
258                 // tests/headers/inner_const.hpp
259                 //
260                 // That's fine because in that case we know it's not a literal.
261                 let canonical_ty = ctx
262                     .safe_resolve_type(ty)
263                     .and_then(|t| t.safe_canonical_type(ctx));
264 
265                 let is_integer = canonical_ty.map_or(false, |t| t.is_integer());
266                 let is_float = canonical_ty.map_or(false, |t| t.is_float());
267 
268                 // TODO: We could handle `char` more gracefully.
269                 // TODO: Strings, though the lookup is a bit more hard (we need
270                 // to look at the canonical type of the pointee too, and check
271                 // is char, u8, or i8 I guess).
272                 let value = if is_integer {
273                     let kind = match *canonical_ty.unwrap().kind() {
274                         TypeKind::Int(kind) => kind,
275                         _ => unreachable!(),
276                     };
277 
278                     let mut val = cursor.evaluate().and_then(|v| v.as_int());
279                     if val.is_none() || !kind.signedness_matches(val.unwrap()) {
280                         let tu = ctx.translation_unit();
281                         val = get_integer_literal_from_cursor(&cursor, tu);
282                     }
283 
284                     val.map(|val| {
285                         if kind == IntKind::Bool {
286                             VarType::Bool(val != 0)
287                         } else {
288                             VarType::Int(val)
289                         }
290                     })
291                 } else if is_float {
292                     cursor
293                         .evaluate()
294                         .and_then(|v| v.as_double())
295                         .map(VarType::Float)
296                 } else {
297                     cursor
298                         .evaluate()
299                         .and_then(|v| v.as_literal_string())
300                         .map(VarType::String)
301                 };
302 
303                 let mangling = cursor_mangling(ctx, &cursor);
304                 let var = Var::new(name, mangling, ty, value, is_const);
305 
306                 Ok(ParseResult::New(var, Some(cursor)))
307             }
308             _ => {
309                 /* TODO */
310                 Err(ParseError::Continue)
311             }
312         }
313     }
314 }
315 
316 /// Try and parse a macro using all the macros parsed until now.
parse_macro( ctx: &BindgenContext, cursor: &clang::Cursor, ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)>317 fn parse_macro(
318     ctx: &BindgenContext,
319     cursor: &clang::Cursor,
320 ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
321     use cexpr::expr;
322 
323     let mut cexpr_tokens = cursor.cexpr_tokens();
324 
325     let parser = expr::IdentifierParser::new(ctx.parsed_macros());
326 
327     match parser.macro_definition(&cexpr_tokens) {
328         Ok((_, (id, val))) => {
329             return Some((id.into(), val));
330         }
331         _ => {}
332     }
333 
334     // Try without the last token, to workaround a libclang bug in versions
335     // previous to 4.0.
336     //
337     // See:
338     //   https://bugs.llvm.org//show_bug.cgi?id=9069
339     //   https://reviews.llvm.org/D26446
340     cexpr_tokens.pop()?;
341 
342     match parser.macro_definition(&cexpr_tokens) {
343         Ok((_, (id, val))) => Some((id.into(), val)),
344         _ => None,
345     }
346 }
347 
parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64>348 fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> {
349     use cexpr::expr;
350     use cexpr::expr::EvalResult;
351 
352     let cexpr_tokens = cursor.cexpr_tokens();
353 
354     // TODO(emilio): We can try to parse other kinds of literals.
355     match expr::expr(&cexpr_tokens) {
356         Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val),
357         _ => None,
358     }
359 }
360 
get_integer_literal_from_cursor( cursor: &clang::Cursor, unit: &clang::TranslationUnit, ) -> Option<i64>361 fn get_integer_literal_from_cursor(
362     cursor: &clang::Cursor,
363     unit: &clang::TranslationUnit,
364 ) -> Option<i64> {
365     use clang_sys::*;
366     let mut value = None;
367     cursor.visit(|c| {
368         match c.kind() {
369             CXCursor_IntegerLiteral | CXCursor_UnaryOperator => {
370                 value = parse_int_literal_tokens(&c);
371             }
372             CXCursor_UnexposedExpr => {
373                 value = get_integer_literal_from_cursor(&c, unit);
374             }
375             _ => (),
376         }
377         if value.is_some() {
378             CXChildVisit_Break
379         } else {
380             CXChildVisit_Continue
381         }
382     });
383     value
384 }
385