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 
5 use std::borrow::Cow;
6 use std::collections::HashMap;
7 use std::io::Write;
8 
9 use syn::{self, UnOp};
10 
11 use crate::bindgen::config::{Config, Language};
12 use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
13 use crate::bindgen::dependencies::Dependencies;
14 use crate::bindgen::ir::{
15     AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
16     Struct, ToCondition, Type,
17 };
18 use crate::bindgen::library::Library;
19 use crate::bindgen::writer::{Source, SourceWriter};
20 use crate::bindgen::Bindings;
21 
22 #[derive(Debug, Clone)]
23 pub enum Literal {
24     Expr(String),
25     Path(String),
26     PostfixUnaryOp {
27         op: &'static str,
28         value: Box<Literal>,
29     },
30     BinOp {
31         left: Box<Literal>,
32         op: &'static str,
33         right: Box<Literal>,
34     },
35     Struct {
36         path: Path,
37         export_name: String,
38         fields: HashMap<String, Literal>,
39     },
40     Cast {
41         ty: Type,
42         value: Box<Literal>,
43     },
44 }
45 
46 impl Literal {
replace_self_with(&mut self, self_ty: &Path)47     fn replace_self_with(&mut self, self_ty: &Path) {
48         match *self {
49             Literal::PostfixUnaryOp { ref mut value, .. } => {
50                 value.replace_self_with(self_ty);
51             }
52             Literal::BinOp {
53                 ref mut left,
54                 ref mut right,
55                 ..
56             } => {
57                 left.replace_self_with(self_ty);
58                 right.replace_self_with(self_ty);
59             }
60             Literal::Struct {
61                 ref mut path,
62                 ref mut export_name,
63                 ref mut fields,
64             } => {
65                 if path.replace_self_with(self_ty) {
66                     *export_name = self_ty.name().to_owned();
67                 }
68                 for ref mut expr in fields.values_mut() {
69                     expr.replace_self_with(self_ty);
70                 }
71             }
72             Literal::Cast {
73                 ref mut ty,
74                 ref mut value,
75             } => {
76                 ty.replace_self_with(self_ty);
77                 value.replace_self_with(self_ty);
78             }
79             Literal::Expr(..) | Literal::Path(..) => {}
80         }
81     }
82 
is_valid(&self, bindings: &Bindings) -> bool83     fn is_valid(&self, bindings: &Bindings) -> bool {
84         match *self {
85             Literal::Expr(..) => true,
86             Literal::Path(..) => true,
87             Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
88             Literal::BinOp {
89                 ref left,
90                 ref right,
91                 ..
92             } => left.is_valid(bindings) && right.is_valid(bindings),
93             Literal::Struct { ref path, .. } => bindings.struct_exists(path),
94             Literal::Cast { ref value, .. } => value.is_valid(bindings),
95         }
96     }
97 
uses_only_primitive_types(&self) -> bool98     pub fn uses_only_primitive_types(&self) -> bool {
99         match self {
100             Literal::Expr(..) => true,
101             Literal::Path(..) => true,
102             Literal::PostfixUnaryOp { ref value, .. } => value.uses_only_primitive_types(),
103             Literal::BinOp {
104                 ref left,
105                 ref right,
106                 ..
107             } => left.uses_only_primitive_types() & right.uses_only_primitive_types(),
108             Literal::Struct { .. } => false,
109             Literal::Cast { ref value, ref ty } => {
110                 value.uses_only_primitive_types() && ty.is_primitive_or_ptr_primitive()
111             }
112         }
113     }
114 }
115 
116 impl Literal {
rename_for_config(&mut self, config: &Config)117     pub fn rename_for_config(&mut self, config: &Config) {
118         match self {
119             Literal::Struct {
120                 ref mut export_name,
121                 fields,
122                 ..
123             } => {
124                 config.export.rename(export_name);
125                 for lit in fields.values_mut() {
126                     lit.rename_for_config(config);
127                 }
128             }
129             Literal::Path(ref mut name) => {
130                 config.export.rename(name);
131             }
132             Literal::PostfixUnaryOp { ref mut value, .. } => {
133                 value.rename_for_config(config);
134             }
135             Literal::BinOp {
136                 ref mut left,
137                 ref mut right,
138                 ..
139             } => {
140                 left.rename_for_config(config);
141                 right.rename_for_config(config);
142             }
143             Literal::Expr(_) => {}
144             Literal::Cast {
145                 ref mut ty,
146                 ref mut value,
147             } => {
148                 ty.rename_for_config(config, &GenericParams::default());
149                 value.rename_for_config(config);
150             }
151         }
152     }
153 
154     // Translate from full blown `syn::Expr` into a simpler `Literal` type
load(expr: &syn::Expr) -> Result<Literal, String>155     pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
156         match *expr {
157             // Match binary expressions of the form `a * b`
158             syn::Expr::Binary(ref bin_expr) => {
159                 let l = Self::load(&bin_expr.left)?;
160                 let r = Self::load(&bin_expr.right)?;
161                 let op = match bin_expr.op {
162                     syn::BinOp::Add(..) => "+",
163                     syn::BinOp::Sub(..) => "-",
164                     syn::BinOp::Mul(..) => "*",
165                     syn::BinOp::Div(..) => "/",
166                     syn::BinOp::Rem(..) => "%",
167                     syn::BinOp::And(..) => "&&",
168                     syn::BinOp::Or(..) => "||",
169                     syn::BinOp::BitXor(..) => "^",
170                     syn::BinOp::BitAnd(..) => "&",
171                     syn::BinOp::BitOr(..) => "|",
172                     syn::BinOp::Shl(..) => "<<",
173                     syn::BinOp::Shr(..) => ">>",
174                     syn::BinOp::Eq(..) => "==",
175                     syn::BinOp::Lt(..) => "<",
176                     syn::BinOp::Le(..) => "<=",
177                     syn::BinOp::Ne(..) => "!=",
178                     syn::BinOp::Ge(..) => ">=",
179                     syn::BinOp::Gt(..) => ">",
180                     syn::BinOp::AddEq(..) => "+=",
181                     syn::BinOp::SubEq(..) => "-=",
182                     syn::BinOp::MulEq(..) => "*=",
183                     syn::BinOp::DivEq(..) => "/=",
184                     syn::BinOp::RemEq(..) => "%=",
185                     syn::BinOp::BitXorEq(..) => "^=",
186                     syn::BinOp::BitAndEq(..) => "&=",
187                     syn::BinOp::BitOrEq(..) => "|=",
188                     syn::BinOp::ShlEq(..) => ">>=",
189                     syn::BinOp::ShrEq(..) => "<<=",
190                 };
191                 Ok(Literal::BinOp {
192                     left: Box::new(l),
193                     op,
194                     right: Box::new(r),
195                 })
196             }
197 
198             // Match literals like true, 'a', 32 etc
199             syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
200                 match lit {
201                     syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
202                     syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
203                         0..=255 => format!("'{}'", value.value().escape_default()),
204                         other_code => format!(r"U'\U{:08X}'", other_code),
205                     })),
206                     syn::Lit::Int(ref value) => {
207                         if value.base10_parse::<i64>().is_err() {
208                             Ok(Literal::Expr(format!("{}ULL", value.base10_digits())))
209                         } else {
210                             Ok(Literal::Expr(value.base10_digits().to_string()))
211                         }
212                     }
213                     syn::Lit::Float(ref value) => {
214                         Ok(Literal::Expr(value.base10_digits().to_string()))
215                     }
216                     syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
217                     // TODO: Add support for byte string and Verbatim
218                     _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
219                 }
220             }
221 
222             syn::Expr::Struct(syn::ExprStruct {
223                 ref path,
224                 ref fields,
225                 ..
226             }) => {
227                 let struct_name = path.segments[0].ident.to_string();
228                 let mut field_map = HashMap::<String, Literal>::default();
229                 for field in fields {
230                     let ident = match field.member {
231                         syn::Member::Named(ref name) => name.to_string(),
232                         syn::Member::Unnamed(ref index) => format!("_{}", index.index),
233                     };
234                     let key = ident.to_string();
235                     let value = Literal::load(&field.expr)?;
236                     field_map.insert(key, value);
237                 }
238                 Ok(Literal::Struct {
239                     path: Path::new(struct_name.clone()),
240                     export_name: struct_name,
241                     fields: field_map,
242                 })
243             }
244 
245             syn::Expr::Unary(syn::ExprUnary {
246                 ref op, ref expr, ..
247             }) => match *op {
248                 UnOp::Neg(_) => {
249                     let val = Self::load(expr)?;
250                     Ok(Literal::PostfixUnaryOp {
251                         op: "-",
252                         value: Box::new(val),
253                     })
254                 }
255                 _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
256             },
257 
258             // Match identifiers, like `5 << SHIFT`
259             syn::Expr::Path(syn::ExprPath {
260                 path: syn::Path { ref segments, .. },
261                 ..
262             }) => {
263                 // Handle only the simplest identifiers and error for anything else.
264                 if segments.len() == 1 {
265                     Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
266                 } else {
267                     Err(format!("Unsupported path expression. {:?}", *segments))
268                 }
269             }
270 
271             syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
272 
273             syn::Expr::Cast(syn::ExprCast {
274                 ref expr, ref ty, ..
275             }) => {
276                 let val = Self::load(expr)?;
277                 match Type::load(ty)? {
278                     Some(ty) => Ok(Literal::Cast {
279                         ty,
280                         value: Box::new(val),
281                     }),
282                     None => Err("Cannot cast to zero sized type.".to_owned()),
283                 }
284             }
285 
286             _ => Err(format!("Unsupported expression. {:?}", *expr)),
287         }
288     }
289 
write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>)290     pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
291         match self {
292             Literal::Expr(v) => match (&**v, config.language) {
293                 ("true", Language::Cython) => write!(out, "True"),
294                 ("false", Language::Cython) => write!(out, "False"),
295                 (v, _) => write!(out, "{}", v),
296             },
297             Literal::Path(v) => write!(out, "{}", v),
298             Literal::PostfixUnaryOp { op, ref value } => {
299                 write!(out, "{}", op);
300                 value.write(config, out);
301             }
302             Literal::BinOp {
303                 ref left,
304                 op,
305                 ref right,
306             } => {
307                 write!(out, "(");
308                 left.write(config, out);
309                 write!(out, " {} ", op);
310                 right.write(config, out);
311                 write!(out, ")");
312             }
313             Literal::Cast { ref ty, ref value } => {
314                 out.write(if config.language == Language::Cython {
315                     "<"
316                 } else {
317                     "("
318                 });
319                 ty.write(config, out);
320                 out.write(if config.language == Language::Cython {
321                     ">"
322                 } else {
323                     ")"
324                 });
325                 value.write(config, out);
326             }
327             Literal::Struct {
328                 export_name,
329                 fields,
330                 path,
331             } => {
332                 match config.language {
333                     Language::C => write!(out, "({})", export_name),
334                     Language::Cxx => write!(out, "{}", export_name),
335                     Language::Cython => write!(out, "<{}>", export_name),
336                 }
337 
338                 write!(out, "{{ ");
339                 let mut is_first_field = true;
340                 // In C++, same order as defined is required.
341                 let ordered_fields = out.bindings().struct_field_names(path);
342                 for ordered_key in ordered_fields.iter() {
343                     if let Some(lit) = fields.get(ordered_key) {
344                         if !is_first_field {
345                             write!(out, ", ");
346                         } else {
347                             is_first_field = false;
348                         }
349                         match config.language {
350                             Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
351                             Language::C => write!(out, ".{} = ", ordered_key),
352                             Language::Cython => {}
353                         }
354                         lit.write(config, out);
355                     }
356                 }
357                 write!(out, " }}");
358             }
359         }
360     }
361 }
362 
363 #[derive(Debug, Clone)]
364 pub struct Constant {
365     pub path: Path,
366     pub export_name: String,
367     pub ty: Type,
368     pub value: Literal,
369     pub cfg: Option<Cfg>,
370     pub annotations: AnnotationSet,
371     pub documentation: Documentation,
372     pub associated_to: Option<Path>,
373 }
374 
375 impl Constant {
load( path: Path, mod_cfg: Option<&Cfg>, ty: &syn::Type, expr: &syn::Expr, attrs: &[syn::Attribute], associated_to: Option<Path>, ) -> Result<Constant, String>376     pub fn load(
377         path: Path,
378         mod_cfg: Option<&Cfg>,
379         ty: &syn::Type,
380         expr: &syn::Expr,
381         attrs: &[syn::Attribute],
382         associated_to: Option<Path>,
383     ) -> Result<Constant, String> {
384         let ty = Type::load(ty)?;
385         let mut ty = match ty {
386             Some(ty) => ty,
387             None => {
388                 return Err("Cannot have a zero sized const definition.".to_owned());
389             }
390         };
391 
392         let mut lit = Literal::load(expr)?;
393 
394         if let Some(ref associated_to) = associated_to {
395             ty.replace_self_with(associated_to);
396             lit.replace_self_with(associated_to);
397         }
398 
399         Ok(Constant::new(
400             path,
401             ty,
402             lit,
403             Cfg::append(mod_cfg, Cfg::load(attrs)),
404             AnnotationSet::load(attrs)?,
405             Documentation::load(attrs),
406             associated_to,
407         ))
408     }
409 
new( path: Path, ty: Type, value: Literal, cfg: Option<Cfg>, annotations: AnnotationSet, documentation: Documentation, associated_to: Option<Path>, ) -> Self410     pub fn new(
411         path: Path,
412         ty: Type,
413         value: Literal,
414         cfg: Option<Cfg>,
415         annotations: AnnotationSet,
416         documentation: Documentation,
417         associated_to: Option<Path>,
418     ) -> Self {
419         let export_name = path.name().to_owned();
420         Self {
421             path,
422             export_name,
423             ty,
424             value,
425             cfg,
426             annotations,
427             documentation,
428             associated_to,
429         }
430     }
431 
uses_only_primitive_types(&self) -> bool432     pub fn uses_only_primitive_types(&self) -> bool {
433         self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
434     }
435 }
436 
437 impl Item for Constant {
path(&self) -> &Path438     fn path(&self) -> &Path {
439         &self.path
440     }
441 
add_dependencies(&self, library: &Library, out: &mut Dependencies)442     fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
443         self.ty.add_dependencies(library, out);
444     }
445 
export_name(&self) -> &str446     fn export_name(&self) -> &str {
447         &self.export_name
448     }
449 
cfg(&self) -> Option<&Cfg>450     fn cfg(&self) -> Option<&Cfg> {
451         self.cfg.as_ref()
452     }
453 
annotations(&self) -> &AnnotationSet454     fn annotations(&self) -> &AnnotationSet {
455         &self.annotations
456     }
457 
annotations_mut(&mut self) -> &mut AnnotationSet458     fn annotations_mut(&mut self) -> &mut AnnotationSet {
459         &mut self.annotations
460     }
461 
container(&self) -> ItemContainer462     fn container(&self) -> ItemContainer {
463         ItemContainer::Constant(self.clone())
464     }
465 
rename_for_config(&mut self, config: &Config)466     fn rename_for_config(&mut self, config: &Config) {
467         if self.associated_to.is_none() {
468             config.export.rename(&mut self.export_name);
469         }
470         self.value.rename_for_config(config);
471         self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
472     }
473 
resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver)474     fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
475         self.ty.resolve_declaration_types(resolver);
476     }
477 }
478 
479 impl Constant {
write_declaration<F: Write>( &self, config: &Config, out: &mut SourceWriter<F>, associated_to_struct: &Struct, )480     pub fn write_declaration<F: Write>(
481         &self,
482         config: &Config,
483         out: &mut SourceWriter<F>,
484         associated_to_struct: &Struct,
485     ) {
486         debug_assert!(self.associated_to.is_some());
487         debug_assert!(config.language == Language::Cxx);
488         debug_assert!(!associated_to_struct.is_transparent);
489         debug_assert!(config.structure.associated_constants_in_body);
490         debug_assert!(config.constant.allow_static_const);
491 
492         if let Type::Ptr { is_const: true, .. } = self.ty {
493             out.write("static ");
494         } else {
495             out.write("static const ");
496         }
497         self.ty.write(config, out);
498         write!(out, " {};", self.export_name())
499     }
500 
write<F: Write>( &self, config: &Config, out: &mut SourceWriter<F>, associated_to_struct: Option<&Struct>, )501     pub fn write<F: Write>(
502         &self,
503         config: &Config,
504         out: &mut SourceWriter<F>,
505         associated_to_struct: Option<&Struct>,
506     ) {
507         if let Some(assoc) = associated_to_struct {
508             if assoc.is_generic() {
509                 return; // Not tested / implemented yet, so bail out.
510             }
511         }
512 
513         if !self.value.is_valid(out.bindings()) {
514             return;
515         }
516 
517         let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
518 
519         let in_body = associated_to_struct.is_some()
520             && config.language == Language::Cxx
521             && config.structure.associated_constants_in_body
522             && config.constant.allow_static_const
523             && !associated_to_transparent;
524 
525         let condition = self.cfg.to_condition(config);
526         condition.write_before(config, out);
527 
528         let name = if in_body {
529             Cow::Owned(format!(
530                 "{}::{}",
531                 associated_to_struct.unwrap().export_name(),
532                 self.export_name(),
533             ))
534         } else if self.associated_to.is_none() {
535             Cow::Borrowed(self.export_name())
536         } else {
537             let associated_name = match associated_to_struct {
538                 Some(s) => Cow::Borrowed(s.export_name()),
539                 None => {
540                     let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
541                     config.export.rename(&mut name);
542                     Cow::Owned(name)
543                 }
544             };
545 
546             Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
547         };
548 
549         let value = match self.value {
550             Literal::Struct {
551                 ref fields,
552                 ref path,
553                 ..
554             } if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
555             _ => &self.value,
556         };
557 
558         self.documentation.write(config, out);
559 
560         let allow_constexpr = if let Type::Primitive(..) = self.ty {
561             config.constant.allow_constexpr
562         } else {
563             false
564         };
565 
566         match config.language {
567             Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
568                 if allow_constexpr {
569                     out.write("constexpr ")
570                 }
571 
572                 if config.constant.allow_static_const {
573                     out.write(if in_body { "inline " } else { "static " });
574                 }
575 
576                 if let Type::Ptr { is_const: true, .. } = self.ty {
577                     // Nothing.
578                 } else {
579                     out.write("const ");
580                 }
581 
582                 self.ty.write(config, out);
583                 write!(out, " {} = ", name);
584                 value.write(config, out);
585                 write!(out, ";");
586             }
587             Language::Cxx | Language::C => {
588                 write!(out, "#define {} ", name);
589                 value.write(config, out);
590             }
591             Language::Cython => {
592                 out.write("const ");
593                 self.ty.write(config, out);
594                 // For extern Cython declarations the initializer is ignored,
595                 // but still useful as documentation, so we write it as a comment.
596                 write!(out, " {} # = ", name);
597                 value.write(config, out);
598             }
599         }
600 
601         condition.write_after(config, out);
602     }
603 }
604