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::ext::IdentExt;
10use syn::{self, UnOp};
11
12use crate::bindgen::config::{Config, Language};
13use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
14use crate::bindgen::dependencies::Dependencies;
15use crate::bindgen::ir::{
16 AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
17 Struct, ToCondition, Type,
18};
19use crate::bindgen::library::Library;
20use crate::bindgen::writer::{Source, SourceWriter};
21use crate::bindgen::Bindings;
22
23fn member_to_ident(member: &syn::Member) -> String {
24 match member {
25 syn::Member::Named(ref name: &Ident) => name.unraw().to_string(),
26 syn::Member::Unnamed(ref index: &Index) => format!("_{}", index.index),
27 }
28}
29
30// TODO: Maybe add support to more std associated constants.
31fn to_known_assoc_constant(associated_to: &Path, name: &str) -> Option<String> {
32 use crate::bindgen::ir::{IntKind, PrimitiveType};
33
34 if name != "MAX" && name != "MIN" {
35 return None;
36 }
37
38 let prim = PrimitiveType::maybe(associated_to.name())?;
39 let prefix = match prim {
40 PrimitiveType::Integer {
41 kind,
42 signed,
43 zeroable: _,
44 } => match kind {
45 IntKind::B8 => {
46 if signed {
47 "INT8"
48 } else {
49 "UINT8"
50 }
51 }
52 IntKind::B16 => {
53 if signed {
54 "INT16"
55 } else {
56 "UINT16"
57 }
58 }
59 IntKind::B32 => {
60 if signed {
61 "INT32"
62 } else {
63 "UINT32"
64 }
65 }
66 IntKind::B64 => {
67 if signed {
68 "INT64"
69 } else {
70 "UINT64"
71 }
72 }
73 _ => return None,
74 },
75 _ => return None,
76 };
77 Some(format!("{}_{}", prefix, name))
78}
79
80#[derive(Debug, Clone)]
81pub enum Literal {
82 Expr(String),
83 Path {
84 associated_to: Option<(Path, String)>,
85 name: String,
86 },
87 PostfixUnaryOp {
88 op: &'static str,
89 value: Box<Literal>,
90 },
91 BinOp {
92 left: Box<Literal>,
93 op: &'static str,
94 right: Box<Literal>,
95 },
96 FieldAccess {
97 base: Box<Literal>,
98 field: String,
99 },
100 Struct {
101 path: Path,
102 export_name: String,
103 fields: HashMap<String, Literal>,
104 },
105 Cast {
106 ty: Type,
107 value: Box<Literal>,
108 },
109}
110
111impl Literal {
112 fn replace_self_with(&mut self, self_ty: &Path) {
113 match *self {
114 Literal::PostfixUnaryOp { ref mut value, .. } => {
115 value.replace_self_with(self_ty);
116 }
117 Literal::BinOp {
118 ref mut left,
119 ref mut right,
120 ..
121 } => {
122 left.replace_self_with(self_ty);
123 right.replace_self_with(self_ty);
124 }
125 Literal::FieldAccess { ref mut base, .. } => {
126 base.replace_self_with(self_ty);
127 }
128 Literal::Struct {
129 ref mut path,
130 ref mut export_name,
131 ref mut fields,
132 } => {
133 if path.replace_self_with(self_ty) {
134 *export_name = self_ty.name().to_owned();
135 }
136 for ref mut expr in fields.values_mut() {
137 expr.replace_self_with(self_ty);
138 }
139 }
140 Literal::Cast {
141 ref mut ty,
142 ref mut value,
143 } => {
144 ty.replace_self_with(self_ty);
145 value.replace_self_with(self_ty);
146 }
147 Literal::Path {
148 ref mut associated_to,
149 ..
150 } => {
151 if let Some((ref mut path, ref mut export_name)) = *associated_to {
152 if path.replace_self_with(self_ty) {
153 *export_name = self_ty.name().to_owned();
154 }
155 }
156 }
157 Literal::Expr(..) => {}
158 }
159 }
160
161 fn is_valid(&self, bindings: &Bindings) -> bool {
162 match *self {
163 Literal::Expr(..) => true,
164 Literal::Path {
165 ref associated_to,
166 ref name,
167 } => {
168 if let Some((ref path, _export_name)) = associated_to {
169 return bindings.struct_exists(path)
170 || to_known_assoc_constant(path, name).is_some();
171 }
172 true
173 }
174 Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
175 Literal::BinOp {
176 ref left,
177 ref right,
178 ..
179 } => left.is_valid(bindings) && right.is_valid(bindings),
180 Literal::FieldAccess { ref base, .. } => base.is_valid(bindings),
181 Literal::Struct { ref path, .. } => bindings.struct_exists(path),
182 Literal::Cast { ref value, .. } => value.is_valid(bindings),
183 }
184 }
185
186 fn can_be_constexpr(&self) -> bool {
187 !self.has_pointer_casts()
188 }
189
190 fn visit(&self, visitor: &mut impl FnMut(&Self) -> bool) -> bool {
191 if !visitor(self) {
192 return false;
193 }
194 match self {
195 Literal::Expr(..) | Literal::Path { .. } => true,
196 Literal::PostfixUnaryOp { ref value, .. } => value.visit(visitor),
197 Literal::BinOp {
198 ref left,
199 ref right,
200 ..
201 } => left.visit(visitor) && right.visit(visitor),
202 Literal::FieldAccess { ref base, .. } => base.visit(visitor),
203 Literal::Struct { ref fields, .. } => {
204 for (_name, field) in fields.iter() {
205 if !field.visit(visitor) {
206 return false;
207 }
208 }
209 true
210 }
211 Literal::Cast { ref value, .. } => value.visit(visitor),
212 }
213 }
214
215 fn has_pointer_casts(&self) -> bool {
216 let mut has_pointer_casts = false;
217 self.visit(&mut |lit| {
218 if let Literal::Cast { ref ty, .. } = *lit {
219 has_pointer_casts = has_pointer_casts || ty.is_ptr();
220 }
221 !has_pointer_casts
222 });
223 has_pointer_casts
224 }
225
226 pub fn uses_only_primitive_types(&self) -> bool {
227 let mut uses_only_primitive_types = true;
228 self.visit(&mut |lit| {
229 // XXX This is a bit sketchy, but alas.
230 uses_only_primitive_types = uses_only_primitive_types
231 && match *lit {
232 Literal::Struct { .. } => false,
233 Literal::Cast { ref ty, .. } => ty.is_primitive_or_ptr_primitive(),
234 _ => true,
235 };
236 uses_only_primitive_types
237 });
238 uses_only_primitive_types
239 }
240}
241
242impl Literal {
243 pub fn rename_for_config(&mut self, config: &Config) {
244 match self {
245 Literal::Struct {
246 ref mut export_name,
247 fields,
248 ..
249 } => {
250 config.export.rename(export_name);
251 for lit in fields.values_mut() {
252 lit.rename_for_config(config);
253 }
254 }
255 Literal::FieldAccess { ref mut base, .. } => {
256 base.rename_for_config(config);
257 }
258 Literal::Path {
259 ref mut associated_to,
260 ref mut name,
261 } => {
262 if let Some((_path, ref mut export_name)) = associated_to {
263 config.export.rename(export_name);
264 } else {
265 config.export.rename(name);
266 }
267 }
268 Literal::PostfixUnaryOp { ref mut value, .. } => {
269 value.rename_for_config(config);
270 }
271 Literal::BinOp {
272 ref mut left,
273 ref mut right,
274 ..
275 } => {
276 left.rename_for_config(config);
277 right.rename_for_config(config);
278 }
279 Literal::Expr(_) => {}
280 Literal::Cast {
281 ref mut ty,
282 ref mut value,
283 } => {
284 ty.rename_for_config(config, &GenericParams::default());
285 value.rename_for_config(config);
286 }
287 }
288 }
289
290 // Translate from full blown `syn::Expr` into a simpler `Literal` type
291 pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
292 match *expr {
293 // Match binary expressions of the form `a * b`
294 syn::Expr::Binary(ref bin_expr) => {
295 let l = Self::load(&bin_expr.left)?;
296 let r = Self::load(&bin_expr.right)?;
297 let op = match bin_expr.op {
298 syn::BinOp::Add(..) => "+",
299 syn::BinOp::Sub(..) => "-",
300 syn::BinOp::Mul(..) => "*",
301 syn::BinOp::Div(..) => "/",
302 syn::BinOp::Rem(..) => "%",
303 syn::BinOp::And(..) => "&&",
304 syn::BinOp::Or(..) => "||",
305 syn::BinOp::BitXor(..) => "^",
306 syn::BinOp::BitAnd(..) => "&",
307 syn::BinOp::BitOr(..) => "|",
308 syn::BinOp::Shl(..) => "<<",
309 syn::BinOp::Shr(..) => ">>",
310 syn::BinOp::Eq(..) => "==",
311 syn::BinOp::Lt(..) => "<",
312 syn::BinOp::Le(..) => "<=",
313 syn::BinOp::Ne(..) => "!=",
314 syn::BinOp::Ge(..) => ">=",
315 syn::BinOp::Gt(..) => ">",
316 syn::BinOp::AddEq(..) => "+=",
317 syn::BinOp::SubEq(..) => "-=",
318 syn::BinOp::MulEq(..) => "*=",
319 syn::BinOp::DivEq(..) => "/=",
320 syn::BinOp::RemEq(..) => "%=",
321 syn::BinOp::BitXorEq(..) => "^=",
322 syn::BinOp::BitAndEq(..) => "&=",
323 syn::BinOp::BitOrEq(..) => "|=",
324 syn::BinOp::ShlEq(..) => "<<=",
325 syn::BinOp::ShrEq(..) => ">>=",
326 };
327 Ok(Literal::BinOp {
328 left: Box::new(l),
329 op,
330 right: Box::new(r),
331 })
332 }
333
334 // Match literals like true, 'a', 32 etc
335 syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
336 match lit {
337 syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
338 syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
339 0..=255 => format!("'{}'", value.value().escape_default()),
340 other_code => format!(r"U'\U{:08X}'", other_code),
341 })),
342 syn::Lit::Int(ref value) => {
343 let suffix = match value.suffix() {
344 "u64" => "ull",
345 "i64" => "ll",
346 "u32" => "u",
347 _ if value.base10_parse::<i64>().is_err() => "ull",
348 _ => "",
349 };
350 Ok(Literal::Expr(format!(
351 "{}{}",
352 value.base10_digits(),
353 suffix
354 )))
355 }
356 syn::Lit::Float(ref value) => {
357 Ok(Literal::Expr(value.base10_digits().to_string()))
358 }
359 syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
360 // TODO: Add support for byte string and Verbatim
361 _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
362 }
363 }
364
365 syn::Expr::Field(syn::ExprField {
366 ref base,
367 ref member,
368 ..
369 }) => Ok(Literal::FieldAccess {
370 base: Box::new(Literal::load(base)?),
371 field: member_to_ident(member),
372 }),
373
374 syn::Expr::Call(syn::ExprCall {
375 ref func, ref args, ..
376 }) => {
377 let struct_name = match Literal::load(func)? {
378 Literal::Path {
379 associated_to: None,
380 name,
381 } => name,
382 _ => return Err(format!("Unsupported call expression. {:?}", *expr)),
383 };
384 let mut fields = HashMap::<String, Literal>::default();
385 for (index, arg) in args.iter().enumerate() {
386 let ident =
387 member_to_ident(&syn::Member::Unnamed(syn::Index::from(index))).to_string();
388 let value = Literal::load(arg)?;
389 fields.insert(ident, value);
390 }
391 Ok(Literal::Struct {
392 path: Path::new(struct_name.clone()),
393 export_name: struct_name,
394 fields,
395 })
396 }
397
398 syn::Expr::Struct(syn::ExprStruct {
399 ref path,
400 ref fields,
401 ..
402 }) => {
403 let struct_name = path.segments[0].ident.unraw().to_string();
404 let mut field_map = HashMap::<String, Literal>::default();
405 for field in fields {
406 let ident = member_to_ident(&field.member).to_string();
407 let value = Literal::load(&field.expr)?;
408 field_map.insert(ident, value);
409 }
410 Ok(Literal::Struct {
411 path: Path::new(struct_name.clone()),
412 export_name: struct_name,
413 fields: field_map,
414 })
415 }
416
417 syn::Expr::Unary(syn::ExprUnary {
418 ref op, ref expr, ..
419 }) => match *op {
420 UnOp::Not(_) => {
421 let val = Self::load(expr)?;
422 Ok(Literal::PostfixUnaryOp {
423 op: "~",
424 value: Box::new(val),
425 })
426 }
427 UnOp::Neg(_) => {
428 let val = Self::load(expr)?;
429 Ok(Literal::PostfixUnaryOp {
430 op: "-",
431 value: Box::new(val),
432 })
433 }
434 _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
435 },
436
437 // Match identifiers, like `5 << SHIFT`
438 syn::Expr::Path(syn::ExprPath { ref path, .. }) => {
439 // Handle only the simplest identifiers and Associated::IDENT
440 // kind of syntax.
441 Ok(match path.segments.len() {
442 1 => Literal::Path {
443 associated_to: None,
444 name: path.segments[0].ident.to_string(),
445 },
446 2 => {
447 let struct_name = path.segments[0].ident.to_string();
448 Literal::Path {
449 associated_to: Some((Path::new(&struct_name), struct_name)),
450 name: path.segments[1].ident.to_string(),
451 }
452 }
453 _ => return Err(format!("Unsupported path expression. {:?}", path)),
454 })
455 }
456
457 syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
458
459 syn::Expr::Cast(syn::ExprCast {
460 ref expr, ref ty, ..
461 }) => {
462 let val = Self::load(expr)?;
463 match Type::load(ty)? {
464 Some(ty) => Ok(Literal::Cast {
465 ty,
466 value: Box::new(val),
467 }),
468 None => Err("Cannot cast to zero sized type.".to_owned()),
469 }
470 }
471
472 _ => Err(format!("Unsupported expression. {:?}", *expr)),
473 }
474 }
475
476 pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
477 match self {
478 Literal::Expr(v) => match (&**v, config.language) {
479 ("true", Language::Cython) => write!(out, "True"),
480 ("false", Language::Cython) => write!(out, "False"),
481 (v, _) => write!(out, "{}", v),
482 },
483 Literal::Path {
484 ref associated_to,
485 ref name,
486 } => {
487 if let Some((ref path, ref export_name)) = associated_to {
488 if let Some(known) = to_known_assoc_constant(path, name) {
489 return write!(out, "{}", known);
490 }
491 let path_separator = match config.language {
492 Language::Cython | Language::C => "_",
493 Language::Cxx => {
494 if config.structure.associated_constants_in_body {
495 "::"
496 } else {
497 "_"
498 }
499 }
500 };
501 write!(out, "{}{}", export_name, path_separator)
502 }
503 write!(out, "{}", name)
504 }
505 Literal::FieldAccess {
506 ref base,
507 ref field,
508 } => {
509 write!(out, "(");
510 base.write(config, out);
511 write!(out, ").{}", field);
512 }
513 Literal::PostfixUnaryOp { op, ref value } => {
514 write!(out, "{}", op);
515 value.write(config, out);
516 }
517 Literal::BinOp {
518 ref left,
519 op,
520 ref right,
521 } => {
522 write!(out, "(");
523 left.write(config, out);
524 write!(out, " {} ", op);
525 right.write(config, out);
526 write!(out, ")");
527 }
528 Literal::Cast { ref ty, ref value } => {
529 out.write(if config.language == Language::Cython {
530 "<"
531 } else {
532 "("
533 });
534 ty.write(config, out);
535 out.write(if config.language == Language::Cython {
536 ">"
537 } else {
538 ")"
539 });
540 value.write(config, out);
541 }
542 Literal::Struct {
543 export_name,
544 fields,
545 path,
546 } => {
547 match config.language {
548 Language::C => write!(out, "({})", export_name),
549 Language::Cxx => write!(out, "{}", export_name),
550 Language::Cython => write!(out, "<{}>", export_name),
551 }
552
553 write!(out, "{{ ");
554 let mut is_first_field = true;
555 // In C++, same order as defined is required.
556 let ordered_fields = out.bindings().struct_field_names(path);
557 for ordered_key in ordered_fields.iter() {
558 if let Some(lit) = fields.get(ordered_key) {
559 if !is_first_field {
560 write!(out, ", ");
561 } else {
562 is_first_field = false;
563 }
564 match config.language {
565 Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
566 Language::C => write!(out, ".{} = ", ordered_key),
567 Language::Cython => {}
568 }
569 lit.write(config, out);
570 }
571 }
572 write!(out, " }}");
573 }
574 }
575 }
576}
577
578#[derive(Debug, Clone)]
579pub struct Constant {
580 pub path: Path,
581 pub export_name: String,
582 pub ty: Type,
583 pub value: Literal,
584 pub cfg: Option<Cfg>,
585 pub annotations: AnnotationSet,
586 pub documentation: Documentation,
587 pub associated_to: Option<Path>,
588}
589
590impl Constant {
591 pub fn load(
592 path: Path,
593 mod_cfg: Option<&Cfg>,
594 ty: &syn::Type,
595 expr: &syn::Expr,
596 attrs: &[syn::Attribute],
597 associated_to: Option<Path>,
598 ) -> Result<Constant, String> {
599 let ty = Type::load(ty)?;
600 let mut ty = match ty {
601 Some(ty) => ty,
602 None => {
603 return Err("Cannot have a zero sized const definition.".to_owned());
604 }
605 };
606
607 let mut lit = Literal::load(expr)?;
608
609 if let Some(ref associated_to) = associated_to {
610 ty.replace_self_with(associated_to);
611 lit.replace_self_with(associated_to);
612 }
613
614 Ok(Constant::new(
615 path,
616 ty,
617 lit,
618 Cfg::append(mod_cfg, Cfg::load(attrs)),
619 AnnotationSet::load(attrs)?,
620 Documentation::load(attrs),
621 associated_to,
622 ))
623 }
624
625 pub fn new(
626 path: Path,
627 ty: Type,
628 value: Literal,
629 cfg: Option<Cfg>,
630 annotations: AnnotationSet,
631 documentation: Documentation,
632 associated_to: Option<Path>,
633 ) -> Self {
634 let export_name = path.name().to_owned();
635 Self {
636 path,
637 export_name,
638 ty,
639 value,
640 cfg,
641 annotations,
642 documentation,
643 associated_to,
644 }
645 }
646
647 pub fn uses_only_primitive_types(&self) -> bool {
648 self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
649 }
650}
651
652impl Item for Constant {
653 fn path(&self) -> &Path {
654 &self.path
655 }
656
657 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
658 self.ty.add_dependencies(library, out);
659 }
660
661 fn export_name(&self) -> &str {
662 &self.export_name
663 }
664
665 fn cfg(&self) -> Option<&Cfg> {
666 self.cfg.as_ref()
667 }
668
669 fn annotations(&self) -> &AnnotationSet {
670 &self.annotations
671 }
672
673 fn annotations_mut(&mut self) -> &mut AnnotationSet {
674 &mut self.annotations
675 }
676
677 fn container(&self) -> ItemContainer {
678 ItemContainer::Constant(self.clone())
679 }
680
681 fn rename_for_config(&mut self, config: &Config) {
682 if self.associated_to.is_none() {
683 config.export.rename(&mut self.export_name);
684 }
685 self.value.rename_for_config(config);
686 self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
687 }
688
689 fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
690 self.ty.resolve_declaration_types(resolver);
691 }
692}
693
694impl Constant {
695 pub fn write_declaration<F: Write>(
696 &self,
697 config: &Config,
698 out: &mut SourceWriter<F>,
699 associated_to_struct: &Struct,
700 ) {
701 debug_assert!(self.associated_to.is_some());
702 debug_assert!(config.language == Language::Cxx);
703 debug_assert!(!associated_to_struct.is_transparent);
704 debug_assert!(config.structure.associated_constants_in_body);
705 debug_assert!(config.constant.allow_static_const);
706
707 if let Type::Ptr { is_const: true, .. } = self.ty {
708 out.write("static ");
709 } else {
710 out.write("static const ");
711 }
712 self.ty.write(config, out);
713 write!(out, " {};", self.export_name())
714 }
715
716 pub fn write<F: Write>(
717 &self,
718 config: &Config,
719 out: &mut SourceWriter<F>,
720 associated_to_struct: Option<&Struct>,
721 ) {
722 if let Some(assoc) = associated_to_struct {
723 if assoc.is_generic() {
724 return; // Not tested / implemented yet, so bail out.
725 }
726 }
727
728 if !self.value.is_valid(out.bindings()) {
729 return;
730 }
731
732 let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
733
734 let in_body = associated_to_struct.is_some()
735 && config.language == Language::Cxx
736 && config.structure.associated_constants_in_body
737 && config.constant.allow_static_const
738 && !associated_to_transparent;
739
740 let condition = self.cfg.to_condition(config);
741 condition.write_before(config, out);
742
743 let name = if in_body {
744 Cow::Owned(format!(
745 "{}::{}",
746 associated_to_struct.unwrap().export_name(),
747 self.export_name(),
748 ))
749 } else if self.associated_to.is_none() {
750 Cow::Borrowed(self.export_name())
751 } else {
752 let associated_name = match associated_to_struct {
753 Some(s) => Cow::Borrowed(s.export_name()),
754 None => {
755 let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
756 config.export.rename(&mut name);
757 Cow::Owned(name)
758 }
759 };
760
761 Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
762 };
763
764 let value = match self.value {
765 Literal::Struct {
766 ref fields,
767 ref path,
768 ..
769 } if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
770 _ => &self.value,
771 };
772
773 self.documentation.write(config, out);
774
775 let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
776 match config.language {
777 Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
778 if allow_constexpr {
779 out.write("constexpr ")
780 }
781
782 if config.constant.allow_static_const {
783 out.write(if in_body { "inline " } else { "static " });
784 }
785
786 if let Type::Ptr { is_const: true, .. } = self.ty {
787 // Nothing.
788 } else {
789 out.write("const ");
790 }
791
792 self.ty.write(config, out);
793 write!(out, " {} = ", name);
794 value.write(config, out);
795 write!(out, ";");
796 }
797 Language::Cxx | Language::C => {
798 write!(out, "#define {} ", name);
799 value.write(config, out);
800 }
801 Language::Cython => {
802 out.write("const ");
803 self.ty.write(config, out);
804 // For extern Cython declarations the initializer is ignored,
805 // but still useful as documentation, so we write it as a comment.
806 write!(out, " {} # = ", name);
807 value.write(config, out);
808 }
809 }
810
811 condition.write_after(config, out);
812 }
813}
814