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`. |
50 | 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? |
68 | pub fn is_const(&self) -> bool { |
69 | self.is_const |
70 | } |
71 | |
72 | /// The value of this constant variable, if any. |
73 | pub fn val(&self) -> Option<&VarType> { |
74 | self.val.as_ref() |
75 | } |
76 | |
77 | /// Get this variable's type. |
78 | pub fn ty(&self) -> TypeId { |
79 | self.ty |
80 | } |
81 | |
82 | /// Get this variable's name. |
83 | pub fn name(&self) -> &str { |
84 | &self.name |
85 | } |
86 | |
87 | /// Get this variable's mangled name. |
88 | pub fn mangled_name(&self) -> Option<&str> { |
89 | self.mangled_name.as_deref() |
90 | } |
91 | } |
92 | |
93 | impl DotAttributes for Var { |
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: &String) = 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 | 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. |
152 | fn handle_function_macro( |
153 | cursor: &clang::Cursor, |
154 | callbacks: &dyn crate::callbacks::ParseCallbacks, |
155 | ) { |
156 | let is_closing_paren: impl Fn(&ClangToken) -> bool = |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: usize) = tokens.iter().position(is_closing_paren) { |
162 | let mut spelled: impl Iterator = tokens.iter().map(ClangToken::spelling); |
163 | // Add 1, to convert index to length. |
164 | let left: impl Iterator = spelled.by_ref().take(boundary + 1); |
165 | let left: Vec = left.collect::<Vec<_>>().concat(); |
166 | if let Ok(left: String) = String::from_utf8(vec:left) { |
167 | let right: Vec<_> = spelled.collect(); |
168 | callbacks.func_macro(&left, &right); |
169 | } |
170 | } |
171 | } |
172 | |
173 | impl ClangSubItemParser for Var { |
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. |
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: Vec = cursor.cexpr_tokens(); |
383 | |
384 | let parser: IdentifierParser<'_> = expr::IdentifierParser::new(identifiers:ctx.parsed_macros()); |
385 | |
386 | match parser.macro_definition(&cexpr_tokens) { |
387 | Ok((_, (id: &[u8], val: EvalResult))) => Some((id.into(), val)), |
388 | _ => None, |
389 | } |
390 | } |
391 | |
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: Vec = 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: i64)))) => Some(val), |
401 | _ => None, |
402 | } |
403 | } |
404 | |
405 | fn get_integer_literal_from_cursor(cursor: &clang::Cursor) -> Option<i64> { |
406 | use clang_sys::*; |
407 | let mut value: Option = None; |
408 | cursor.visit(|c: Cursor| { |
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 | |