Skip to main content

nbindgen/bindgen/ir/
constant.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::io::Write;
8
9use syn::{self, UnOp};
10
11use crate::bindgen::config::Config;
12use crate::bindgen::dependencies::Dependencies;
13use crate::bindgen::ir::{
14    AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
15    Struct, ToCondition, Type,
16};
17use crate::bindgen::library::Library;
18use crate::bindgen::writer::SourceWriter;
19use crate::bindgen::Bindings;
20
21#[derive(Debug, Clone)]
22pub enum Literal {
23    Expr(String),
24    Path(String),
25    PostfixUnaryOp {
26        op: &'static str,
27        value: Box<Literal>,
28    },
29    BinOp {
30        left: Box<Literal>,
31        op: &'static str,
32        right: Box<Literal>,
33    },
34    Struct {
35        path: Path,
36        export_name: String,
37        fields: HashMap<String, Literal>,
38    },
39}
40
41impl Literal {
42    fn replace_self_with(&mut self, self_ty: &Path) {
43        match *self {
44            Literal::PostfixUnaryOp { .. }
45            | Literal::BinOp { .. }
46            | Literal::Expr(..)
47            | Literal::Path(..) => {}
48            Literal::Struct {
49                ref mut path,
50                ref mut export_name,
51                ref mut fields,
52            } => {
53                if path.replace_self_with(self_ty) {
54                    *export_name = self_ty.name().to_owned();
55                }
56                for (ref _name, ref mut expr) in fields {
57                    expr.replace_self_with(self_ty);
58                }
59            }
60        }
61    }
62
63    fn is_valid(&self, bindings: &Bindings) -> bool {
64        match *self {
65            Literal::Expr(..) => true,
66            Literal::Path(..) => true,
67            Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
68            Literal::BinOp {
69                ref left,
70                ref right,
71                ..
72            } => left.is_valid(bindings) && right.is_valid(bindings),
73            Literal::Struct { ref path, .. } => bindings.struct_exists(path),
74        }
75    }
76}
77
78impl Literal {
79    pub fn rename_for_config(&mut self, config: &Config) {
80        match self {
81            Literal::Struct {
82                ref mut export_name,
83                fields,
84                ..
85            } => {
86                config.export.rename(export_name);
87                for (_, lit) in fields {
88                    lit.rename_for_config(config);
89                }
90            }
91            Literal::Path(ref mut name) => {
92                config.export.rename(name);
93            }
94            Literal::PostfixUnaryOp { ref mut value, .. } => {
95                value.rename_for_config(config);
96            }
97            Literal::BinOp {
98                ref mut left,
99                ref mut right,
100                ..
101            } => {
102                left.rename_for_config(config);
103                right.rename_for_config(config);
104            }
105            Literal::Expr(_) => {}
106        }
107    }
108
109    // Translate from full blown `syn::Expr` into a simpler `Literal` type
110    pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
111        match *expr {
112            // Match binary expressions of the form `a * b`
113            syn::Expr::Binary(ref bin_expr) => {
114                let l = Self::load(&bin_expr.left)?;
115                let r = Self::load(&bin_expr.right)?;
116                let op = match bin_expr.op {
117                    syn::BinOp::Add(..) => "+",
118                    syn::BinOp::Sub(..) => "-",
119                    syn::BinOp::Mul(..) => "*",
120                    syn::BinOp::Div(..) => "div",
121                    syn::BinOp::Rem(..) => "mod",
122                    syn::BinOp::And(..) => "and",
123                    syn::BinOp::Or(..) => "or",
124                    syn::BinOp::BitXor(..) => "xor",
125                    syn::BinOp::BitAnd(..) => "and",
126                    syn::BinOp::BitOr(..) => "or",
127                    syn::BinOp::Shl(..) => "shl",
128                    syn::BinOp::Shr(..) => "shr",
129                    syn::BinOp::Eq(..) => "==",
130                    syn::BinOp::Lt(..) => "<",
131                    syn::BinOp::Le(..) => "<=",
132                    syn::BinOp::Ne(..) => "!=",
133                    syn::BinOp::Ge(..) => ">=",
134                    syn::BinOp::Gt(..) => ">",
135                    syn::BinOp::AddEq(..) => "+=",
136                    syn::BinOp::SubEq(..) => "-=",
137                    syn::BinOp::MulEq(..) => "*=",
138                    syn::BinOp::DivEq(..) => "/=",
139                    syn::BinOp::RemEq(..) => "%=",
140                    syn::BinOp::BitXorEq(..) => "^=",
141                    syn::BinOp::BitAndEq(..) => "&=",
142                    syn::BinOp::BitOrEq(..) => "|=",
143                    syn::BinOp::ShlEq(..) => ">>=",
144                    syn::BinOp::ShrEq(..) => "<<=",
145                };
146                Ok(Literal::BinOp {
147                    left: Box::new(l),
148                    op,
149                    right: Box::new(r),
150                })
151            }
152
153            // Match literals like "one", 'a', 32 etc
154            syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
155                match lit {
156                    syn::Lit::Str(ref value) => {
157                        Ok(Literal::Expr(format!("u8\"{}\"", value.value())))
158                    }
159                    syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
160                    syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
161                        0..=255 => format!("'{}'", value.value().escape_default()),
162                        other_code => format!(r"L'\u{:X}'", other_code),
163                    })),
164                    syn::Lit::Int(ref value) => {
165                        Ok(Literal::Expr(value.base10_digits().to_string()))
166                    }
167                    syn::Lit::Float(ref value) => {
168                        Ok(Literal::Expr(value.base10_digits().to_string()))
169                    }
170                    syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
171                    // TODO: Add support for byte string and Verbatim
172                    _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
173                }
174            }
175
176            syn::Expr::Struct(syn::ExprStruct {
177                ref path,
178                ref fields,
179                ..
180            }) => {
181                let struct_name = path.segments[0].ident.to_string();
182                let mut field_map = HashMap::<String, Literal>::default();
183                for field in fields {
184                    let ident = match field.member {
185                        syn::Member::Named(ref name) => name.to_string(),
186                        syn::Member::Unnamed(ref index) => format!("F{}", index.index),
187                    };
188                    let key = ident.to_string();
189                    let value = Literal::load(&field.expr)?;
190                    field_map.insert(key, value);
191                }
192                Ok(Literal::Struct {
193                    path: Path::new(struct_name.clone()),
194                    export_name: struct_name,
195                    fields: field_map,
196                })
197            }
198
199            syn::Expr::Unary(syn::ExprUnary {
200                ref op, ref expr, ..
201            }) => match *op {
202                UnOp::Neg(_) => {
203                    let val = Self::load(expr)?;
204                    Ok(Literal::PostfixUnaryOp {
205                        op: "-",
206                        value: Box::new(val),
207                    })
208                }
209                _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
210            },
211
212            // Match identifiers, like `5 << SHIFT`
213            syn::Expr::Path(syn::ExprPath {
214                path: syn::Path { ref segments, .. },
215                ..
216            }) => {
217                // Handle only the simplest identifiers and error for anything else.
218                if segments.len() == 1 {
219                    Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
220                } else {
221                    Err(format!("Unsupported path expression. {:?}", *segments))
222                }
223            }
224
225            syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
226
227            _ => Err(format!("Unsupported expression. {:?}", *expr)),
228        }
229    }
230
231    fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
232        match self {
233            Literal::Expr(v) => write!(out, "{}", v),
234            Literal::Path(v) => write!(out, "{}", v),
235            Literal::PostfixUnaryOp { op, ref value } => {
236                write!(out, "{}", op);
237                value.write(config, out);
238            }
239            Literal::BinOp {
240                ref left,
241                op,
242                ref right,
243            } => {
244                write!(out, "(");
245                left.write(config, out);
246                write!(out, " {} ", op);
247                right.write(config, out);
248                write!(out, ")");
249            }
250            Literal::Struct {
251                export_name,
252                fields,
253                path,
254            } => {
255                write!(out, "{}(", export_name);
256
257                let mut is_first_field = true;
258                // In C++, same order as defined is required.
259                let ordered_fields = out.bindings().struct_field_names(path);
260                for ordered_key in ordered_fields.iter() {
261                    if let Some(ref lit) = fields.get(ordered_key) {
262                        if !is_first_field {
263                            write!(out, ", ");
264                        } else {
265                            is_first_field = false;
266                        }
267                        write!(out, "{}: ", ordered_key);
268
269                        lit.write(config, out);
270                    }
271                }
272                write!(out, ")");
273            }
274        }
275    }
276}
277
278#[derive(Debug, Clone)]
279pub struct Constant {
280    pub path: Path,
281    pub export_name: String,
282    pub ty: Type,
283    pub value: Literal,
284    pub cfg: Option<Cfg>,
285    pub annotations: AnnotationSet,
286    pub documentation: Documentation,
287    pub associated_to: Option<Path>,
288}
289
290fn can_handle(ty: &Type, expr: &syn::Expr) -> bool {
291    if ty.is_primitive_or_ptr_primitive() {
292        return true;
293    }
294    match *expr {
295        syn::Expr::Struct(_) => true,
296        _ => false,
297    }
298}
299
300impl Constant {
301    pub fn load(
302        path: Path,
303        mod_cfg: Option<&Cfg>,
304        ty: &syn::Type,
305        expr: &syn::Expr,
306        attrs: &[syn::Attribute],
307        associated_to: Option<Path>,
308    ) -> Result<Constant, String> {
309        let ty = Type::load(ty)?;
310        let mut ty = match ty {
311            Some(ty) => ty,
312            None => {
313                return Err("Cannot have a zero sized const definition.".to_owned());
314            }
315        };
316
317        if !can_handle(&ty, expr) {
318            return Err("Unhandled const definition".to_owned());
319        }
320
321        let mut lit = Literal::load(&expr)?;
322
323        if let Some(ref associated_to) = associated_to {
324            ty.replace_self_with(associated_to);
325            lit.replace_self_with(associated_to);
326        }
327
328        Ok(Constant::new(
329            path,
330            ty,
331            lit,
332            Cfg::append(mod_cfg, Cfg::load(attrs)),
333            AnnotationSet::load(attrs)?,
334            Documentation::load(attrs),
335            associated_to,
336        ))
337    }
338
339    pub fn new(
340        path: Path,
341        ty: Type,
342        value: Literal,
343        cfg: Option<Cfg>,
344        annotations: AnnotationSet,
345        documentation: Documentation,
346        associated_to: Option<Path>,
347    ) -> Self {
348        let export_name = path.name().to_owned();
349        Self {
350            path,
351            export_name,
352            ty,
353            value,
354            cfg,
355            annotations,
356            documentation,
357            associated_to,
358        }
359    }
360}
361
362impl Item for Constant {
363    fn path(&self) -> &Path {
364        &self.path
365    }
366
367    fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
368        self.ty.add_dependencies(library, out);
369    }
370
371    fn export_name(&self) -> &str {
372        &self.export_name
373    }
374
375    fn cfg(&self) -> Option<&Cfg> {
376        self.cfg.as_ref()
377    }
378
379    fn annotations(&self) -> &AnnotationSet {
380        &self.annotations
381    }
382
383    fn annotations_mut(&mut self) -> &mut AnnotationSet {
384        &mut self.annotations
385    }
386
387    fn container(&self) -> ItemContainer {
388        ItemContainer::Constant(self.clone())
389    }
390
391    fn rename_for_config(&mut self, config: &Config) {
392        if self.associated_to.is_none() {
393            config.export.rename(&mut self.export_name);
394        }
395        self.value.rename_for_config(config);
396        self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
397    }
398}
399
400impl Constant {
401    pub fn write<F: Write>(
402        &self,
403        config: &Config,
404        out: &mut SourceWriter<F>,
405        associated_to_struct: Option<&Struct>,
406    ) {
407        if let Some(assoc) = associated_to_struct {
408            if assoc.is_generic() {
409                return; // Not tested / implemented yet, so bail out.
410            }
411        }
412
413        if !self.value.is_valid(out.bindings()) {
414            return;
415        }
416
417        let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
418
419        let in_body = associated_to_struct.is_some() && false && !associated_to_transparent;
420
421        let condition = (&self.cfg).to_condition(config);
422        condition.write_before(config, out);
423
424        let name = if in_body {
425            Cow::Owned(format!(
426                "{}::{}",
427                associated_to_struct.unwrap().export_name(),
428                self.export_name(),
429            ))
430        } else if self.associated_to.is_none() {
431            Cow::Borrowed(self.export_name())
432        } else {
433            let associated_name = match associated_to_struct {
434                Some(s) => Cow::Borrowed(s.export_name()),
435                None => {
436                    let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
437                    config.export.rename(&mut name);
438                    Cow::Owned(name)
439                }
440            };
441
442            Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
443        };
444
445        let value = match self.value {
446            Literal::Struct {
447                ref fields,
448                ref path,
449                ..
450            } if out.bindings().struct_is_transparent(path) => &fields.iter().next().unwrap().1,
451            _ => &self.value,
452        };
453
454        write!(out, "const {}* = ", name);
455        value.write(config, out);
456        condition.write_after(config, out);
457    }
458}