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 | |
15 | use std::io; |
16 | use std::num::Wrapping; |
17 | |
18 | /// The type for a constant variable. |
19 | #[derive (Debug)] |
20 | pub(crate) 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(crate) 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 link name of the variable. |
41 | link_name: Option<String>, |
42 | /// The type of the variable. |
43 | ty: TypeId, |
44 | /// The value of the variable, that needs to be suitable for `ty`. |
45 | val: Option<VarType>, |
46 | /// Whether this variable is const. |
47 | is_const: bool, |
48 | } |
49 | |
50 | impl Var { |
51 | /// Construct a new `Var`. |
52 | pub(crate) fn new( |
53 | name: String, |
54 | mangled_name: Option<String>, |
55 | link_name: Option<String>, |
56 | ty: TypeId, |
57 | val: Option<VarType>, |
58 | is_const: bool, |
59 | ) -> Var { |
60 | assert!(!name.is_empty()); |
61 | Var { |
62 | name, |
63 | mangled_name, |
64 | link_name, |
65 | ty, |
66 | val, |
67 | is_const, |
68 | } |
69 | } |
70 | |
71 | /// Is this variable `const` qualified? |
72 | pub(crate) fn is_const(&self) -> bool { |
73 | self.is_const |
74 | } |
75 | |
76 | /// The value of this constant variable, if any. |
77 | pub(crate) fn val(&self) -> Option<&VarType> { |
78 | self.val.as_ref() |
79 | } |
80 | |
81 | /// Get this variable's type. |
82 | pub(crate) fn ty(&self) -> TypeId { |
83 | self.ty |
84 | } |
85 | |
86 | /// Get this variable's name. |
87 | pub(crate) fn name(&self) -> &str { |
88 | &self.name |
89 | } |
90 | |
91 | /// Get this variable's mangled name. |
92 | pub(crate) fn mangled_name(&self) -> Option<&str> { |
93 | self.mangled_name.as_deref() |
94 | } |
95 | |
96 | /// Get this variable's link name. |
97 | pub fn link_name(&self) -> Option<&str> { |
98 | self.link_name.as_deref() |
99 | } |
100 | } |
101 | |
102 | impl DotAttributes for Var { |
103 | fn dot_attributes<W>( |
104 | &self, |
105 | _ctx: &BindgenContext, |
106 | out: &mut W, |
107 | ) -> io::Result<()> |
108 | where |
109 | W: io::Write, |
110 | { |
111 | if self.is_const { |
112 | writeln!(out, "<tr><td>const</td><td>true</td></tr>" )?; |
113 | } |
114 | |
115 | if let Some(ref mangled: &String) = self.mangled_name { |
116 | writeln!( |
117 | out, |
118 | "<tr><td>mangled name</td><td> {}</td></tr>" , |
119 | mangled |
120 | )?; |
121 | } |
122 | |
123 | Ok(()) |
124 | } |
125 | } |
126 | |
127 | fn default_macro_constant_type(ctx: &BindgenContext, value: i64) -> IntKind { |
128 | if value < 0 || |
129 | ctx.options().default_macro_constant_type == |
130 | MacroTypeVariation::Signed |
131 | { |
132 | if value < i32::MIN as i64 || value > i32::MAX as i64 { |
133 | IntKind::I64 |
134 | } else if !ctx.options().fit_macro_constants || |
135 | value < i16::MIN as i64 || |
136 | value > i16::MAX as i64 |
137 | { |
138 | IntKind::I32 |
139 | } else if value < i8::MIN as i64 || value > i8::MAX as i64 { |
140 | IntKind::I16 |
141 | } else { |
142 | IntKind::I8 |
143 | } |
144 | } else if value > u32::MAX as i64 { |
145 | IntKind::U64 |
146 | } else if !ctx.options().fit_macro_constants || value > u16::MAX as i64 { |
147 | IntKind::U32 |
148 | } else if value > u8::MAX as i64 { |
149 | IntKind::U16 |
150 | } else { |
151 | IntKind::U8 |
152 | } |
153 | } |
154 | |
155 | /// Parses tokens from a CXCursor_MacroDefinition pointing into a function-like |
156 | /// macro, and calls the func_macro callback. |
157 | fn handle_function_macro( |
158 | cursor: &clang::Cursor, |
159 | callbacks: &dyn crate::callbacks::ParseCallbacks, |
160 | ) { |
161 | let is_closing_paren: impl Fn(&ClangToken) -> bool = |t: &ClangToken| { |
162 | // Test cheap token kind before comparing exact spellings. |
163 | t.kind == clang_sys::CXToken_Punctuation && t.spelling() == b")" |
164 | }; |
165 | let tokens: Vec<_> = cursor.tokens().iter().collect(); |
166 | if let Some(boundary: usize) = tokens.iter().position(is_closing_paren) { |
167 | let mut spelled: impl Iterator = tokens.iter().map(ClangToken::spelling); |
168 | // Add 1, to convert index to length. |
169 | let left: impl Iterator = spelled.by_ref().take(boundary + 1); |
170 | let left: Vec = left.collect::<Vec<_>>().concat(); |
171 | if let Ok(left: String) = String::from_utf8(vec:left) { |
172 | let right: Vec<_> = spelled.collect(); |
173 | callbacks.func_macro(&left, &right); |
174 | } |
175 | } |
176 | } |
177 | |
178 | impl ClangSubItemParser for Var { |
179 | fn parse( |
180 | cursor: clang::Cursor, |
181 | ctx: &mut BindgenContext, |
182 | ) -> Result<ParseResult<Self>, ParseError> { |
183 | use cexpr::expr::EvalResult; |
184 | use cexpr::literal::CChar; |
185 | use clang_sys::*; |
186 | match cursor.kind() { |
187 | CXCursor_MacroDefinition => { |
188 | for callbacks in &ctx.options().parse_callbacks { |
189 | match callbacks.will_parse_macro(&cursor.spelling()) { |
190 | MacroParsingBehavior::Ignore => { |
191 | return Err(ParseError::Continue); |
192 | } |
193 | MacroParsingBehavior::Default => {} |
194 | } |
195 | |
196 | if cursor.is_macro_function_like() { |
197 | handle_function_macro(&cursor, callbacks.as_ref()); |
198 | // We handled the macro, skip macro processing below. |
199 | return Err(ParseError::Continue); |
200 | } |
201 | } |
202 | |
203 | let value = parse_macro(ctx, &cursor); |
204 | |
205 | let (id, value) = match value { |
206 | Some(v) => v, |
207 | None => return Err(ParseError::Continue), |
208 | }; |
209 | |
210 | assert!(!id.is_empty(), "Empty macro name?" ); |
211 | |
212 | let previously_defined = ctx.parsed_macro(&id); |
213 | |
214 | // NB: It's important to "note" the macro even if the result is |
215 | // not an integer, otherwise we might loose other kind of |
216 | // derived macros. |
217 | ctx.note_parsed_macro(id.clone(), value.clone()); |
218 | |
219 | if previously_defined { |
220 | let name = String::from_utf8(id).unwrap(); |
221 | duplicated_macro_diagnostic(&name, cursor.location(), ctx); |
222 | return Err(ParseError::Continue); |
223 | } |
224 | |
225 | // NOTE: Unwrapping, here and above, is safe, because the |
226 | // identifier of a token comes straight from clang, and we |
227 | // enforce utf8 there, so we should have already panicked at |
228 | // this point. |
229 | let name = String::from_utf8(id).unwrap(); |
230 | let (type_kind, val) = match value { |
231 | EvalResult::Invalid => return Err(ParseError::Continue), |
232 | EvalResult::Float(f) => { |
233 | (TypeKind::Float(FloatKind::Double), VarType::Float(f)) |
234 | } |
235 | EvalResult::Char(c) => { |
236 | let c = match c { |
237 | CChar::Char(c) => { |
238 | assert_eq!(c.len_utf8(), 1); |
239 | c as u8 |
240 | } |
241 | CChar::Raw(c) => { |
242 | assert!(c <= u8::MAX as u64); |
243 | c as u8 |
244 | } |
245 | }; |
246 | |
247 | (TypeKind::Int(IntKind::U8), VarType::Char(c)) |
248 | } |
249 | EvalResult::Str(val) => { |
250 | let char_ty = Item::builtin_type( |
251 | TypeKind::Int(IntKind::U8), |
252 | true, |
253 | ctx, |
254 | ); |
255 | for callbacks in &ctx.options().parse_callbacks { |
256 | callbacks.str_macro(&name, &val); |
257 | } |
258 | (TypeKind::Pointer(char_ty), VarType::String(val)) |
259 | } |
260 | EvalResult::Int(Wrapping(value)) => { |
261 | let kind = ctx |
262 | .options() |
263 | .last_callback(|c| c.int_macro(&name, value)) |
264 | .unwrap_or_else(|| { |
265 | default_macro_constant_type(ctx, value) |
266 | }); |
267 | |
268 | (TypeKind::Int(kind), VarType::Int(value)) |
269 | } |
270 | }; |
271 | |
272 | let ty = Item::builtin_type(type_kind, true, ctx); |
273 | |
274 | Ok(ParseResult::New( |
275 | Var::new(name, None, None, ty, Some(val), true), |
276 | Some(cursor), |
277 | )) |
278 | } |
279 | CXCursor_VarDecl => { |
280 | let mut name = cursor.spelling(); |
281 | if cursor.linkage() == CXLinkage_External { |
282 | if let Some(nm) = ctx.options().last_callback(|callbacks| { |
283 | callbacks.generated_name_override(ItemInfo { |
284 | name: name.as_str(), |
285 | kind: ItemKind::Var, |
286 | }) |
287 | }) { |
288 | name = nm; |
289 | } |
290 | } |
291 | // No more changes to name |
292 | let name = name; |
293 | |
294 | if name.is_empty() { |
295 | warn!("Empty constant name?" ); |
296 | return Err(ParseError::Continue); |
297 | } |
298 | |
299 | let link_name = ctx.options().last_callback(|callbacks| { |
300 | callbacks.generated_link_name_override(ItemInfo { |
301 | name: name.as_str(), |
302 | kind: ItemKind::Var, |
303 | }) |
304 | }); |
305 | |
306 | let ty = cursor.cur_type(); |
307 | |
308 | // TODO(emilio): do we have to special-case constant arrays in |
309 | // some other places? |
310 | let is_const = ty.is_const() || |
311 | ([CXType_ConstantArray, CXType_IncompleteArray] |
312 | .contains(&ty.kind()) && |
313 | ty.elem_type() |
314 | .map_or(false, |element| element.is_const())); |
315 | |
316 | let ty = match Item::from_ty(&ty, cursor, None, ctx) { |
317 | Ok(ty) => ty, |
318 | Err(e) => { |
319 | assert!( |
320 | matches!(ty.kind(), CXType_Auto | CXType_Unexposed), |
321 | "Couldn't resolve constant type, and it \ |
322 | wasn't an nondeductible auto type or unexposed \ |
323 | type!" |
324 | ); |
325 | return Err(e); |
326 | } |
327 | }; |
328 | |
329 | // Note: Ty might not be totally resolved yet, see |
330 | // tests/headers/inner_const.hpp |
331 | // |
332 | // That's fine because in that case we know it's not a literal. |
333 | let canonical_ty = ctx |
334 | .safe_resolve_type(ty) |
335 | .and_then(|t| t.safe_canonical_type(ctx)); |
336 | |
337 | let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); |
338 | let is_float = canonical_ty.map_or(false, |t| t.is_float()); |
339 | |
340 | // TODO: We could handle `char` more gracefully. |
341 | // TODO: Strings, though the lookup is a bit more hard (we need |
342 | // to look at the canonical type of the pointee too, and check |
343 | // is char, u8, or i8 I guess). |
344 | let value = if is_integer { |
345 | let kind = match *canonical_ty.unwrap().kind() { |
346 | TypeKind::Int(kind) => kind, |
347 | _ => unreachable!(), |
348 | }; |
349 | |
350 | let mut val = cursor.evaluate().and_then(|v| v.as_int()); |
351 | if val.is_none() || !kind.signedness_matches(val.unwrap()) { |
352 | val = get_integer_literal_from_cursor(&cursor); |
353 | } |
354 | |
355 | val.map(|val| { |
356 | if kind == IntKind::Bool { |
357 | VarType::Bool(val != 0) |
358 | } else { |
359 | VarType::Int(val) |
360 | } |
361 | }) |
362 | } else if is_float { |
363 | cursor |
364 | .evaluate() |
365 | .and_then(|v| v.as_double()) |
366 | .map(VarType::Float) |
367 | } else { |
368 | cursor |
369 | .evaluate() |
370 | .and_then(|v| v.as_literal_string()) |
371 | .map(VarType::String) |
372 | }; |
373 | |
374 | let mangling = cursor_mangling(ctx, &cursor); |
375 | let var = |
376 | Var::new(name, mangling, link_name, ty, value, is_const); |
377 | |
378 | Ok(ParseResult::New(var, Some(cursor))) |
379 | } |
380 | _ => { |
381 | /* TODO */ |
382 | Err(ParseError::Continue) |
383 | } |
384 | } |
385 | } |
386 | } |
387 | |
388 | /// This function uses a [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] to parse each |
389 | /// macro that cannot be parsed by the normal bindgen process for `#define`s. |
390 | /// |
391 | /// To construct the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit], first precompiled |
392 | /// headers are generated for all input headers. An empty temporary `.c` file is generated to pass |
393 | /// to the translation unit. On the evaluation of each macro, a [`String`] is generated with the |
394 | /// new contents of the empty file and passed in for reparsing. The precompiled headers and |
395 | /// preservation of the [`FallbackTranslationUnit`][clang::FallbackTranslationUnit] across macro |
396 | /// evaluations are both optimizations that have significantly improved the performance. |
397 | fn parse_macro_clang_fallback( |
398 | ctx: &mut BindgenContext, |
399 | cursor: &clang::Cursor, |
400 | ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> { |
401 | if !ctx.options().clang_macro_fallback { |
402 | return None; |
403 | } |
404 | |
405 | let ftu = ctx.try_ensure_fallback_translation_unit()?; |
406 | let contents = format!("int main() {{ {}; }}" , cursor.spelling(),); |
407 | ftu.reparse(&contents).ok()?; |
408 | // Children of root node of AST |
409 | let root_children = ftu.translation_unit().cursor().collect_children(); |
410 | // Last child in root is function declaration |
411 | // Should be FunctionDecl |
412 | let main_func = root_children.last()?; |
413 | // Children should all be statements in function declaration |
414 | let all_stmts = main_func.collect_children(); |
415 | // First child in all_stmts should be the statement containing the macro to evaluate |
416 | // Should be CompoundStmt |
417 | let macro_stmt = all_stmts.first()?; |
418 | // Children should all be expressions from the compound statement |
419 | let paren_exprs = macro_stmt.collect_children(); |
420 | // First child in all_exprs is the expression utilizing the given macro to be evaluated |
421 | // Should be ParenExpr |
422 | let paren = paren_exprs.first()?; |
423 | |
424 | Some(( |
425 | cursor.spelling().into_bytes(), |
426 | cexpr::expr::EvalResult::Int(Wrapping(paren.evaluate()?.as_int()?)), |
427 | )) |
428 | } |
429 | |
430 | /// Try and parse a macro using all the macros parsed until now. |
431 | fn parse_macro( |
432 | ctx: &mut BindgenContext, |
433 | cursor: &clang::Cursor, |
434 | ) -> Option<(Vec<u8>, cexpr::expr::EvalResult)> { |
435 | use cexpr::expr; |
436 | |
437 | let cexpr_tokens: Vec = cursor.cexpr_tokens(); |
438 | |
439 | let parser: IdentifierParser<'_> = expr::IdentifierParser::new(identifiers:ctx.parsed_macros()); |
440 | |
441 | match parser.macro_definition(&cexpr_tokens) { |
442 | Ok((_, (id: &[u8], val: EvalResult))) => Some((id.into(), val)), |
443 | _ => parse_macro_clang_fallback(ctx, cursor), |
444 | } |
445 | } |
446 | |
447 | fn parse_int_literal_tokens(cursor: &clang::Cursor) -> Option<i64> { |
448 | use cexpr::expr; |
449 | use cexpr::expr::EvalResult; |
450 | |
451 | let cexpr_tokens: Vec = cursor.cexpr_tokens(); |
452 | |
453 | // TODO(emilio): We can try to parse other kinds of literals. |
454 | match expr::expr(&cexpr_tokens) { |
455 | Ok((_, EvalResult::Int(Wrapping(val: i64)))) => Some(val), |
456 | _ => None, |
457 | } |
458 | } |
459 | |
460 | fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> { |
461 | use clang_sys::*; |
462 | let mut value: Option = None; |
463 | cursor.visit(|c: Cursor| { |
464 | match c.kind() { |
465 | CXCursor_IntegerLiteral | CXCursor_UnaryOperator => { |
466 | value = parse_int_literal_tokens(&c); |
467 | } |
468 | CXCursor_UnexposedExpr => { |
469 | value = get_integer_literal_from_cursor(&c); |
470 | } |
471 | _ => (), |
472 | } |
473 | if value.is_some() { |
474 | CXChildVisit_Break |
475 | } else { |
476 | CXChildVisit_Continue |
477 | } |
478 | }); |
479 | value |
480 | } |
481 | |
482 | fn duplicated_macro_diagnostic( |
483 | macro_name: &str, |
484 | _location: crate::clang::SourceLocation, |
485 | _ctx: &BindgenContext, |
486 | ) { |
487 | warn!("Duplicated macro definition: {}" , macro_name); |
488 | |
489 | #[cfg (feature = "experimental" )] |
490 | // FIXME (pvdrz & amanjeev): This diagnostic message shows way too often to be actually |
491 | // useful. We have to change the logic where this function is called to be able to emit this |
492 | // message only when the duplication is an actual issue. |
493 | // |
494 | // If I understood correctly, `bindgen` ignores all `#undef` directives. Meaning that this: |
495 | // ```c |
496 | // #define FOO 1 |
497 | // #undef FOO |
498 | // #define FOO 2 |
499 | // ``` |
500 | // |
501 | // Will trigger this message even though there's nothing wrong with it. |
502 | #[allow (clippy::overly_complex_bool_expr)] |
503 | if false && _ctx.options().emit_diagnostics { |
504 | use crate::diagnostics::{get_line, Diagnostic, Level, Slice}; |
505 | use std::borrow::Cow; |
506 | |
507 | let mut slice = Slice::default(); |
508 | let mut source = Cow::from(macro_name); |
509 | |
510 | let (file, line, col, _) = _location.location(); |
511 | if let Some(filename) = file.name() { |
512 | if let Ok(Some(code)) = get_line(&filename, line) { |
513 | source = code.into(); |
514 | } |
515 | slice.with_location(filename, line, col); |
516 | } |
517 | |
518 | slice.with_source(source); |
519 | |
520 | Diagnostic::default() |
521 | .with_title("Duplicated macro definition." , Level::Warn) |
522 | .add_slice(slice) |
523 | .add_annotation("This macro had a duplicate." , Level::Note) |
524 | .display(); |
525 | } |
526 | } |
527 | |