| 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 | |