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