133d722a9Sopenharmony_ciuse crate::clang::{Clang, Node}; 233d722a9Sopenharmony_ciuse crate::syntax::attrs::OtherAttrs; 333d722a9Sopenharmony_ciuse crate::syntax::cfg::CfgExpr; 433d722a9Sopenharmony_ciuse crate::syntax::namespace::Namespace; 533d722a9Sopenharmony_ciuse crate::syntax::report::Errors; 633d722a9Sopenharmony_ciuse crate::syntax::{Api, Discriminant, Doc, Enum, EnumRepr, ForeignName, Pair, Variant}; 733d722a9Sopenharmony_ciuse flate2::write::GzDecoder; 833d722a9Sopenharmony_ciuse memmap::Mmap; 933d722a9Sopenharmony_ciuse proc_macro2::{Delimiter, Group, Ident, TokenStream}; 1033d722a9Sopenharmony_ciuse quote::{format_ident, quote, quote_spanned}; 1133d722a9Sopenharmony_ciuse std::env; 1233d722a9Sopenharmony_ciuse std::fmt::{self, Display}; 1333d722a9Sopenharmony_ciuse std::fs::File; 1433d722a9Sopenharmony_ciuse std::io::Write; 1533d722a9Sopenharmony_ciuse std::path::PathBuf; 1633d722a9Sopenharmony_ciuse std::str::FromStr; 1733d722a9Sopenharmony_ciuse syn::{parse_quote, Path}; 1833d722a9Sopenharmony_ci 1933d722a9Sopenharmony_ciconst CXX_CLANG_AST: &str = "CXX_CLANG_AST"; 2033d722a9Sopenharmony_ci 2133d722a9Sopenharmony_cipub fn load(cx: &mut Errors, apis: &mut [Api]) { 2233d722a9Sopenharmony_ci let ref mut variants_from_header = Vec::new(); 2333d722a9Sopenharmony_ci for api in apis { 2433d722a9Sopenharmony_ci if let Api::Enum(enm) = api { 2533d722a9Sopenharmony_ci if enm.variants_from_header { 2633d722a9Sopenharmony_ci if enm.variants.is_empty() { 2733d722a9Sopenharmony_ci variants_from_header.push(enm); 2833d722a9Sopenharmony_ci } else { 2933d722a9Sopenharmony_ci let span = span_for_enum_error(enm); 3033d722a9Sopenharmony_ci cx.error( 3133d722a9Sopenharmony_ci span, 3233d722a9Sopenharmony_ci "enum with #![variants_from_header] must be written with no explicit variants", 3333d722a9Sopenharmony_ci ); 3433d722a9Sopenharmony_ci } 3533d722a9Sopenharmony_ci } 3633d722a9Sopenharmony_ci } 3733d722a9Sopenharmony_ci } 3833d722a9Sopenharmony_ci 3933d722a9Sopenharmony_ci let span = match variants_from_header.get(0) { 4033d722a9Sopenharmony_ci None => return, 4133d722a9Sopenharmony_ci Some(enm) => enm.variants_from_header_attr.clone().unwrap(), 4233d722a9Sopenharmony_ci }; 4333d722a9Sopenharmony_ci 4433d722a9Sopenharmony_ci let ast_dump_path = match env::var_os(CXX_CLANG_AST) { 4533d722a9Sopenharmony_ci Some(ast_dump_path) => PathBuf::from(ast_dump_path), 4633d722a9Sopenharmony_ci None => { 4733d722a9Sopenharmony_ci let msg = format!( 4833d722a9Sopenharmony_ci "environment variable ${} has not been provided", 4933d722a9Sopenharmony_ci CXX_CLANG_AST, 5033d722a9Sopenharmony_ci ); 5133d722a9Sopenharmony_ci return cx.error(span, msg); 5233d722a9Sopenharmony_ci } 5333d722a9Sopenharmony_ci }; 5433d722a9Sopenharmony_ci 5533d722a9Sopenharmony_ci let memmap = File::open(&ast_dump_path).and_then(|file| unsafe { Mmap::map(&file) }); 5633d722a9Sopenharmony_ci let mut gunzipped; 5733d722a9Sopenharmony_ci let ast_dump_bytes = match match memmap { 5833d722a9Sopenharmony_ci Ok(ref memmap) => { 5933d722a9Sopenharmony_ci let is_gzipped = memmap.get(..2) == Some(b"\x1f\x8b"); 6033d722a9Sopenharmony_ci if is_gzipped { 6133d722a9Sopenharmony_ci gunzipped = Vec::new(); 6233d722a9Sopenharmony_ci let decode_result = GzDecoder::new(&mut gunzipped).write_all(memmap); 6333d722a9Sopenharmony_ci decode_result.map(|_| gunzipped.as_slice()) 6433d722a9Sopenharmony_ci } else { 6533d722a9Sopenharmony_ci Ok(memmap as &[u8]) 6633d722a9Sopenharmony_ci } 6733d722a9Sopenharmony_ci } 6833d722a9Sopenharmony_ci Err(error) => Err(error), 6933d722a9Sopenharmony_ci } { 7033d722a9Sopenharmony_ci Ok(bytes) => bytes, 7133d722a9Sopenharmony_ci Err(error) => { 7233d722a9Sopenharmony_ci let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); 7333d722a9Sopenharmony_ci return cx.error(span, msg); 7433d722a9Sopenharmony_ci } 7533d722a9Sopenharmony_ci }; 7633d722a9Sopenharmony_ci 7733d722a9Sopenharmony_ci let ref root: Node = match serde_json::from_slice(ast_dump_bytes) { 7833d722a9Sopenharmony_ci Ok(root) => root, 7933d722a9Sopenharmony_ci Err(error) => { 8033d722a9Sopenharmony_ci let msg = format!("failed to read {}: {}", ast_dump_path.display(), error); 8133d722a9Sopenharmony_ci return cx.error(span, msg); 8233d722a9Sopenharmony_ci } 8333d722a9Sopenharmony_ci }; 8433d722a9Sopenharmony_ci 8533d722a9Sopenharmony_ci let ref mut namespace = Vec::new(); 8633d722a9Sopenharmony_ci traverse(cx, root, namespace, variants_from_header, None); 8733d722a9Sopenharmony_ci 8833d722a9Sopenharmony_ci for enm in variants_from_header { 8933d722a9Sopenharmony_ci if enm.variants.is_empty() { 9033d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 9133d722a9Sopenharmony_ci let name = CxxName(&enm.name); 9233d722a9Sopenharmony_ci let msg = format!("failed to find any C++ definition of enum {}", name); 9333d722a9Sopenharmony_ci cx.error(span, msg); 9433d722a9Sopenharmony_ci } 9533d722a9Sopenharmony_ci } 9633d722a9Sopenharmony_ci} 9733d722a9Sopenharmony_ci 9833d722a9Sopenharmony_cifn traverse<'a>( 9933d722a9Sopenharmony_ci cx: &mut Errors, 10033d722a9Sopenharmony_ci node: &'a Node, 10133d722a9Sopenharmony_ci namespace: &mut Vec<&'a str>, 10233d722a9Sopenharmony_ci variants_from_header: &mut [&mut Enum], 10333d722a9Sopenharmony_ci mut idx: Option<usize>, 10433d722a9Sopenharmony_ci) { 10533d722a9Sopenharmony_ci match &node.kind { 10633d722a9Sopenharmony_ci Clang::NamespaceDecl(decl) => { 10733d722a9Sopenharmony_ci let name = match &decl.name { 10833d722a9Sopenharmony_ci Some(name) => name, 10933d722a9Sopenharmony_ci // Can ignore enums inside an anonymous namespace. 11033d722a9Sopenharmony_ci None => return, 11133d722a9Sopenharmony_ci }; 11233d722a9Sopenharmony_ci namespace.push(name); 11333d722a9Sopenharmony_ci idx = None; 11433d722a9Sopenharmony_ci } 11533d722a9Sopenharmony_ci Clang::EnumDecl(decl) => { 11633d722a9Sopenharmony_ci let name = match &decl.name { 11733d722a9Sopenharmony_ci Some(name) => name, 11833d722a9Sopenharmony_ci None => return, 11933d722a9Sopenharmony_ci }; 12033d722a9Sopenharmony_ci idx = None; 12133d722a9Sopenharmony_ci for (i, enm) in variants_from_header.iter_mut().enumerate() { 12233d722a9Sopenharmony_ci if enm.name.cxx == **name && enm.name.namespace.iter().eq(&*namespace) { 12333d722a9Sopenharmony_ci if !enm.variants.is_empty() { 12433d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 12533d722a9Sopenharmony_ci let qual_name = CxxName(&enm.name); 12633d722a9Sopenharmony_ci let msg = format!("found multiple C++ definitions of enum {}", qual_name); 12733d722a9Sopenharmony_ci cx.error(span, msg); 12833d722a9Sopenharmony_ci return; 12933d722a9Sopenharmony_ci } 13033d722a9Sopenharmony_ci let fixed_underlying_type = match &decl.fixed_underlying_type { 13133d722a9Sopenharmony_ci Some(fixed_underlying_type) => fixed_underlying_type, 13233d722a9Sopenharmony_ci None => { 13333d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 13433d722a9Sopenharmony_ci let name = &enm.name.cxx; 13533d722a9Sopenharmony_ci let qual_name = CxxName(&enm.name); 13633d722a9Sopenharmony_ci let msg = format!( 13733d722a9Sopenharmony_ci "implicit implementation-defined repr for enum {} is not supported yet; consider changing its C++ definition to `enum {}: int {{...}}", 13833d722a9Sopenharmony_ci qual_name, name, 13933d722a9Sopenharmony_ci ); 14033d722a9Sopenharmony_ci cx.error(span, msg); 14133d722a9Sopenharmony_ci return; 14233d722a9Sopenharmony_ci } 14333d722a9Sopenharmony_ci }; 14433d722a9Sopenharmony_ci let repr = translate_qual_type( 14533d722a9Sopenharmony_ci cx, 14633d722a9Sopenharmony_ci enm, 14733d722a9Sopenharmony_ci fixed_underlying_type 14833d722a9Sopenharmony_ci .desugared_qual_type 14933d722a9Sopenharmony_ci .as_ref() 15033d722a9Sopenharmony_ci .unwrap_or(&fixed_underlying_type.qual_type), 15133d722a9Sopenharmony_ci ); 15233d722a9Sopenharmony_ci enm.repr = EnumRepr::Foreign { rust_type: repr }; 15333d722a9Sopenharmony_ci idx = Some(i); 15433d722a9Sopenharmony_ci break; 15533d722a9Sopenharmony_ci } 15633d722a9Sopenharmony_ci } 15733d722a9Sopenharmony_ci if idx.is_none() { 15833d722a9Sopenharmony_ci return; 15933d722a9Sopenharmony_ci } 16033d722a9Sopenharmony_ci } 16133d722a9Sopenharmony_ci Clang::EnumConstantDecl(decl) => { 16233d722a9Sopenharmony_ci if let Some(idx) = idx { 16333d722a9Sopenharmony_ci let enm = &mut *variants_from_header[idx]; 16433d722a9Sopenharmony_ci let span = enm 16533d722a9Sopenharmony_ci .variants_from_header_attr 16633d722a9Sopenharmony_ci .as_ref() 16733d722a9Sopenharmony_ci .unwrap() 16833d722a9Sopenharmony_ci .path 16933d722a9Sopenharmony_ci .get_ident() 17033d722a9Sopenharmony_ci .unwrap() 17133d722a9Sopenharmony_ci .span(); 17233d722a9Sopenharmony_ci let cxx_name = match ForeignName::parse(&decl.name, span) { 17333d722a9Sopenharmony_ci Ok(foreign_name) => foreign_name, 17433d722a9Sopenharmony_ci Err(_) => { 17533d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 17633d722a9Sopenharmony_ci let msg = format!("unsupported C++ variant name: {}", decl.name); 17733d722a9Sopenharmony_ci return cx.error(span, msg); 17833d722a9Sopenharmony_ci } 17933d722a9Sopenharmony_ci }; 18033d722a9Sopenharmony_ci let rust_name: Ident = match syn::parse_str(&decl.name) { 18133d722a9Sopenharmony_ci Ok(ident) => ident, 18233d722a9Sopenharmony_ci Err(_) => format_ident!("__Variant{}", enm.variants.len()), 18333d722a9Sopenharmony_ci }; 18433d722a9Sopenharmony_ci let discriminant = match discriminant_value(&node.inner) { 18533d722a9Sopenharmony_ci ParsedDiscriminant::Constant(discriminant) => discriminant, 18633d722a9Sopenharmony_ci ParsedDiscriminant::Successor => match enm.variants.last() { 18733d722a9Sopenharmony_ci None => Discriminant::zero(), 18833d722a9Sopenharmony_ci Some(last) => match last.discriminant.checked_succ() { 18933d722a9Sopenharmony_ci Some(discriminant) => discriminant, 19033d722a9Sopenharmony_ci None => { 19133d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 19233d722a9Sopenharmony_ci let msg = format!( 19333d722a9Sopenharmony_ci "overflow processing discriminant value for variant: {}", 19433d722a9Sopenharmony_ci decl.name, 19533d722a9Sopenharmony_ci ); 19633d722a9Sopenharmony_ci return cx.error(span, msg); 19733d722a9Sopenharmony_ci } 19833d722a9Sopenharmony_ci }, 19933d722a9Sopenharmony_ci }, 20033d722a9Sopenharmony_ci ParsedDiscriminant::Fail => { 20133d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 20233d722a9Sopenharmony_ci let msg = format!( 20333d722a9Sopenharmony_ci "failed to obtain discriminant value for variant: {}", 20433d722a9Sopenharmony_ci decl.name, 20533d722a9Sopenharmony_ci ); 20633d722a9Sopenharmony_ci cx.error(span, msg); 20733d722a9Sopenharmony_ci Discriminant::zero() 20833d722a9Sopenharmony_ci } 20933d722a9Sopenharmony_ci }; 21033d722a9Sopenharmony_ci enm.variants.push(Variant { 21133d722a9Sopenharmony_ci cfg: CfgExpr::Unconditional, 21233d722a9Sopenharmony_ci doc: Doc::new(), 21333d722a9Sopenharmony_ci attrs: OtherAttrs::none(), 21433d722a9Sopenharmony_ci name: Pair { 21533d722a9Sopenharmony_ci namespace: Namespace::ROOT, 21633d722a9Sopenharmony_ci cxx: cxx_name, 21733d722a9Sopenharmony_ci rust: rust_name, 21833d722a9Sopenharmony_ci }, 21933d722a9Sopenharmony_ci discriminant, 22033d722a9Sopenharmony_ci expr: None, 22133d722a9Sopenharmony_ci }); 22233d722a9Sopenharmony_ci } 22333d722a9Sopenharmony_ci } 22433d722a9Sopenharmony_ci _ => {} 22533d722a9Sopenharmony_ci } 22633d722a9Sopenharmony_ci for inner in &node.inner { 22733d722a9Sopenharmony_ci traverse(cx, inner, namespace, variants_from_header, idx); 22833d722a9Sopenharmony_ci } 22933d722a9Sopenharmony_ci if let Clang::NamespaceDecl(_) = &node.kind { 23033d722a9Sopenharmony_ci let _ = namespace.pop().unwrap(); 23133d722a9Sopenharmony_ci } 23233d722a9Sopenharmony_ci} 23333d722a9Sopenharmony_ci 23433d722a9Sopenharmony_cifn translate_qual_type(cx: &mut Errors, enm: &Enum, qual_type: &str) -> Path { 23533d722a9Sopenharmony_ci let rust_std_name = match qual_type { 23633d722a9Sopenharmony_ci "char" => "c_char", 23733d722a9Sopenharmony_ci "int" => "c_int", 23833d722a9Sopenharmony_ci "long" => "c_long", 23933d722a9Sopenharmony_ci "long long" => "c_longlong", 24033d722a9Sopenharmony_ci "signed char" => "c_schar", 24133d722a9Sopenharmony_ci "short" => "c_short", 24233d722a9Sopenharmony_ci "unsigned char" => "c_uchar", 24333d722a9Sopenharmony_ci "unsigned int" => "c_uint", 24433d722a9Sopenharmony_ci "unsigned long" => "c_ulong", 24533d722a9Sopenharmony_ci "unsigned long long" => "c_ulonglong", 24633d722a9Sopenharmony_ci "unsigned short" => "c_ushort", 24733d722a9Sopenharmony_ci unsupported => { 24833d722a9Sopenharmony_ci let span = &enm.variants_from_header_attr; 24933d722a9Sopenharmony_ci let qual_name = CxxName(&enm.name); 25033d722a9Sopenharmony_ci let msg = format!( 25133d722a9Sopenharmony_ci "unsupported underlying type for {}: {}", 25233d722a9Sopenharmony_ci qual_name, unsupported, 25333d722a9Sopenharmony_ci ); 25433d722a9Sopenharmony_ci cx.error(span, msg); 25533d722a9Sopenharmony_ci "c_int" 25633d722a9Sopenharmony_ci } 25733d722a9Sopenharmony_ci }; 25833d722a9Sopenharmony_ci let span = enm 25933d722a9Sopenharmony_ci .variants_from_header_attr 26033d722a9Sopenharmony_ci .as_ref() 26133d722a9Sopenharmony_ci .unwrap() 26233d722a9Sopenharmony_ci .path 26333d722a9Sopenharmony_ci .get_ident() 26433d722a9Sopenharmony_ci .unwrap() 26533d722a9Sopenharmony_ci .span(); 26633d722a9Sopenharmony_ci let ident = Ident::new(rust_std_name, span); 26733d722a9Sopenharmony_ci let path = quote_spanned!(span=> ::cxx::core::ffi::#ident); 26833d722a9Sopenharmony_ci parse_quote!(#path) 26933d722a9Sopenharmony_ci} 27033d722a9Sopenharmony_ci 27133d722a9Sopenharmony_cienum ParsedDiscriminant { 27233d722a9Sopenharmony_ci Constant(Discriminant), 27333d722a9Sopenharmony_ci Successor, 27433d722a9Sopenharmony_ci Fail, 27533d722a9Sopenharmony_ci} 27633d722a9Sopenharmony_ci 27733d722a9Sopenharmony_cifn discriminant_value(mut clang: &[Node]) -> ParsedDiscriminant { 27833d722a9Sopenharmony_ci if clang.is_empty() { 27933d722a9Sopenharmony_ci // No discriminant expression provided; use successor of previous 28033d722a9Sopenharmony_ci // discriminant. 28133d722a9Sopenharmony_ci return ParsedDiscriminant::Successor; 28233d722a9Sopenharmony_ci } 28333d722a9Sopenharmony_ci 28433d722a9Sopenharmony_ci loop { 28533d722a9Sopenharmony_ci if clang.len() != 1 { 28633d722a9Sopenharmony_ci return ParsedDiscriminant::Fail; 28733d722a9Sopenharmony_ci } 28833d722a9Sopenharmony_ci 28933d722a9Sopenharmony_ci let node = &clang[0]; 29033d722a9Sopenharmony_ci match &node.kind { 29133d722a9Sopenharmony_ci Clang::ImplicitCastExpr => clang = &node.inner, 29233d722a9Sopenharmony_ci Clang::ConstantExpr(expr) => match Discriminant::from_str(&expr.value) { 29333d722a9Sopenharmony_ci Ok(discriminant) => return ParsedDiscriminant::Constant(discriminant), 29433d722a9Sopenharmony_ci Err(_) => return ParsedDiscriminant::Fail, 29533d722a9Sopenharmony_ci }, 29633d722a9Sopenharmony_ci _ => return ParsedDiscriminant::Fail, 29733d722a9Sopenharmony_ci } 29833d722a9Sopenharmony_ci } 29933d722a9Sopenharmony_ci} 30033d722a9Sopenharmony_ci 30133d722a9Sopenharmony_cifn span_for_enum_error(enm: &Enum) -> TokenStream { 30233d722a9Sopenharmony_ci let enum_token = enm.enum_token; 30333d722a9Sopenharmony_ci let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new()); 30433d722a9Sopenharmony_ci brace_token.set_span(enm.brace_token.span); 30533d722a9Sopenharmony_ci quote!(#enum_token #brace_token) 30633d722a9Sopenharmony_ci} 30733d722a9Sopenharmony_ci 30833d722a9Sopenharmony_cistruct CxxName<'a>(&'a Pair); 30933d722a9Sopenharmony_ci 31033d722a9Sopenharmony_ciimpl<'a> Display for CxxName<'a> { 31133d722a9Sopenharmony_ci fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 31233d722a9Sopenharmony_ci for namespace in &self.0.namespace { 31333d722a9Sopenharmony_ci write!(formatter, "{}::", namespace)?; 31433d722a9Sopenharmony_ci } 31533d722a9Sopenharmony_ci write!(formatter, "{}", self.0.cxx) 31633d722a9Sopenharmony_ci } 31733d722a9Sopenharmony_ci} 318