• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Intermediate representation of variables.
2 
3 use super::super::codegen::MacroTypeVariation;
4 use super::context::{BindgenContext, TypeId};
5 use super::dot::DotAttributes;
6 use super::function::cursor_mangling;
7 use super::int::IntKind;
8 use super::item::Item;
9 use super::ty::{FloatKind, TypeKind};
10 use crate::callbacks::{ItemInfo, ItemKind, MacroParsingBehavior};
11 use crate::clang;
12 use crate::clang::ClangToken;
13 use crate::parse::{ClangSubItemParser, ParseError, ParseResult};
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_deref()
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 
default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind118 fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind {
119     if value < 0 ||
120         ctx.options().default_macro_constant_type ==
121             MacroTypeVariation::Signed
122     {
123         if value < i32::min_value() as i64 || value > i32::max_value() as i64 {
124             IntKind::I64
125         } else if !ctx.options().fit_macro_constants ||
126             value < i16::min_value() as i64 ||
127             value > i16::max_value() as i64
128         {
129             IntKind::I32
130         } else if value < i8::min_value() as i64 ||
131             value > i8::max_value() as i64
132         {
133             IntKind::I16
134         } else {
135             IntKind::I8
136         }
137     } else if value > u32::max_value() as i64 {
138         IntKind::U64
139     } else if !ctx.options().fit_macro_constants ||
140         value > u16::max_value() as i64
141     {
142         IntKind::U32
143     } else if value > u8::max_value() as i64 {
144         IntKind::U16
145     } else {
146         IntKind::U8
147     }
148 }
149 
150 /// Parses tokens from a CXCursor_MacroDefinition pointing into a function-like
151 /// macro, and calls the func_macro callback.
handle_function_macro( cursor: &clang::Cursor, callbacks: &dyn crate::callbacks::ParseCallbacks, )152 fn handle_function_macro(
153     cursor: &clang::Cursor,
154     callbacks: &dyn crate::callbacks::ParseCallbacks,
155 ) {
156     let is_closing_paren = |t: &ClangToken| {
157         // Test cheap token kind before comparing exact spellings.
158         t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")"
159     };
160     let tokens: Vec<_> = cursor.tokens().iter().collect();
161     if let Some(boundary) = tokens.iter().position(is_closing_paren) {
162         let mut spelled = tokens.iter().map(ClangToken::spelling);
163         // Add 1, to convert index to length.
164         let left = spelled.by_ref().take(boundary + 1);
165         let left = left.collect::<Vec<_>>().concat();
166         if let Ok(left) = String::from_utf8(left) {
167             let right: Vec<_> = spelled.collect();
168             callbacks.func_macro(&left, &right);
169         }
170     }
171 }
172 
173 impl ClangSubItemParser for Var {
parse( cursor: clang::Cursor, ctx: &mut BindgenContext, ) -> Result<ParseResult<Self>, ParseError>174     fn parse(
175         cursor: clang::Cursor,
176         ctx: &mut BindgenContext,
177     ) -> Result<ParseResult<Self>, ParseError> {
178         use cexpr::expr::EvalResult;
179         use cexpr::literal::CChar;
180         use clang_sys::*;
181         match cursor.kind() {
182             CXCursor_MacroDefinition => {
183                 for callbacks in &ctx.options().parse_callbacks {
184                     match callbacks.will_parse_macro(&cursor.spelling()) {
185                         MacroParsingBehavior::Ignore => {
186                             return Err(ParseError::Continue);
187                         }
188                         MacroParsingBehavior::Default => {}
189                     }
190 
191                     if cursor.is_macro_function_like() {
192                         handle_function_macro(&cursor, callbacks.as_ref());
193                         // We handled the macro, skip macro processing below.
194                         return Err(ParseError::Continue);
195                     }
196                 }
197 
198                 let value = parse_macro(ctx, &cursor);
199 
200                 let (id, value) = match value {
201                     Some(v) => v,
202                     None => return Err(ParseError::Continue),
203                 };
204 
205                 assert!(!id.is_empty(), "Empty macro name?");
206 
207                 let previously_defined = ctx.parsed_macro(&id);
208 
209                 // NB: It's important to "note" the macro even if the result is
210                 // not an integer, otherwise we might loose other kind of
211                 // derived macros.
212                 ctx.note_parsed_macro(id.clone(), value.clone());
213 
214                 if previously_defined {
215                     let name = String::from_utf8(id).unwrap();
216                     warn!("Duplicated macro definition: {}", name);
217                     return Err(ParseError::Continue);
218                 }
219 
220                 // NOTE: Unwrapping, here and above, is safe, because the
221                 // identifier of a token comes straight from clang, and we
222                 // enforce utf8 there, so we should have already panicked at
223                 // this point.
224                 let name = String::from_utf8(id).unwrap();
225                 let (type_kind, val) = match value {
226                     EvalResult::Invalid => return Err(ParseError::Continue),
227                     EvalResult::Float(f) => {
228                         (TypeKind::Float(FloatKind::Double), VarType::Float(f))
229                     }
230                     EvalResult::Char(c) => {
231                         let c = match c {
232                             CChar::Char(c) => {
233                                 assert_eq!(c.len_utf8(), 1);
234                                 c as u8
235                             }
236                             CChar::Raw(c) => {
237                                 assert!(c <= ::std::u8::MAX as u64);
238                                 c as u8
239                             }
240                         };
241 
242                         (TypeKind::Int(IntKind::U8), VarType::Char(c))
243                     }
244                     EvalResult::Str(val) => {
245                         let char_ty = Item::builtin_type(
246                             TypeKind::Int(IntKind::U8),
247                             true,
248                             ctx,
249                         );
250                         for callbacks in &ctx.options().parse_callbacks {
251                             callbacks.str_macro(&name, &val);
252                         }
253                         (TypeKind::Pointer(char_ty), VarType::String(val))
254                     }
255                     EvalResult::Int(Wrapping(value)) => {
256                         let kind = ctx
257                             .options()
258                             .last_callback(|c| c.int_macro(&name, value))
259                             .unwrap_or_else(|| {
260                                 default_macro_constant_type(ctx, value)
261                             });
262 
263                         (TypeKind::Int(kind), VarType::Int(value))
264                     }
265                 };
266 
267                 let ty = Item::builtin_type(type_kind, true, ctx);
268 
269                 Ok(ParseResult::New(
270                     Var::new(name, None, ty, Some(val), true),
271                     Some(cursor),
272                 ))
273             }
274             CXCursor_VarDecl => {
275                 let mut name = cursor.spelling();
276                 if cursor.linkage() == CXLinkage_External {
277                     if let Some(nm) = ctx.options().last_callback(|callbacks| {
278                         callbacks.generated_name_override(ItemInfo {
279                             name: name.as_str(),
280                             kind: ItemKind::Var,
281                         })
282                     }) {
283                         name = nm;
284                     }
285                 }
286                 // No more changes to name
287                 let name = name;
288 
289                 if name.is_empty() {
290                     warn!("Empty constant name?");
291                     return Err(ParseError::Continue);
292                 }
293 
294                 let ty = cursor.cur_type();
295 
296                 // TODO(emilio): do we have to special-case constant arrays in
297                 // some other places?
298                 let is_const = ty.is_const() ||
299                     ([CXType_ConstantArray, CXType_IncompleteArray]
300                         .contains(&ty.kind()) &&
301                         ty.elem_type()
302                             .map_or(false, |element| element.is_const()));
303 
304                 let ty = match Item::from_ty(&ty, cursor, None, ctx) {
305                     Ok(ty) => ty,
306                     Err(e) => {
307                         assert!(
308                             matches!(ty.kind(), CXType_Auto | CXType_Unexposed),
309                             "Couldn't resolve constant type, and it \
310                              wasn't an nondeductible auto type or unexposed \
311                              type!"
312                         );
313                         return Err(e);
314                     }
315                 };
316 
317                 // Note: Ty might not be totally resolved yet, see
318                 // tests/headers/inner_const.hpp
319                 //
320                 // That's fine because in that case we know it's not a literal.
321                 let canonical_ty = ctx
322                     .safe_resolve_type(ty)
323                     .and_then(|t| t.safe_canonical_type(ctx));
324 
325                 let is_integer = canonical_ty.map_or(false, |t| t.is_integer());
326                 let is_float = canonical_ty.map_or(false, |t| t.is_float());
327 
328                 // TODO: We could handle `char` more gracefully.
329                 // TODO: Strings, though the lookup is a bit more hard (we need
330                 // to look at the canonical type of the pointee too, and check
331                 // is char, u8, or i8 I guess).
332                 let value = if is_integer {
333                     let kind = match *canonical_ty.unwrap().kind() {
334                         TypeKind::Int(kind) => kind,
335                         _ => unreachable!(),
336                     };
337 
338                     let mut val = cursor.evaluate().and_then(|v| v.as_int());
339                     if val.is_none() || !kind.signedness_matches(val.unwrap()) {
340                         val = get_integer_literal_from_cursor(&cursor);
341                     }
342 
343                     val.map(|val| {
344                         if kind == IntKind::Bool {
345                             VarType::Bool(val != 0)
346                         } else {
347                             VarType::Int(val)
348                         }
349                     })
350                 } else if is_float {
351                     cursor
352                         .evaluate()
353                         .and_then(|v| v.as_double())
354                         .map(VarType::Float)
355                 } else {
356                     cursor
357                         .evaluate()
358                         .and_then(|v| v.as_literal_string())
359                         .map(VarType::String)
360                 };
361 
362                 let mangling = cursor_mangling(ctx, &cursor);
363                 let var = Var::new(name, mangling, ty, value, is_const);
364 
365                 Ok(ParseResult::New(var, Some(cursor)))
366             }
367             _ => {
368                 /* TODO */
369                 Err(ParseError::Continue)
370             }
371         }
372     }
373 }
374 
375 /// 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)>376 fn parse_macro(
377     ctx: &BindgenContext,
378     cursor: &clang::Cursor,
379 ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> {
380     use cexpr::expr;
381 
382     let cexpr_tokens = cursor.cexpr_tokens();
383 
384     let parser = expr::IdentifierParser::new(ctx.parsed_macros());
385 
386     match parser.macro_definition(&cexpr_tokens) {
387         Ok((_, (id, val))) => Some((id.into(), val)),
388         _ => None,
389     }
390 }
391 
parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64>392 fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> {
393     use cexpr::expr;
394     use cexpr::expr::EvalResult;
395 
396     let cexpr_tokens = cursor.cexpr_tokens();
397 
398     // TODO(emilio): We can try to parse other kinds of literals.
399     match expr::expr(&cexpr_tokens) {
400         Ok((_, EvalResult::Int(Wrapping(val)))) => Some(val),
401         _ => None,
402     }
403 }
404 
get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64>405 fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> {
406     use clang_sys::*;
407     let mut value = None;
408     cursor.visit(|c| {
409         match c.kind() {
410             CXCursor_IntegerLiteral | CXCursor_UnaryOperator => {
411                 value = parse_int_literal_tokens(&c);
412             }
413             CXCursor_UnexposedExpr => {
414                 value = get_integer_literal_from_cursor(&c);
415             }
416             _ => (),
417         }
418         if value.is_some() {
419             CXChildVisit_Break
420         } else {
421             CXChildVisit_Continue
422         }
423     });
424     value
425 }
426