RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.

This commit is contained in:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

View File

@@ -0,0 +1,445 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path;
use std::rc::Rc;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{
Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct,
};
use crate::bindgen::writer::{Source, SourceWriter};
/// A bindings header that can be written.
pub struct Bindings {
pub config: Config,
/// The map from path to struct, used to lookup whether a given type is a
/// transparent struct. This is needed to generate code for constants.
struct_map: ItemMap<Struct>,
struct_fileds_memo: RefCell<HashMap<BindgenPath, Rc<Vec<String>>>>,
globals: Vec<Static>,
constants: Vec<Constant>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
}
#[derive(PartialEq)]
enum NamespaceOperation {
Open,
Close,
}
impl Bindings {
pub(crate) fn new(
config: Config,
struct_map: ItemMap<Struct>,
constants: Vec<Constant>,
globals: Vec<Static>,
items: Vec<ItemContainer>,
functions: Vec<Function>,
) -> Bindings {
Bindings {
config,
struct_map,
struct_fileds_memo: Default::default(),
globals,
constants,
items,
functions,
}
}
// FIXME(emilio): What to do when the configuration doesn't match?
pub fn struct_is_transparent(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.struct_map.for_items(path, |s| any |= s.is_transparent);
any
}
pub fn struct_exists(&self, path: &BindgenPath) -> bool {
let mut any = false;
self.struct_map.for_items(path, |_| any = true);
any
}
pub fn struct_field_names(&self, path: &BindgenPath) -> Rc<Vec<String>> {
let mut memos = self.struct_fileds_memo.borrow_mut();
if let Some(memo) = memos.get(path) {
return memo.clone();
}
let mut fields = Vec::<String>::new();
self.struct_map.for_items(path, |st| {
let mut pos: usize = 0;
for field in &st.fields {
if let Some(found_pos) = fields.iter().position(|v| *v == field.name) {
pos = found_pos + 1;
} else {
fields.insert(pos, field.name.clone());
pos += 1;
}
}
});
let fields = Rc::new(fields);
memos.insert(path.clone(), fields.clone());
fields
}
pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
// Don't compare files if we've never written this file before
if !path.as_ref().is_file() {
if let Some(parent) = path::Path::new(path.as_ref()).parent() {
fs::create_dir_all(parent).unwrap();
}
self.write(File::create(path).unwrap());
return true;
}
let mut new_file_contents = Vec::new();
self.write(&mut new_file_contents);
let mut old_file_contents = Vec::new();
{
let mut old_file = File::open(&path).unwrap();
old_file.read_to_end(&mut old_file_contents).unwrap();
}
if old_file_contents != new_file_contents {
let mut new_file = File::create(&path).unwrap();
new_file.write_all(&new_file_contents).unwrap();
true
} else {
false
}
}
pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
if let Some(ref f) = self.config.header {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}
if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
write!(out, "#ifndef {}", f);
out.new_line();
write!(out, "#define {}", f);
out.new_line();
}
if self.config.pragma_once && self.config.language != Language::Cython {
out.new_line_if_not_start();
write!(out, "#pragma once");
out.new_line();
}
if self.config.include_version {
out.new_line_if_not_start();
write!(
out,
"/* Generated with cbindgen:{} */",
crate::bindgen::config::VERSION
);
out.new_line();
}
if let Some(ref f) = self.config.autogen_warning {
out.new_line_if_not_start();
write!(out, "{}", f);
out.new_line();
}
if self.config.no_includes
&& self.config.sys_includes().is_empty()
&& self.config.includes().is_empty()
&& (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython)
&& self.config.after_includes.is_none()
{
return;
}
out.new_line_if_not_start();
if !self.config.no_includes {
match self.config.language {
Language::C => {
out.write("#include <stdarg.h>");
out.new_line();
out.write("#include <stdbool.h>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <stddef.h>");
out.new_line();
}
out.write("#include <stdint.h>");
out.new_line();
out.write("#include <stdlib.h>");
out.new_line();
}
Language::Cxx => {
out.write("#include <cstdarg>");
out.new_line();
if self.config.usize_is_size_t {
out.write("#include <cstddef>");
out.new_line();
}
out.write("#include <cstdint>");
out.new_line();
out.write("#include <cstdlib>");
out.new_line();
out.write("#include <ostream>");
out.new_line();
out.write("#include <new>");
out.new_line();
if self.config.enumeration.cast_assert_name.is_none()
&& (self.config.enumeration.derive_mut_casts
|| self.config.enumeration.derive_const_casts)
{
out.write("#include <cassert>");
out.new_line();
}
}
Language::Cython => {
out.write(
"from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t",
);
out.new_line();
out.write(
"from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t",
);
out.new_line();
out.write("cdef extern from *");
out.open_brace();
out.write("ctypedef bint bool");
out.new_line();
out.write("ctypedef struct va_list");
out.new_line();
out.close_brace(false);
}
}
}
for include in self.config.sys_includes() {
write!(out, "#include <{}>", include);
out.new_line();
}
for include in self.config.includes() {
write!(out, "#include \"{}\"", include);
out.new_line();
}
if self.config.language == Language::Cython {
for (module, names) in &self.config.cython.cimports {
write!(out, "from {} cimport {}", module, names.join(", "));
out.new_line();
}
}
if let Some(ref line) = self.config.after_includes {
write!(out, "{}", line);
out.new_line();
}
}
pub fn write<F: Write>(&self, file: F) {
let mut out = SourceWriter::new(file, self);
self.write_headers(&mut out);
self.open_namespaces(&mut out);
for constant in &self.constants {
if constant.uses_only_primitive_types() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
for item in &self.items {
if item
.deref()
.annotations()
.bool("no-export")
.unwrap_or(false)
{
continue;
}
out.new_line_if_not_start();
match *item {
ItemContainer::Constant(..) => unreachable!(),
ItemContainer::Static(..) => unreachable!(),
ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
}
out.new_line();
}
for constant in &self.constants {
if !constant.uses_only_primitive_types() {
out.new_line_if_not_start();
constant.write(&self.config, &mut out, None);
out.new_line();
}
}
if !self.functions.is_empty() || !self.globals.is_empty() {
if self.config.cpp_compatible_c() {
out.new_line_if_not_start();
out.write("#ifdef __cplusplus");
}
if self.config.language == Language::Cxx {
if let Some(ref using_namespaces) = self.config.using_namespaces {
for namespace in using_namespaces {
out.new_line();
write!(out, "using namespace {};", namespace);
}
out.new_line();
}
}
if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
out.new_line();
out.write("extern \"C\" {");
out.new_line();
}
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
for global in &self.globals {
out.new_line_if_not_start();
global.write(&self.config, &mut out);
out.new_line();
}
for function in &self.functions {
out.new_line_if_not_start();
function.write(&self.config, &mut out);
out.new_line();
}
if self.config.cpp_compatible_c() {
out.new_line();
out.write("#ifdef __cplusplus");
}
if self.config.language == Language::Cxx || self.config.cpp_compatible_c() {
out.new_line();
out.write("} // extern \"C\"");
out.new_line();
}
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
}
if self.config.language == Language::Cython
&& self.globals.is_empty()
&& self.constants.is_empty()
&& self.items.is_empty()
&& self.functions.is_empty()
{
out.write("pass");
}
self.close_namespaces(&mut out);
if let Some(f) = self.config.include_guard() {
out.new_line_if_not_start();
if self.config.language == Language::C {
write!(out, "#endif /* {} */", f);
} else {
write!(out, "#endif // {}", f);
}
out.new_line();
}
if let Some(ref f) = self.config.trailer {
out.new_line_if_not_start();
write!(out, "{}", f);
if !f.ends_with('\n') {
out.new_line();
}
}
}
fn all_namespaces(&self) -> Vec<&str> {
if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() {
return vec![];
}
let mut ret = vec![];
if let Some(ref namespace) = self.config.namespace {
ret.push(&**namespace);
}
if let Some(ref namespaces) = self.config.namespaces {
for namespace in namespaces {
ret.push(&**namespace);
}
}
ret
}
fn open_close_namespaces<F: Write>(&self, op: NamespaceOperation, out: &mut SourceWriter<F>) {
if self.config.language == Language::Cython {
if op == NamespaceOperation::Open {
out.new_line();
let header = self.config.cython.header.as_deref().unwrap_or("*");
write!(out, "cdef extern from {}", header);
out.open_brace();
} else {
out.close_brace(false);
}
return;
}
let mut namespaces = self.all_namespaces();
if namespaces.is_empty() {
return;
}
if op == NamespaceOperation::Close {
namespaces.reverse();
}
if self.config.cpp_compatible_c() {
out.new_line_if_not_start();
out.write("#ifdef __cplusplus");
}
for namespace in namespaces {
out.new_line();
match op {
NamespaceOperation::Open => write!(out, "namespace {} {{", namespace),
NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace),
}
}
out.new_line();
if self.config.cpp_compatible_c() {
out.write("#endif // __cplusplus");
out.new_line();
}
}
pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Open, out);
}
pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
self.open_close_namespaces(NamespaceOperation::Close, out);
}
}

View File

@@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use proc_macro2::TokenStream;
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
// $(#[$outer:meta])*
// ($($vis:tt)*) $BitFlags:ident: $T:ty {
// $(
// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
// )+
// }
#[derive(Debug)]
pub struct Bitflags {
attrs: Vec<syn::Attribute>,
vis: syn::Visibility,
struct_token: Token![struct],
name: syn::Ident,
colon_token: Token![:],
repr: syn::Type,
flags: Flags,
}
impl Bitflags {
pub fn expand(&self) -> (syn::ItemStruct, syn::ItemImpl) {
let Bitflags {
ref attrs,
ref vis,
ref name,
ref repr,
ref flags,
..
} = *self;
let struct_ = parse_quote! {
/// cbindgen:internal-derive-bitflags=true
#(#attrs)*
#vis struct #name {
bits: #repr,
}
};
let consts = flags.expand(name, repr);
let impl_ = parse_quote! {
impl #name {
#consts
}
};
(struct_, impl_)
}
}
impl Parse for Bitflags {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
vis: input.parse()?,
struct_token: input.parse()?,
name: input.parse()?,
colon_token: input.parse()?,
repr: input.parse()?,
flags: input.parse()?,
})
}
}
// $(#[$inner:ident $($args:tt)*])*
// const $Flag:ident = $value:expr;
#[derive(Debug)]
struct Flag {
attrs: Vec<syn::Attribute>,
const_token: Token![const],
name: syn::Ident,
equals_token: Token![=],
value: syn::Expr,
semicolon_token: Token![;],
}
impl Flag {
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
let Flag {
ref attrs,
ref name,
ref value,
..
} = *self;
quote! {
#(#attrs)*
pub const #name : #struct_name = #struct_name { bits: (#value) as #repr };
}
}
}
impl Parse for Flag {
fn parse(input: ParseStream) -> ParseResult<Self> {
Ok(Self {
attrs: input.call(syn::Attribute::parse_outer)?,
const_token: input.parse()?,
name: input.parse()?,
equals_token: input.parse()?,
value: input.parse()?,
semicolon_token: input.parse()?,
})
}
}
#[derive(Debug)]
struct Flags(Vec<Flag>);
impl Parse for Flags {
fn parse(input: ParseStream) -> ParseResult<Self> {
let content;
let _ = braced!(content in input);
let mut flags = vec![];
while !content.is_empty() {
flags.push(content.parse()?);
}
Ok(Flags(flags))
}
}
impl Flags {
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
let mut ts = quote! {};
for flag in &self.0 {
ts.extend(flag.expand(struct_name, repr));
}
ts
}
}
pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
let parser = Bitflags::parse;
parser.parse2(tokens)
}

View File

@@ -0,0 +1,396 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::path;
use crate::bindgen::bindings::Bindings;
use crate::bindgen::cargo::Cargo;
use crate::bindgen::config::{Braces, Config, Language, Profile, Style};
use crate::bindgen::error::Error;
use crate::bindgen::library::Library;
use crate::bindgen::parser::{self, Parse};
/// A builder for generating a bindings header.
#[derive(Debug, Clone)]
pub struct Builder {
config: Config,
srcs: Vec<path::PathBuf>,
lib: Option<(path::PathBuf, Option<String>)>,
lib_cargo: Option<Cargo>,
std_types: bool,
lockfile: Option<path::PathBuf>,
}
impl Builder {
#[allow(clippy::new_without_default)]
pub fn new() -> Builder {
Builder {
config: Config::default(),
srcs: Vec::new(),
lib: None,
lib_cargo: None,
std_types: true,
lockfile: None,
}
}
#[allow(unused)]
pub fn with_header<S: AsRef<str>>(mut self, header: S) -> Builder {
self.config.header = Some(String::from(header.as_ref()));
self
}
#[allow(unused)]
pub fn with_no_includes(mut self) -> Builder {
self.config.no_includes = true;
self
}
#[allow(unused)]
pub fn with_include<S: AsRef<str>>(mut self, include: S) -> Builder {
self.config.includes.push(String::from(include.as_ref()));
self
}
#[allow(unused)]
pub fn with_sys_include<S: AsRef<str>>(mut self, include: S) -> Builder {
self.config
.sys_includes
.push(String::from(include.as_ref()));
self
}
#[allow(unused)]
pub fn with_after_include<S: AsRef<str>>(mut self, line: S) -> Builder {
self.config.after_includes = Some(String::from(line.as_ref()));
self
}
#[allow(unused)]
pub fn with_trailer<S: AsRef<str>>(mut self, trailer: S) -> Builder {
self.config.trailer = Some(String::from(trailer.as_ref()));
self
}
#[allow(unused)]
pub fn with_include_guard<S: AsRef<str>>(mut self, include_guard: S) -> Builder {
self.config.include_guard = Some(String::from(include_guard.as_ref()));
self
}
#[allow(unused)]
pub fn with_pragma_once(mut self, pragma_once: bool) -> Builder {
self.config.pragma_once = pragma_once;
self
}
#[allow(unused)]
pub fn with_autogen_warning<S: AsRef<str>>(mut self, autogen_warning: S) -> Builder {
self.config.autogen_warning = Some(String::from(autogen_warning.as_ref()));
self
}
#[allow(unused)]
pub fn with_include_version(mut self, include_version: bool) -> Builder {
self.config.include_version = include_version;
self
}
#[allow(unused)]
pub fn with_namespace<S: AsRef<str>>(mut self, namespace: S) -> Builder {
self.config.namespace = Some(String::from(namespace.as_ref()));
self
}
#[allow(unused)]
pub fn with_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
self.config.namespaces = Some(
namespaces
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_using_namespaces<S: AsRef<str>>(mut self, namespaces: &[S]) -> Builder {
self.config.using_namespaces = Some(
namespaces
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_braces(mut self, braces: Braces) -> Builder {
self.config.braces = braces;
self
}
#[allow(unused)]
pub fn with_line_length(mut self, line_length: usize) -> Builder {
self.config.line_length = line_length;
self
}
#[allow(unused)]
pub fn with_tab_width(mut self, tab_width: usize) -> Builder {
self.config.tab_width = tab_width;
self
}
#[allow(unused)]
pub fn with_language(mut self, language: Language) -> Builder {
self.config.language = language;
self
}
#[allow(unused)]
pub fn with_style(mut self, style: Style) -> Builder {
self.config.style = style;
self
}
#[allow(unused)]
pub fn include_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
self.config
.export
.include
.push(String::from(item_name.as_ref()));
self
}
#[allow(unused)]
pub fn exclude_item<S: AsRef<str>>(mut self, item_name: S) -> Builder {
self.config
.export
.exclude
.push(String::from(item_name.as_ref()));
self
}
#[allow(unused)]
pub fn rename_item<S: AsRef<str>>(mut self, from: S, to: S) -> Builder {
self.config
.export
.rename
.insert(String::from(from.as_ref()), String::from(to.as_ref()));
self
}
#[allow(unused)]
pub fn with_item_prefix<S: AsRef<str>>(mut self, prefix: S) -> Builder {
self.config.export.prefix = Some(String::from(prefix.as_ref()));
self
}
#[allow(unused)]
pub fn with_parse_deps(mut self, parse_deps: bool) -> Builder {
self.config.parse.parse_deps = parse_deps;
self
}
#[allow(unused)]
pub fn with_parse_include<S: AsRef<str>>(mut self, include: &[S]) -> Builder {
self.config.parse.include =
Some(include.iter().map(|x| String::from(x.as_ref())).collect());
self
}
#[allow(unused)]
pub fn with_parse_exclude<S: AsRef<str>>(mut self, exclude: &[S]) -> Builder {
self.config.parse.exclude = exclude.iter().map(|x| String::from(x.as_ref())).collect();
self
}
#[allow(unused)]
pub fn with_parse_expand<S: AsRef<str>>(mut self, expand: &[S]) -> Builder {
self.config.parse.expand.crates = expand.iter().map(|x| String::from(x.as_ref())).collect();
self
}
#[allow(unused)]
pub fn with_parse_expand_all_features(mut self, expand_all_features: bool) -> Builder {
self.config.parse.expand.all_features = expand_all_features;
self
}
#[allow(unused)]
pub fn with_parse_expand_default_features(mut self, expand_default_features: bool) -> Builder {
self.config.parse.expand.default_features = expand_default_features;
self
}
#[allow(unused)]
pub fn with_parse_expand_features<S: AsRef<str>>(mut self, expand_features: &[S]) -> Builder {
self.config.parse.expand.features = Some(
expand_features
.iter()
.map(|x| String::from(x.as_ref()))
.collect(),
);
self
}
#[allow(unused)]
pub fn with_parse_expand_profile(mut self, profile: Profile) -> Builder {
self.config.parse.expand.profile = profile;
self
}
#[allow(unused)]
pub fn with_parse_extra_bindings<S: AsRef<str>>(mut self, extra_bindings: &[S]) -> Builder {
self.config.parse.extra_bindings = extra_bindings
.iter()
.map(|x| String::from(x.as_ref()))
.collect();
self
}
#[allow(unused)]
pub fn with_only_target_dependencies(mut self, only_target_dependencies: bool) -> Builder {
self.config.only_target_dependencies = only_target_dependencies;
self
}
#[allow(unused)]
pub fn with_documentation(mut self, documentation: bool) -> Builder {
self.config.documentation = documentation;
self
}
#[allow(unused)]
pub fn with_target_os_define(mut self, platform: &str, preprocessor_define: &str) -> Builder {
self.config.defines.insert(
format!("target_os = {}", platform),
preprocessor_define.to_owned(),
);
self
}
#[allow(unused)]
pub fn with_define(mut self, key: &str, value: &str, preprocessor_define: &str) -> Builder {
self.config.defines.insert(
format!("{} = {}", key, value),
preprocessor_define.to_owned(),
);
self
}
#[allow(unused)]
pub fn with_config(mut self, config: Config) -> Builder {
self.config = config;
self
}
#[allow(unused)]
pub fn with_std_types(mut self, std_types: bool) -> Builder {
self.std_types = std_types;
self
}
#[allow(unused)]
pub fn with_src<P: AsRef<path::Path>>(mut self, src: P) -> Builder {
self.srcs.push(src.as_ref().to_owned());
self
}
#[allow(unused)]
pub fn with_crate<P: AsRef<path::Path>>(mut self, lib_dir: P) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib = Some((path::PathBuf::from(lib_dir.as_ref()), None));
self
}
#[allow(unused)]
pub fn with_crate_and_name<P: AsRef<path::Path>, S: AsRef<str>>(
mut self,
lib_dir: P,
binding_lib_name: S,
) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib = Some((
path::PathBuf::from(lib_dir.as_ref()),
Some(String::from(binding_lib_name.as_ref())),
));
self
}
#[allow(unused)]
pub(crate) fn with_cargo(mut self, lib: Cargo) -> Builder {
debug_assert!(self.lib.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lib_cargo = Some(lib);
self
}
#[allow(unused)]
pub fn with_lockfile<P: AsRef<path::Path>>(mut self, lockfile: P) -> Builder {
debug_assert!(self.lockfile.is_none());
debug_assert!(self.lib_cargo.is_none());
self.lockfile = Some(path::PathBuf::from(lockfile.as_ref()));
self
}
pub fn generate(self) -> Result<Bindings, Error> {
let mut result = Parse::new();
if self.std_types {
result.add_std_types();
}
for x in &self.srcs {
result.extend_with(&parser::parse_src(x, &self.config)?);
}
if let Some((lib_dir, binding_lib_name)) = self.lib.clone() {
let lockfile = self.lockfile.as_ref().and_then(|p| p.to_str());
let cargo = Cargo::load(
&lib_dir,
lockfile,
binding_lib_name.as_deref(),
self.config.parse.parse_deps,
self.config.parse.clean,
self.config.only_target_dependencies,
/* existing_metadata = */ None,
)?;
result.extend_with(&parser::parse_lib(cargo, &self.config)?);
} else if let Some(cargo) = self.lib_cargo.clone() {
result.extend_with(&parser::parse_lib(cargo, &self.config)?);
}
Library::new(
self.config,
result.constants,
result.globals,
result.enums,
result.structs,
result.unions,
result.opaque_items,
result.typedefs,
result.functions,
)
.generate()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_style() {
assert_eq!(
Style::Tag,
Builder::new().with_style(Style::Tag).config.style
);
}
}

View File

@@ -0,0 +1,252 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::path::{Path, PathBuf};
use crate::bindgen::cargo::cargo_expand;
use crate::bindgen::cargo::cargo_lock::{self, Lock};
pub(crate) use crate::bindgen::cargo::cargo_metadata::PackageRef;
use crate::bindgen::cargo::cargo_metadata::{self, Metadata};
use crate::bindgen::cargo::cargo_toml;
use crate::bindgen::config::Profile;
use crate::bindgen::error::Error;
use crate::bindgen::ir::Cfg;
/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, Option<&str>) {
let split: Vec<&str> = dep_string.split_whitespace().collect();
(split[0], split.get(1).cloned())
}
/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
manifest_path: PathBuf,
binding_crate_name: String,
lock: Option<Lock>,
metadata: Metadata,
clean: bool,
}
impl Cargo {
/// Gather metadata from cargo for a specific library and binding crate
/// name. If dependency finding isn't needed then Cargo.lock files don't
/// need to be parsed.
pub(crate) fn load(
crate_dir: &Path,
lock_file: Option<&str>,
binding_crate_name: Option<&str>,
use_cargo_lock: bool,
clean: bool,
only_target_dependencies: bool,
existing_metadata_file: Option<&Path>,
) -> Result<Cargo, Error> {
let toml_path = crate_dir.join("Cargo.toml");
let metadata =
cargo_metadata::metadata(&toml_path, existing_metadata_file, only_target_dependencies)
.map_err(|x| Error::CargoMetadata(toml_path.to_str().unwrap().to_owned(), x))?;
let lock_path = lock_file
.map(PathBuf::from)
.unwrap_or_else(|| Path::new(&metadata.workspace_root).join("Cargo.lock"));
let lock = if use_cargo_lock {
match cargo_lock::lock(&lock_path) {
Ok(lock) => Some(lock),
Err(x) => {
warn!("Couldn't load lock file {:?}: {:?}", lock_path, x);
None
}
}
} else {
None
};
// Use the specified binding crate name or infer it from the manifest
let binding_crate_name = match binding_crate_name {
Some(s) => s.to_owned(),
None => {
let manifest = cargo_toml::manifest(&toml_path)
.map_err(|x| Error::CargoToml(toml_path.to_str().unwrap().to_owned(), x))?;
manifest.package.name
}
};
Ok(Cargo {
manifest_path: toml_path,
binding_crate_name,
lock,
metadata,
clean,
})
}
pub(crate) fn binding_crate_name(&self) -> &str {
&self.binding_crate_name
}
pub(crate) fn binding_crate_ref(&self) -> PackageRef {
match self.find_pkg_ref(&self.binding_crate_name) {
Some(pkg_ref) => pkg_ref,
None => panic!(
"Unable to find {} for {:?}",
self.binding_crate_name, self.manifest_path
),
}
}
pub(crate) fn dependencies(&self, package: &PackageRef) -> Vec<(PackageRef, Option<Cfg>)> {
let lock = match self.lock {
Some(ref lock) => lock,
None => return vec![],
};
let mut dependencies = None;
// Find the dependencies listing in the lockfile
if let Some(ref root) = lock.root {
// If the version is not on the lockfile then it shouldn't be
// ambiguous.
if root.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == root.version)
{
dependencies = root.dependencies.as_ref();
}
}
if dependencies.is_none() {
if let Some(ref lock_packages) = lock.package {
for lock_package in lock_packages {
if lock_package.name == package.name
&& package
.version
.as_ref()
.map_or(true, |v| *v == lock_package.version)
{
dependencies = lock_package.dependencies.as_ref();
break;
}
}
}
}
if dependencies.is_none() {
return vec![];
}
dependencies
.unwrap()
.iter()
.map(|dep| {
let (dep_name, dep_version) = parse_dep_string(dep);
// If a version was not specified find the only package with the name of the dependency
let dep_version = dep_version.or_else(|| {
let mut versions = self.metadata.packages.iter().filter_map(|package| {
if package.name_and_version.name != dep_name {
return None;
}
package.name_and_version.version.as_deref()
});
// If the iterator contains more items, meaning multiple versions of the same
// package are present, warn! amd abort.
let version = versions.next();
if versions.next().is_none() {
version
} else {
warn!("when looking for a version for package {}, multiple versions where found", dep_name);
None
}
});
// Try to find the cfgs in the Cargo.toml
let cfg = self
.metadata
.packages
.get(package)
.and_then(|meta_package| meta_package.dependencies.get(dep_name))
.and_then(|meta_dep| Cfg::load_metadata(meta_dep));
let package_ref = PackageRef {
name: dep_name.to_owned(),
version: dep_version.map(|v| v.to_owned()),
};
(package_ref, cfg)
})
.collect()
}
/// Finds the package reference in `cargo metadata` that has `package_name`
/// ignoring the version.
fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
for package in &self.metadata.packages {
if package.name_and_version.name == package_name {
return Some(package.name_and_version.clone());
}
}
None
}
/// Finds the directory for a specified package reference.
#[allow(unused)]
pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
Path::new(&meta_package.manifest_path)
.parent()
.map(|x| x.to_owned())
})
}
/// Finds `src/lib.rs` for a specified package reference.
pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
let kind_lib = String::from("lib");
let kind_staticlib = String::from("staticlib");
let kind_rlib = String::from("rlib");
let kind_cdylib = String::from("cdylib");
let kind_dylib = String::from("dylib");
self.metadata
.packages
.get(package)
.and_then(|meta_package| {
for target in &meta_package.targets {
if target.kind.contains(&kind_lib)
|| target.kind.contains(&kind_staticlib)
|| target.kind.contains(&kind_rlib)
|| target.kind.contains(&kind_cdylib)
|| target.kind.contains(&kind_dylib)
{
return Some(PathBuf::from(&target.src_path));
}
}
None
})
}
pub(crate) fn expand_crate(
&self,
package: &PackageRef,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, cargo_expand::Error> {
cargo_expand::expand(
&self.manifest_path,
&package.name,
package.version.as_deref(),
self.clean,
expand_all_features,
expand_default_features,
expand_features,
profile,
)
}
}

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::config::Profile;
use std::env;
use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::{from_utf8, Utf8Error};
extern crate tempfile;
use self::tempfile::Builder;
#[derive(Debug)]
/// Possible errors that can occur during `rustc -Zunpretty=expanded`.
pub enum Error {
/// Error during creation of temporary directory
Io(io::Error),
/// Output of `cargo metadata` was not valid utf8
Utf8(Utf8Error),
/// Error during execution of `cargo rustc -Zunpretty=expanded`
Compile(String),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Utf8(ref err) => err.fmt(f),
Error::Compile(ref err) => write!(f, "{}", err),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Utf8(ref err) => Some(err),
Error::Compile(..) => None,
}
}
}
/// Use rustc to expand and pretty print the crate into a single file,
/// removing any macros in the process.
#[allow(clippy::too_many_arguments)]
pub fn expand(
manifest_path: &Path,
crate_name: &str,
version: Option<&str>,
use_tempdir: bool,
expand_all_features: bool,
expand_default_features: bool,
expand_features: &Option<Vec<String>>,
profile: Profile,
) -> Result<String, Error> {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
let mut _temp_dir = None; // drop guard
if use_tempdir {
_temp_dir = Some(Builder::new().prefix("cbindgen-expand").tempdir()?);
cmd.env("CARGO_TARGET_DIR", _temp_dir.unwrap().path());
} else if let Ok(ref path) = env::var("CARGO_EXPAND_TARGET_DIR") {
cmd.env("CARGO_TARGET_DIR", path);
} else if let Ok(ref path) = env::var("OUT_DIR") {
// When cbindgen was started programatically from a build.rs file, Cargo is running and
// locking the default target directory. In this case we need to use another directory,
// else we would end up in a deadlock. If Cargo is running `OUT_DIR` will be set, so we
// can use a directory relative to that.
cmd.env("CARGO_TARGET_DIR", PathBuf::from(path).join("expanded"));
}
// Set this variable so that we don't call it recursively if we expand a crate that is using
// cbindgen
cmd.env("_CBINDGEN_IS_RUNNING", "1");
cmd.arg("rustc");
cmd.arg("--lib");
// When build with the release profile we can't choose the `check` profile.
if profile != Profile::Release {
cmd.arg("--profile=check");
}
cmd.arg("--manifest-path");
cmd.arg(manifest_path);
if let Some(features) = expand_features {
cmd.arg("--features");
let mut features_str = String::new();
for (index, feature) in features.iter().enumerate() {
if index != 0 {
features_str.push(' ');
}
features_str.push_str(feature);
}
cmd.arg(features_str);
}
if expand_all_features {
cmd.arg("--all-features");
}
if !expand_default_features {
cmd.arg("--no-default-features");
}
match profile {
Profile::Debug => {}
Profile::Release => {
cmd.arg("--release");
}
}
cmd.arg("-p");
let mut package = crate_name.to_owned();
if let Some(version) = version {
package.push(':');
package.push_str(version);
}
cmd.arg(&package);
cmd.arg("--verbose");
cmd.arg("--");
cmd.arg("-Zunpretty=expanded");
info!("Command: {:?}", cmd);
let output = cmd.output()?;
let src = from_utf8(&output.stdout)?.to_owned();
let error = from_utf8(&output.stderr)?.to_owned();
if src.is_empty() {
Err(Error::Compile(error))
} else {
Ok(src)
}
}

View File

@@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
#[derive(Debug)]
/// Possible errors that can occur during Cargo.toml parsing.
pub enum Error {
/// Error during reading of Cargo.toml
Io(io::Error),
/// Deserialization error
Toml(toml::de::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::Toml(err)
}
}
#[derive(Clone, Deserialize, Debug)]
pub struct Lock {
pub root: Option<Package>,
pub package: Option<Vec<Package>>,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Package {
pub name: String,
pub version: String,
/// A list of dependencies formatted like "NAME VERSION-OPT REGISTRY-OPT"
pub dependencies: Option<Vec<String>>,
}
/// Parse the Cargo.toml for a given path
pub fn lock(manifest_path: &Path) -> Result<Lock, Error> {
let mut s = String::new();
let mut f = File::open(manifest_path)?;
f.read_to_string(&mut s)?;
toml::from_str::<Lock>(&s).map_err(|x| x.into())
}

View File

@@ -0,0 +1,257 @@
#![deny(missing_docs)]
//! Structured access to the output of `cargo metadata`
//! Usually used from within a `cargo-*` executable
// Forked from `https://github.com/oli-obk/cargo_metadata`
// Modifications:
// 1. Remove `resolve` from Metadata because it was causing parse failures
// 2. Fix the `manifest-path` argument
// 3. Add `--all-features` argument
// 4. Remove the `--no-deps` argument
use std::borrow::{Borrow, Cow};
use std::collections::{HashMap, HashSet};
use std::env;
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::Path;
use std::process::{Command, Output};
use std::str::Utf8Error;
#[derive(Clone, Deserialize, Debug)]
/// Starting point for metadata returned by `cargo metadata`
pub struct Metadata {
/// A list of all crates referenced by this crate (and the crate itself)
pub packages: HashSet<Package>,
version: usize,
/// path to the workspace containing the `Cargo.lock`
pub workspace_root: String,
}
/// A reference to a package including it's name and the specific version.
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct PackageRef {
pub name: String,
pub version: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
/// A crate
pub struct Package {
#[serde(flatten)]
pub name_and_version: PackageRef,
id: String,
source: Option<String>,
/// List of dependencies of this particular package
pub dependencies: HashSet<Dependency>,
/// Targets provided by the crate (lib, bin, example, test, ...)
pub targets: Vec<Target>,
features: HashMap<String, Vec<String>>,
/// path containing the `Cargo.toml`
pub manifest_path: String,
}
#[derive(Clone, Deserialize, Debug)]
/// A dependency of the main crate
pub struct Dependency {
/// Name as given in the `Cargo.toml`
pub name: String,
source: Option<String>,
/// Whether this is required or optional
pub req: String,
kind: Option<String>,
optional: bool,
uses_default_features: bool,
features: Vec<String>,
pub target: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
/// A single target (lib, bin, example, ...) provided by a crate
pub struct Target {
/// Name as given in the `Cargo.toml` or generated from the file name
pub name: String,
/// Kind of target ("bin", "example", "test", "bench", "lib")
pub kind: Vec<String>,
/// Almost the same as `kind`, except when an example is a library instad of an executable.
/// In that case `crate_types` contains things like `rlib` and `dylib` while `kind` is `example`
#[serde(default)]
pub crate_types: Vec<String>,
/// Path to the main source file of the target
pub src_path: String,
}
#[derive(Debug)]
/// Possible errors that can occur during metadata parsing.
pub enum Error {
/// Error during execution of `cargo metadata`
Io(io::Error),
/// Metadata extraction failure
Metadata(Output),
/// Output of `cargo metadata` was not valid utf8
Utf8(Utf8Error),
/// Deserialization error (structure of json did not match expected structure)
Json(serde_json::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Json(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Metadata(_) => write!(f, "Metadata error"),
Error::Utf8(ref err) => err.fmt(f),
Error::Json(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Metadata(_) => None,
Error::Utf8(ref err) => Some(err),
Error::Json(ref err) => Some(err),
}
}
}
// Implementations that let us lookup Packages and Dependencies by name (string)
impl Borrow<PackageRef> for Package {
fn borrow(&self) -> &PackageRef {
&self.name_and_version
}
}
impl Hash for Package {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name_and_version.hash(state);
}
}
impl PartialEq for Package {
fn eq(&self, other: &Self) -> bool {
self.name_and_version == other.name_and_version
}
}
impl Eq for Package {}
impl Borrow<str> for Dependency {
fn borrow(&self) -> &str {
&self.name
}
}
impl Hash for Dependency {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for Dependency {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for Dependency {}
fn discover_target(manifest_path: &Path) -> Option<String> {
if let Ok(target) = std::env::var("TARGET") {
return Some(target);
}
// We must be running as a standalone script, not under cargo.
// Let's use the host platform instead.
// We figure out the host platform through rustc and use that.
// We unfortunatelly cannot go through cargo, since cargo rustc _also_ builds.
// If `rustc` fails to run, we just fall back to not passing --filter-platforms.
//
// NOTE: We set the current directory in case of rustup shenanigans.
let rustc = env::var("RUSTC").unwrap_or_else(|_| String::from("rustc"));
debug!("Discovering host platform by {:?}", rustc);
let rustc_output = Command::new(rustc)
.current_dir(manifest_path.parent().unwrap())
.arg("-vV")
.output();
let rustc_output = match rustc_output {
Ok(ref out) => String::from_utf8_lossy(&out.stdout),
Err(..) => return None,
};
let field = "host: ";
rustc_output.lines().find_map(|l| {
// XXX l.strip_prefix(field) re-implemented to preserve MSRV
if l.starts_with(field) {
Some(l[field.len()..].into())
} else {
None
}
})
}
/// The main entry point to obtaining metadata
pub fn metadata(
manifest_path: &Path,
existing_metadata_file: Option<&Path>,
only_target: bool,
) -> Result<Metadata, Error> {
let output;
let metadata = match existing_metadata_file {
Some(path) => Cow::Owned(std::fs::read_to_string(path)?),
None => {
let target = if only_target {
let target = discover_target(manifest_path);
if target.is_none() {
warn!(
"Failed to discover host platform for cargo metadata; \
will fetch dependencies for all platforms."
);
}
target
} else {
None
};
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
cmd.arg("metadata");
cmd.arg("--all-features");
cmd.arg("--format-version").arg("1");
if let Some(target) = target {
cmd.arg("--filter-platform").arg(target);
}
cmd.arg("--manifest-path");
cmd.arg(manifest_path);
output = cmd.output()?;
if !output.status.success() {
return Err(Error::Metadata(output));
}
Cow::Borrowed(std::str::from_utf8(&output.stdout)?)
}
};
let meta: Metadata = serde_json::from_str(&*metadata)?;
Ok(meta)
}

View File

@@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::error;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
#[derive(Debug)]
/// Possible errors that can occur during Cargo.toml parsing.
pub enum Error {
/// Error during reading of Cargo.toml
Io(io::Error),
/// Deserialization error
Toml(toml::de::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<toml::de::Error> for Error {
fn from(err: toml::de::Error) -> Self {
Error::Toml(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Toml(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Toml(ref err) => Some(err),
}
}
}
#[derive(Clone, Deserialize, Debug)]
pub struct Manifest {
pub package: Package,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Package {
pub name: String,
}
/// Parse the Cargo.toml for a given path
pub fn manifest(manifest_path: &Path) -> Result<Manifest, Error> {
let mut s = String::new();
let mut f = File::open(manifest_path)?;
f.read_to_string(&mut s)?;
toml::from_str::<Manifest>(&s).map_err(|x| x.into())
}

View File

@@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[allow(clippy::module_inception)]
mod cargo;
pub(crate) mod cargo_expand;
pub(crate) mod cargo_lock;
pub(crate) mod cargo_metadata;
pub(crate) mod cargo_toml;
pub(crate) use self::cargo::*;

View File

@@ -0,0 +1,325 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::declarationtyperesolver::DeclarationType;
use crate::bindgen::ir::{ArrayLength, Function, Type};
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::{Config, Language};
// This code is for translating Rust types into C declarations.
// See Section 6.7, Declarations, in the C standard for background.
// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
enum CDeclarator {
Ptr {
is_const: bool,
is_nullable: bool,
is_ref: bool,
},
Array(String),
Func(Vec<(Option<String>, CDecl)>, bool),
}
impl CDeclarator {
fn is_ptr(&self) -> bool {
match self {
CDeclarator::Ptr { .. } | CDeclarator::Func(..) => true,
_ => false,
}
}
}
struct CDecl {
type_qualifers: String,
type_name: String,
type_generic_args: Vec<Type>,
declarators: Vec<CDeclarator>,
type_ctype: Option<DeclarationType>,
}
impl CDecl {
fn new() -> CDecl {
CDecl {
type_qualifers: String::new(),
type_name: String::new(),
type_generic_args: Vec::new(),
declarators: Vec::new(),
type_ctype: None,
}
}
fn from_type(t: &Type, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
cdecl.build_type(t, false, config);
cdecl
}
fn from_func_arg(t: &Type, array_length: Option<&str>, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
let length = match array_length {
Some(l) => l,
None => return CDecl::from_type(t, config),
};
let (ty, is_const) = match t {
Type::Ptr { ty, is_const, .. } => (ty, is_const),
_ => unreachable!(
"Should never have an array length for a non pointer type {:?}",
t
),
};
let ptr_as_array = Type::Array(ty.clone(), ArrayLength::Value(length.to_string()));
cdecl.build_type(&ptr_as_array, *is_const, config);
cdecl
}
fn from_func(f: &Function, layout_vertical: bool, config: &Config) -> CDecl {
let mut cdecl = CDecl::new();
cdecl.build_func(f, layout_vertical, config);
cdecl
}
fn build_func(&mut self, f: &Function, layout_vertical: bool, config: &Config) {
let args = f
.args
.iter()
.map(|arg| {
(
arg.name.clone(),
CDecl::from_func_arg(&arg.ty, arg.array_length.as_deref(), config),
)
})
.collect();
self.declarators
.push(CDeclarator::Func(args, layout_vertical));
self.build_type(&f.ret, false, config);
}
fn build_type(&mut self, t: &Type, is_const: bool, config: &Config) {
match t {
Type::Path(ref generic) => {
if is_const {
assert!(
self.type_qualifers.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_qualifers = "const".to_owned();
}
assert!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_name = generic.export_name().to_owned();
assert!(
self.type_generic_args.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_generic_args = generic.generics().to_owned();
self.type_ctype = generic.ctype().cloned();
}
Type::Primitive(ref p) => {
if is_const {
assert!(
self.type_qualifers.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_qualifers = "const".to_owned();
}
assert!(
self.type_name.is_empty(),
"error generating cdecl for {:?}",
t
);
self.type_name = p.to_repr_c(config).to_string();
}
Type::Ptr {
ref ty,
is_nullable,
is_const: ptr_is_const,
is_ref,
} => {
self.declarators.push(CDeclarator::Ptr {
is_const,
is_nullable: *is_nullable,
is_ref: *is_ref,
});
self.build_type(ty, *ptr_is_const, config);
}
Type::Array(ref t, ref constant) => {
let len = constant.as_str().to_owned();
self.declarators.push(CDeclarator::Array(len));
self.build_type(t, is_const, config);
}
Type::FuncPtr {
ref ret,
ref args,
is_nullable: _,
} => {
let args = args
.iter()
.map(|(ref name, ref ty)| (name.clone(), CDecl::from_type(ty, config)))
.collect();
self.declarators.push(CDeclarator::Ptr {
is_const: false,
is_nullable: true,
is_ref: false,
});
self.declarators.push(CDeclarator::Func(args, false));
self.build_type(ret, false, config);
}
}
}
fn write<F: Write>(&self, out: &mut SourceWriter<F>, ident: Option<&str>, config: &Config) {
// Write the type-specifier and type-qualifier first
if !self.type_qualifers.is_empty() {
write!(out, "{} ", self.type_qualifers);
}
if config.language != Language::Cython {
if let Some(ref ctype) = self.type_ctype {
write!(out, "{} ", ctype.to_str());
}
}
write!(out, "{}", self.type_name);
if !self.type_generic_args.is_empty() {
out.write("<");
out.write_horizontal_source_list(&self.type_generic_args, ListType::Join(", "));
out.write(">");
}
// When we have an identifier, put a space between the type and the declarators
if ident.is_some() {
out.write(" ");
}
// Write the left part of declarators before the identifier
let mut iter_rev = self.declarators.iter().rev().peekable();
#[allow(clippy::while_let_on_iterator)]
while let Some(declarator) = iter_rev.next() {
let next_is_pointer = iter_rev.peek().map_or(false, |x| x.is_ptr());
match *declarator {
CDeclarator::Ptr {
is_const,
is_nullable,
is_ref,
} => {
out.write(if is_ref { "&" } else { "*" });
if is_const {
out.write("const ");
}
if !is_nullable && !is_ref && config.language != Language::Cython {
if let Some(attr) = &config.pointer.non_null_attribute {
write!(out, "{} ", attr);
}
}
}
CDeclarator::Array(..) => {
if next_is_pointer {
out.write("(");
}
}
CDeclarator::Func(..) => {
if next_is_pointer {
out.write("(");
}
}
}
}
// Write the identifier
if let Some(ident) = ident {
write!(out, "{}", ident);
}
// Write the right part of declarators after the identifier
let mut iter = self.declarators.iter();
let mut last_was_pointer = false;
#[allow(clippy::while_let_on_iterator)]
while let Some(declarator) = iter.next() {
match *declarator {
CDeclarator::Ptr { .. } => {
last_was_pointer = true;
}
CDeclarator::Array(ref constant) => {
if last_was_pointer {
out.write(")");
}
write!(out, "[{}]", constant);
last_was_pointer = false;
}
CDeclarator::Func(ref args, layout_vertical) => {
if last_was_pointer {
out.write(")");
}
out.write("(");
if args.is_empty() && config.language == Language::C {
out.write("void");
}
if layout_vertical {
let align_length = out.line_length_for_align();
out.push_set_spaces(align_length);
for (i, &(ref arg_ident, ref arg_ty)) in args.iter().enumerate() {
if i != 0 {
out.write(",");
out.new_line();
}
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
arg_ty.write(out, arg_ident, config);
}
out.pop_tab();
} else {
for (i, &(ref arg_ident, ref arg_ty)) in args.iter().enumerate() {
if i != 0 {
out.write(", ");
}
// Convert &Option<String> to Option<&str>
let arg_ident = arg_ident.as_ref().map(|x| x.as_ref());
arg_ty.write(out, arg_ident, config);
}
}
out.write(")");
last_was_pointer = true;
}
}
}
}
}
pub fn write_func<F: Write>(
out: &mut SourceWriter<F>,
f: &Function,
layout_vertical: bool,
config: &Config,
) {
CDecl::from_func(f, layout_vertical, config).write(out, Some(f.path().name()), config);
}
pub fn write_field<F: Write>(out: &mut SourceWriter<F>, t: &Type, ident: &str, config: &Config) {
CDecl::from_type(t, config).write(out, Some(ident), config);
}
pub fn write_type<F: Write>(out: &mut SourceWriter<F>, t: &Type, config: &Config) {
CDecl::from_type(t, config).write(out, None, config);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::ir::Path;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
impl DeclarationType {
pub fn to_str(self) -> &'static str {
match self {
DeclarationType::Struct => "struct",
DeclarationType::Enum => "enum",
DeclarationType::Union => "union",
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum DeclarationType {
Struct,
Enum,
Union,
}
#[derive(Default)]
pub struct DeclarationTypeResolver {
types: HashMap<Path, Option<DeclarationType>>,
}
impl DeclarationTypeResolver {
fn insert(&mut self, path: &Path, ty: Option<DeclarationType>) {
if let Entry::Vacant(vacant_entry) = self.types.entry(path.clone()) {
vacant_entry.insert(ty);
}
}
pub fn add_enum(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Enum));
}
pub fn add_struct(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Struct));
}
pub fn add_union(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Union));
}
pub fn add_none(&mut self, path: &Path) {
self.insert(path, None);
}
pub fn type_for(&self, path: &Path) -> Option<DeclarationType> {
*self.types.get(path)?
}
}

View File

@@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp::Ordering;
use std::collections::HashSet;
use crate::bindgen::ir::{ItemContainer, Path};
/// A dependency list is used for gathering what order to output the types.
#[derive(Default)]
pub struct Dependencies {
pub order: Vec<ItemContainer>,
pub items: HashSet<Path>,
}
impl Dependencies {
pub fn new() -> Dependencies {
Dependencies {
order: Vec::new(),
items: HashSet::new(),
}
}
pub fn sort(&mut self) {
// Sort untagged enums and opaque structs into their own layers because they don't
// depend on each other or anything else.
let ordering = |a: &ItemContainer, b: &ItemContainer| match (a, b) {
(&ItemContainer::Enum(ref x), &ItemContainer::Enum(ref y))
if x.tag.is_none() && y.tag.is_none() =>
{
x.path.cmp(&y.path)
}
(&ItemContainer::Enum(ref x), _) if x.tag.is_none() => Ordering::Less,
(_, &ItemContainer::Enum(ref x)) if x.tag.is_none() => Ordering::Greater,
(&ItemContainer::OpaqueItem(ref x), &ItemContainer::OpaqueItem(ref y)) => {
x.path.cmp(&y.path)
}
(&ItemContainer::OpaqueItem(_), _) => Ordering::Less,
(_, &ItemContainer::OpaqueItem(_)) => Ordering::Greater,
_ => Ordering::Equal,
};
self.order.sort_by(ordering);
}
}

View File

@@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::error;
use std::fmt;
pub use crate::bindgen::cargo::cargo_expand::Error as CargoExpandError;
pub use crate::bindgen::cargo::cargo_metadata::Error as CargoMetadataError;
pub use crate::bindgen::cargo::cargo_toml::Error as CargoTomlError;
pub use syn::parse::Error as ParseError;
#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub enum Error {
CargoMetadata(String, CargoMetadataError),
CargoToml(String, CargoTomlError),
CargoExpand(String, CargoExpandError),
ParseSyntaxError {
crate_name: String,
src_path: String,
error: ParseError,
},
ParseCannotOpenFile {
crate_name: String,
src_path: String,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::CargoMetadata(ref path, ref error) => write!(
f,
"Couldn't execute `cargo metadata` with manifest {:?}: {:?}",
path, error
),
Error::CargoToml(ref path, ref error) => {
write!(f, "Couldn't load manifest file {:?}: {:?}", path, error)
}
Error::CargoExpand(ref crate_name, ref error) => write!(
f,
"Parsing crate `{}`: couldn't run `cargo rustc -Zunpretty=expanded`: {:?}",
crate_name, error
),
Error::ParseSyntaxError {
ref crate_name,
ref src_path,
ref error,
} => {
write!(
f,
"Parsing crate `{}`:`{}`:\n{:?}",
crate_name, src_path, error
)?;
if !src_path.is_empty() {
write!(
f,
"\nTry running `rustc -Z parse-only {}` to see a nicer error message",
src_path,
)?
}
Ok(())
}
Error::ParseCannotOpenFile {
ref crate_name,
ref src_path,
} => write!(
f,
"Parsing crate `{}`: cannot open file `{}`.",
crate_name, src_path
),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::CargoMetadata(_, ref error) => Some(error),
Error::CargoToml(_, ref error) => Some(error),
Error::CargoExpand(_, ref error) => Some(error),
Error::ParseSyntaxError { ref error, .. } => Some(error),
Error::ParseCannotOpenFile { .. } => None,
}
}
}

View File

@@ -0,0 +1,179 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::str::FromStr;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::utilities::SynAttributeHelpers;
// A system for specifying properties on items. Annotations are
// given through document comments and parsed by this code.
//
// An annotation is in the form cbindgen:PROPERTY=VALUE
// Where PROPERTY depends on the item
// Where VALUE can be
// * list - [Item1, Item2, Item3, ...]
// * atom - Foo
// * bool - true,false
// Examples:
// * cbindgen:field-names=[mHandle, mNamespace]
// * cbindgen:function-postfix=WR_DESTRUCTOR_SAFE
/// A value specified by an annotation.
#[derive(Debug, Clone)]
pub enum AnnotationValue {
List(Vec<String>),
Atom(Option<String>),
Bool(bool),
}
/// A set of annotations specified by a document comment.
#[derive(Debug, Default, Clone)]
pub struct AnnotationSet {
annotations: HashMap<String, AnnotationValue>,
pub must_use: bool,
}
impl AnnotationSet {
pub fn new() -> AnnotationSet {
AnnotationSet {
annotations: HashMap::new(),
must_use: false,
}
}
pub fn is_empty(&self) -> bool {
self.annotations.is_empty() && !self.must_use
}
pub(crate) fn must_use(&self, config: &Config) -> bool {
self.must_use && config.language != Language::Cython
}
pub fn load(attrs: &[syn::Attribute]) -> Result<AnnotationSet, String> {
let lines = attrs.get_comment_lines();
let lines: Vec<&str> = lines
.iter()
.filter_map(|line| {
let line = line.trim_start();
if !line.starts_with("cbindgen:") {
return None;
}
Some(line)
})
.collect();
let must_use = attrs.has_attr_word("must_use");
let mut annotations = HashMap::new();
// Look at each line for an annotation
for line in lines {
debug_assert!(line.starts_with("cbindgen:"));
// Remove the "cbindgen:" prefix
let annotation = &line[9..];
// Split the annotation in two
let parts: Vec<&str> = annotation.split('=').map(|x| x.trim()).collect();
if parts.len() > 2 {
return Err(format!("Couldn't parse {}.", line));
}
// Grab the name that this annotation is modifying
let name = parts[0];
// If the annotation only has a name, assume it's setting a bool flag
if parts.len() == 1 {
annotations.insert(name.to_string(), AnnotationValue::Bool(true));
continue;
}
// Parse the value we're setting the name to
let value = parts[1];
if let Some(x) = parse_list(value) {
annotations.insert(name.to_string(), AnnotationValue::List(x));
continue;
}
if let Ok(x) = value.parse::<bool>() {
annotations.insert(name.to_string(), AnnotationValue::Bool(x));
continue;
}
annotations.insert(
name.to_string(),
if value.is_empty() {
AnnotationValue::Atom(None)
} else {
AnnotationValue::Atom(Some(value.to_string()))
},
);
}
Ok(AnnotationSet {
annotations,
must_use,
})
}
/// Adds an annotation value if none is specified.
pub fn add_default(&mut self, name: &str, value: AnnotationValue) {
if let Entry::Vacant(e) = self.annotations.entry(name.to_string()) {
e.insert(value);
}
}
pub fn list(&self, name: &str) -> Option<Vec<String>> {
match self.annotations.get(name) {
Some(&AnnotationValue::List(ref x)) => Some(x.clone()),
_ => None,
}
}
pub fn atom(&self, name: &str) -> Option<Option<String>> {
match self.annotations.get(name) {
Some(&AnnotationValue::Atom(ref x)) => Some(x.clone()),
_ => None,
}
}
pub fn bool(&self, name: &str) -> Option<bool> {
match self.annotations.get(name) {
Some(&AnnotationValue::Bool(ref x)) => Some(*x),
_ => None,
}
}
pub fn parse_atom<T>(&self, name: &str) -> Option<T>
where
T: Default + FromStr,
{
match self.annotations.get(name) {
Some(&AnnotationValue::Atom(ref x)) => Some(
x.as_ref()
.map_or(T::default(), |y| y.parse::<T>().ok().unwrap()),
),
_ => None,
}
}
}
/// Parse lists like "[x, y, z]". This is not implemented efficiently or well.
fn parse_list(list: &str) -> Option<Vec<String>> {
if list.len() < 2 {
return None;
}
match (list.chars().next(), list.chars().last()) {
(Some('['), Some(']')) => Some(
list[1..list.len() - 1]
.split(',')
.map(|x| x.trim().to_string())
.collect(),
),
_ => None,
}
}

View File

@@ -0,0 +1,365 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fmt;
use std::io::Write;
use crate::bindgen::cargo::cargo_metadata::Dependency;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::writer::SourceWriter;
#[derive(PartialEq, Eq)]
enum DefineKey<'a> {
Boolean(&'a str),
Named(&'a str, &'a str),
}
impl<'a> DefineKey<'a> {
fn load(key: &str) -> DefineKey {
// TODO: dirty parser
if !key.contains('=') {
return DefineKey::Boolean(key);
}
let mut splits = key.trim().split('=');
let name = match splits.next() {
Some(n) => n.trim(),
None => return DefineKey::Boolean(key),
};
let value = match splits.next() {
Some(v) => v.trim(),
None => return DefineKey::Boolean(key),
};
if splits.next().is_some() {
return DefineKey::Boolean(key);
}
DefineKey::Named(name, value)
}
}
#[derive(Debug, Clone)]
pub enum Cfg {
Boolean(String),
Named(String, String),
Any(Vec<Cfg>),
All(Vec<Cfg>),
Not(Box<Cfg>),
}
impl fmt::Display for Cfg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Cfg::Boolean(key) => write!(f, "{}", key),
Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
Cfg::Any(cfgs) => {
write!(f, "any(")?;
for (index, cfg) in cfgs.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", cfg)?;
}
write!(f, ")")
}
Cfg::All(cfgs) => {
write!(f, "all(")?;
for (index, cfg) in cfgs.iter().enumerate() {
if index > 0 {
write!(f, ", ")?;
}
write!(f, "{}", cfg)?;
}
write!(f, ")")
}
Cfg::Not(cfg) => write!(f, "not({})", cfg),
}
}
}
impl Cfg {
pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
if cfgs.is_empty() {
None
} else {
Some(Cfg::All(cfgs.to_owned()))
}
}
pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
match (parent, child) {
(None, None) => None,
(None, Some(child)) => Some(child),
(Some(parent), None) => Some(parent.clone()),
(Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
}
}
pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
let mut configs = Vec::new();
for attr in attrs {
if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
if !path.is_ident("cfg") || nested.len() != 1 {
continue;
}
if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
configs.push(config);
}
}
}
match configs.len() {
0 => None,
1 => Some(configs.pop().unwrap()),
_ => Some(Cfg::All(configs)),
}
}
pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
let target = dependency.target.as_ref()?;
match syn::parse_str::<syn::Meta>(target) {
Ok(target) => {
// Parsing succeeded using the #[cfg] syntax
if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
if !path.is_ident("cfg") || nested.len() != 1 {
return None;
}
Cfg::load_single(nested.first().unwrap())
} else {
None
}
}
Err(_) => {
// Parsing failed using #[cfg], this may be a literal target
// name
Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
target,
proc_macro2::Span::call_site(),
))))
}
}
}
fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
Some(match *item {
syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
}
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
ref path,
lit: syn::Lit::Str(ref value),
..
})) => Cfg::Named(
format!("{}", path.segments.first().unwrap().ident),
value.value(),
),
syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
ref path,
ref nested,
..
})) => {
if path.is_ident("any") {
Cfg::Any(Cfg::load_list(nested.iter())?)
} else if path.is_ident("all") {
Cfg::All(Cfg::load_list(nested.iter())?)
} else if path.is_ident("not") {
if nested.len() != 1 {
return None;
}
Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
} else {
return None;
}
}
_ => return None,
})
}
fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
let mut configs = Vec::new();
for attr in attrs {
configs.push(Cfg::load_single(attr)?);
}
if configs.is_empty() {
None
} else {
Some(configs)
}
}
}
pub trait ToCondition: Sized {
fn to_condition(&self, config: &Config) -> Option<Condition>;
}
impl<'a> ToCondition for Option<Cfg> {
fn to_condition(&self, config: &Config) -> Option<Condition> {
self.as_ref()?.to_condition(config)
}
}
impl<'a> ToCondition for Cfg {
fn to_condition(&self, config: &Config) -> Option<Condition> {
match *self {
Cfg::Boolean(ref cfg_name) => {
let define = config
.defines
.iter()
.find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
if let Some((_, define)) = define {
Some(Condition::Define(define.to_owned()))
} else {
warn!(
"Missing `[defines]` entry for `{}` in cbindgen config.",
self,
);
None
}
}
Cfg::Named(ref cfg_name, ref cfg_value) => {
let define = config.defines.iter().find(|(key, ..)| {
DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
});
if let Some((_, define)) = define {
Some(Condition::Define(define.to_owned()))
} else {
warn!(
"Missing `[defines]` entry for `{}` in cbindgen config.",
self,
);
None
}
}
Cfg::Any(ref children) => {
let conditions: Vec<_> = children
.iter()
.filter_map(|x| x.to_condition(config))
.collect();
match conditions.len() {
0 => None,
1 => conditions.into_iter().next(),
_ => Some(Condition::Any(conditions)),
}
}
Cfg::All(ref children) => {
let cfgs: Vec<_> = children
.iter()
.filter_map(|x| x.to_condition(config))
.collect();
match cfgs.len() {
0 => None,
1 => cfgs.into_iter().next(),
_ => Some(Condition::All(cfgs)),
}
}
Cfg::Not(ref child) => child
.to_condition(config)
.map(|cfg| Condition::Not(Box::new(cfg))),
}
}
}
#[derive(Debug, Clone)]
pub enum Condition {
Define(String),
Any(Vec<Condition>),
All(Vec<Condition>),
Not(Box<Condition>),
}
impl Condition {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match *self {
Condition::Define(ref define) => {
if config.language == Language::Cython {
write!(out, "{}", define);
} else {
out.write("defined(");
write!(out, "{}", define);
out.write(")");
}
}
Condition::Any(ref conditions) => {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(if config.language == Language::Cython {
" or "
} else {
" || "
});
}
condition.write(config, out);
}
out.write(")");
}
Condition::All(ref conditions) => {
out.write("(");
for (i, condition) in conditions.iter().enumerate() {
if i != 0 {
out.write(if config.language == Language::Cython {
" and "
} else {
" && "
});
}
condition.write(config, out);
}
out.write(")");
}
Condition::Not(ref condition) => {
out.write(if config.language == Language::Cython {
"not "
} else {
"!"
});
condition.write(config, out);
}
}
}
}
pub trait ConditionWrite {
fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
}
impl ConditionWrite for Option<Condition> {
fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if let Some(ref cfg) = *self {
if config.language == Language::Cython {
out.write("IF ");
cfg.write(config, out);
out.open_brace();
} else {
out.push_set_spaces(0);
out.write("#if ");
cfg.write(config, out);
out.pop_set_spaces();
out.new_line();
}
}
}
fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.is_some() {
if config.language == Language::Cython {
out.close_brace(false);
} else {
out.new_line();
out.push_set_spaces(0);
out.write("#endif");
out.pop_set_spaces();
}
}
}
}

View File

@@ -0,0 +1,603 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::collections::HashMap;
use std::io::Write;
use syn::{self, UnOp};
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
Struct, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
use crate::bindgen::Bindings;
#[derive(Debug, Clone)]
pub enum Literal {
Expr(String),
Path(String),
PostfixUnaryOp {
op: &'static str,
value: Box<Literal>,
},
BinOp {
left: Box<Literal>,
op: &'static str,
right: Box<Literal>,
},
Struct {
path: Path,
export_name: String,
fields: HashMap<String, Literal>,
},
Cast {
ty: Type,
value: Box<Literal>,
},
}
impl Literal {
fn replace_self_with(&mut self, self_ty: &Path) {
match *self {
Literal::PostfixUnaryOp { ref mut value, .. } => {
value.replace_self_with(self_ty);
}
Literal::BinOp {
ref mut left,
ref mut right,
..
} => {
left.replace_self_with(self_ty);
right.replace_self_with(self_ty);
}
Literal::Struct {
ref mut path,
ref mut export_name,
ref mut fields,
} => {
if path.replace_self_with(self_ty) {
*export_name = self_ty.name().to_owned();
}
for ref mut expr in fields.values_mut() {
expr.replace_self_with(self_ty);
}
}
Literal::Cast {
ref mut ty,
ref mut value,
} => {
ty.replace_self_with(self_ty);
value.replace_self_with(self_ty);
}
Literal::Expr(..) | Literal::Path(..) => {}
}
}
fn is_valid(&self, bindings: &Bindings) -> bool {
match *self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
Literal::BinOp {
ref left,
ref right,
..
} => left.is_valid(bindings) && right.is_valid(bindings),
Literal::Struct { ref path, .. } => bindings.struct_exists(path),
Literal::Cast { ref value, .. } => value.is_valid(bindings),
}
}
pub fn uses_only_primitive_types(&self) -> bool {
match self {
Literal::Expr(..) => true,
Literal::Path(..) => true,
Literal::PostfixUnaryOp { ref value, .. } => value.uses_only_primitive_types(),
Literal::BinOp {
ref left,
ref right,
..
} => left.uses_only_primitive_types() & right.uses_only_primitive_types(),
Literal::Struct { .. } => false,
Literal::Cast { ref value, ref ty } => {
value.uses_only_primitive_types() && ty.is_primitive_or_ptr_primitive()
}
}
}
}
impl Literal {
pub fn rename_for_config(&mut self, config: &Config) {
match self {
Literal::Struct {
ref mut export_name,
fields,
..
} => {
config.export.rename(export_name);
for lit in fields.values_mut() {
lit.rename_for_config(config);
}
}
Literal::Path(ref mut name) => {
config.export.rename(name);
}
Literal::PostfixUnaryOp { ref mut value, .. } => {
value.rename_for_config(config);
}
Literal::BinOp {
ref mut left,
ref mut right,
..
} => {
left.rename_for_config(config);
right.rename_for_config(config);
}
Literal::Expr(_) => {}
Literal::Cast {
ref mut ty,
ref mut value,
} => {
ty.rename_for_config(config, &GenericParams::default());
value.rename_for_config(config);
}
}
}
// Translate from full blown `syn::Expr` into a simpler `Literal` type
pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
match *expr {
// Match binary expressions of the form `a * b`
syn::Expr::Binary(ref bin_expr) => {
let l = Self::load(&bin_expr.left)?;
let r = Self::load(&bin_expr.right)?;
let op = match bin_expr.op {
syn::BinOp::Add(..) => "+",
syn::BinOp::Sub(..) => "-",
syn::BinOp::Mul(..) => "*",
syn::BinOp::Div(..) => "/",
syn::BinOp::Rem(..) => "%",
syn::BinOp::And(..) => "&&",
syn::BinOp::Or(..) => "||",
syn::BinOp::BitXor(..) => "^",
syn::BinOp::BitAnd(..) => "&",
syn::BinOp::BitOr(..) => "|",
syn::BinOp::Shl(..) => "<<",
syn::BinOp::Shr(..) => ">>",
syn::BinOp::Eq(..) => "==",
syn::BinOp::Lt(..) => "<",
syn::BinOp::Le(..) => "<=",
syn::BinOp::Ne(..) => "!=",
syn::BinOp::Ge(..) => ">=",
syn::BinOp::Gt(..) => ">",
syn::BinOp::AddEq(..) => "+=",
syn::BinOp::SubEq(..) => "-=",
syn::BinOp::MulEq(..) => "*=",
syn::BinOp::DivEq(..) => "/=",
syn::BinOp::RemEq(..) => "%=",
syn::BinOp::BitXorEq(..) => "^=",
syn::BinOp::BitAndEq(..) => "&=",
syn::BinOp::BitOrEq(..) => "|=",
syn::BinOp::ShlEq(..) => ">>=",
syn::BinOp::ShrEq(..) => "<<=",
};
Ok(Literal::BinOp {
left: Box::new(l),
op,
right: Box::new(r),
})
}
// Match literals like true, 'a', 32 etc
syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
match lit {
syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
0..=255 => format!("'{}'", value.value().escape_default()),
other_code => format!(r"U'\U{:08X}'", other_code),
})),
syn::Lit::Int(ref value) => {
if value.base10_parse::<i64>().is_err() {
Ok(Literal::Expr(format!("{}ULL", value.base10_digits())))
} else {
Ok(Literal::Expr(value.base10_digits().to_string()))
}
}
syn::Lit::Float(ref value) => {
Ok(Literal::Expr(value.base10_digits().to_string()))
}
syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
// TODO: Add support for byte string and Verbatim
_ => Err(format!("Unsupported literal expression. {:?}", *lit)),
}
}
syn::Expr::Struct(syn::ExprStruct {
ref path,
ref fields,
..
}) => {
let struct_name = path.segments[0].ident.to_string();
let mut field_map = HashMap::<String, Literal>::default();
for field in fields {
let ident = match field.member {
syn::Member::Named(ref name) => name.to_string(),
syn::Member::Unnamed(ref index) => format!("_{}", index.index),
};
let key = ident.to_string();
let value = Literal::load(&field.expr)?;
field_map.insert(key, value);
}
Ok(Literal::Struct {
path: Path::new(struct_name.clone()),
export_name: struct_name,
fields: field_map,
})
}
syn::Expr::Unary(syn::ExprUnary {
ref op, ref expr, ..
}) => match *op {
UnOp::Neg(_) => {
let val = Self::load(expr)?;
Ok(Literal::PostfixUnaryOp {
op: "-",
value: Box::new(val),
})
}
_ => Err(format!("Unsupported Unary expression. {:?}", *op)),
},
// Match identifiers, like `5 << SHIFT`
syn::Expr::Path(syn::ExprPath {
path: syn::Path { ref segments, .. },
..
}) => {
// Handle only the simplest identifiers and error for anything else.
if segments.len() == 1 {
Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
} else {
Err(format!("Unsupported path expression. {:?}", *segments))
}
}
syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
syn::Expr::Cast(syn::ExprCast {
ref expr, ref ty, ..
}) => {
let val = Self::load(expr)?;
match Type::load(ty)? {
Some(ty) => Ok(Literal::Cast {
ty,
value: Box::new(val),
}),
None => Err("Cannot cast to zero sized type.".to_owned()),
}
}
_ => Err(format!("Unsupported expression. {:?}", *expr)),
}
}
pub(crate) fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
match self {
Literal::Expr(v) => match (&**v, config.language) {
("true", Language::Cython) => write!(out, "True"),
("false", Language::Cython) => write!(out, "False"),
(v, _) => write!(out, "{}", v),
},
Literal::Path(v) => write!(out, "{}", v),
Literal::PostfixUnaryOp { op, ref value } => {
write!(out, "{}", op);
value.write(config, out);
}
Literal::BinOp {
ref left,
op,
ref right,
} => {
write!(out, "(");
left.write(config, out);
write!(out, " {} ", op);
right.write(config, out);
write!(out, ")");
}
Literal::Cast { ref ty, ref value } => {
out.write(if config.language == Language::Cython {
"<"
} else {
"("
});
ty.write(config, out);
out.write(if config.language == Language::Cython {
">"
} else {
")"
});
value.write(config, out);
}
Literal::Struct {
export_name,
fields,
path,
} => {
match config.language {
Language::C => write!(out, "({})", export_name),
Language::Cxx => write!(out, "{}", export_name),
Language::Cython => write!(out, "<{}>", export_name),
}
write!(out, "{{ ");
let mut is_first_field = true;
// In C++, same order as defined is required.
let ordered_fields = out.bindings().struct_field_names(path);
for ordered_key in ordered_fields.iter() {
if let Some(lit) = fields.get(ordered_key) {
if !is_first_field {
write!(out, ", ");
} else {
is_first_field = false;
}
match config.language {
Language::Cxx => write!(out, "/* .{} = */ ", ordered_key),
Language::C => write!(out, ".{} = ", ordered_key),
Language::Cython => {}
}
lit.write(config, out);
}
}
write!(out, " }}");
}
}
}
}
#[derive(Debug, Clone)]
pub struct Constant {
pub path: Path,
pub export_name: String,
pub ty: Type,
pub value: Literal,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub associated_to: Option<Path>,
}
impl Constant {
pub fn load(
path: Path,
mod_cfg: Option<&Cfg>,
ty: &syn::Type,
expr: &syn::Expr,
attrs: &[syn::Attribute],
associated_to: Option<Path>,
) -> Result<Constant, String> {
let ty = Type::load(ty)?;
let mut ty = match ty {
Some(ty) => ty,
None => {
return Err("Cannot have a zero sized const definition.".to_owned());
}
};
let mut lit = Literal::load(expr)?;
if let Some(ref associated_to) = associated_to {
ty.replace_self_with(associated_to);
lit.replace_self_with(associated_to);
}
Ok(Constant::new(
path,
ty,
lit,
Cfg::append(mod_cfg, Cfg::load(attrs)),
AnnotationSet::load(attrs)?,
Documentation::load(attrs),
associated_to,
))
}
pub fn new(
path: Path,
ty: Type,
value: Literal,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
associated_to: Option<Path>,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
ty,
value,
cfg,
annotations,
documentation,
associated_to,
}
}
pub fn uses_only_primitive_types(&self) -> bool {
self.value.uses_only_primitive_types() && self.ty.is_primitive_or_ptr_primitive()
}
}
impl Item for Constant {
fn path(&self) -> &Path {
&self.path
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Constant(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
if self.associated_to.is_none() {
config.export.rename(&mut self.export_name);
}
self.value.rename_for_config(config);
self.ty.rename_for_config(config, &GenericParams::default()); // FIXME: should probably propagate something here
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
}
impl Constant {
pub fn write_declaration<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
associated_to_struct: &Struct,
) {
debug_assert!(self.associated_to.is_some());
debug_assert!(config.language == Language::Cxx);
debug_assert!(!associated_to_struct.is_transparent);
debug_assert!(config.structure.associated_constants_in_body);
debug_assert!(config.constant.allow_static_const);
if let Type::Ptr { is_const: true, .. } = self.ty {
out.write("static ");
} else {
out.write("static const ");
}
self.ty.write(config, out);
write!(out, " {};", self.export_name())
}
pub fn write<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
associated_to_struct: Option<&Struct>,
) {
if let Some(assoc) = associated_to_struct {
if assoc.is_generic() {
return; // Not tested / implemented yet, so bail out.
}
}
if !self.value.is_valid(out.bindings()) {
return;
}
let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
let in_body = associated_to_struct.is_some()
&& config.language == Language::Cxx
&& config.structure.associated_constants_in_body
&& config.constant.allow_static_const
&& !associated_to_transparent;
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
let name = if in_body {
Cow::Owned(format!(
"{}::{}",
associated_to_struct.unwrap().export_name(),
self.export_name(),
))
} else if self.associated_to.is_none() {
Cow::Borrowed(self.export_name())
} else {
let associated_name = match associated_to_struct {
Some(s) => Cow::Borrowed(s.export_name()),
None => {
let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
config.export.rename(&mut name);
Cow::Owned(name)
}
};
Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
};
let value = match self.value {
Literal::Struct {
ref fields,
ref path,
..
} if out.bindings().struct_is_transparent(path) => fields.iter().next().unwrap().1,
_ => &self.value,
};
self.documentation.write(config, out);
let allow_constexpr = if let Type::Primitive(..) = self.ty {
config.constant.allow_constexpr
} else {
false
};
match config.language {
Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
if allow_constexpr {
out.write("constexpr ")
}
if config.constant.allow_static_const {
out.write(if in_body { "inline " } else { "static " });
}
if let Type::Ptr { is_const: true, .. } = self.ty {
// Nothing.
} else {
out.write("const ");
}
self.ty.write(config, out);
write!(out, " {} = ", name);
value.write(config, out);
write!(out, ";");
}
Language::Cxx | Language::C => {
write!(out, "#define {} ", name);
value.write(config, out);
}
Language::Cython => {
out.write("const ");
self.ty.write(config, out);
// For extern Cython declarations the initializer is ignored,
// but still useful as documentation, so we write it as a comment.
write!(out, " {} # = ", name);
value.write(config, out);
}
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,106 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, DocumentationStyle, Language};
use crate::bindgen::utilities::SynAttributeHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Documentation {
pub doc_comment: Vec<String>,
}
impl Documentation {
pub fn load(attrs: &[syn::Attribute]) -> Self {
let doc = attrs
.get_comment_lines()
.into_iter()
.filter(|x| !x.trim_start().starts_with("cbindgen:"))
.collect();
Documentation { doc_comment: doc }
}
pub fn simple(line: &str) -> Self {
Documentation {
doc_comment: vec![line.to_owned()],
}
}
pub fn none() -> Self {
Documentation {
doc_comment: Vec::new(),
}
}
}
impl Source for Documentation {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.doc_comment.is_empty() || !config.documentation {
return;
}
// Cython uses Python-style comments, so `documentation_style` is not relevant.
if config.language == Language::Cython {
for line in &self.doc_comment {
write!(out, "#{}", line);
out.new_line();
}
return;
}
let style = match config.documentation_style {
DocumentationStyle::Auto if config.language == Language::C => DocumentationStyle::Doxy,
DocumentationStyle::Auto if config.language == Language::Cxx => DocumentationStyle::Cxx,
DocumentationStyle::Auto => DocumentationStyle::C, // Fallback if `Language` gets extended.
other => other,
};
// Following these documents for style conventions:
// https://en.wikibooks.org/wiki/C++_Programming/Code/Style_Conventions/Comments
// https://www.cs.cmu.edu/~410/doc/doxygen.html
match style {
DocumentationStyle::C => {
out.write("/*");
out.new_line();
}
DocumentationStyle::Doxy => {
out.write("/**");
out.new_line();
}
_ => (),
}
for line in &self.doc_comment {
match style {
DocumentationStyle::C => out.write(""),
DocumentationStyle::Doxy => out.write(" *"),
DocumentationStyle::C99 => out.write("//"),
DocumentationStyle::Cxx => out.write("///"),
DocumentationStyle::Auto => unreachable!(), // Auto case should always be covered
}
write!(out, "{}", line);
out.new_line();
}
match style {
DocumentationStyle::C => {
out.write(" */");
out.new_line();
}
DocumentationStyle::Doxy => {
out.write(" */");
out.new_line();
}
_ => (),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::ir::{AnnotationSet, Cfg, ConditionWrite};
use crate::bindgen::ir::{Documentation, Path, ToCondition, Type};
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Field {
pub name: String,
pub ty: Type,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Field {
pub fn from_name_and_type(name: String, ty: Type) -> Field {
Field {
name,
ty,
cfg: None,
annotations: AnnotationSet::new(),
documentation: Documentation::none(),
}
}
pub fn load(field: &syn::Field, self_path: &Path) -> Result<Option<Field>, String> {
Ok(if let Some(mut ty) = Type::load(&field.ty)? {
ty.replace_self_with(self_path);
Some(Field {
name: field
.ident
.as_ref()
.ok_or_else(|| "field is missing identifier".to_string())?
.to_string(),
ty,
cfg: Cfg::load(&field.attrs),
annotations: AnnotationSet::load(&field.attrs)?,
documentation: Documentation::load(&field.attrs),
})
} else {
None
})
}
}
impl Source for Field {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
// Cython doesn't support conditional fields.
let condition = self.cfg.to_condition(config);
if config.language != Language::Cython {
condition.write_before(config, out);
}
self.documentation.write(config, out);
cdecl::write_field(out, &self.ty, &self.name, config);
// Cython extern declarations don't manage layouts, layouts are defined entierly by the
// corresponding C code. So we can omit bitfield sizes which are not supported by Cython.
if config.language != Language::Cython {
if let Some(bitfield) = self.annotations.atom("bitfield") {
write!(out, ": {}", bitfield.unwrap_or_default());
}
}
if config.language != Language::Cython {
condition.write_after(config, out);
// FIXME(#634): `write_vertical_source_list` should support
// configuring list elements natively. For now we print a newline
// here to avoid printing `#endif;` with semicolon.
if condition.is_some() {
out.new_line();
}
}
}
}

View File

@@ -0,0 +1,400 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language, Layout};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericPath, Path, PrimitiveType,
ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct FunctionArgument {
pub name: Option<String>,
pub ty: Type,
pub array_length: Option<String>,
}
#[derive(Debug, Clone)]
pub struct Function {
pub path: Path,
/// Path to the self-type of the function
/// If the function is a method, this will contain the path of the type in the impl block
pub self_type_path: Option<Path>,
pub ret: Type,
pub args: Vec<FunctionArgument>,
pub extern_decl: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub never_return: bool,
}
impl Function {
pub fn load(
path: Path,
self_type_path: Option<&Path>,
sig: &syn::Signature,
extern_decl: bool,
attrs: &[syn::Attribute],
mod_cfg: Option<&Cfg>,
) -> Result<Function, String> {
let mut args = sig.inputs.iter().try_skip_map(|x| x.as_argument())?;
let mut never_return = false;
let mut ret = match sig.output {
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
syn::ReturnType::Type(_, ref ty) => {
if let syn::Type::Never(_) = ty.as_ref() {
never_return = true;
Type::Primitive(PrimitiveType::Void)
} else {
Type::load(ty)?.unwrap_or(Type::Primitive(PrimitiveType::Void))
}
}
};
if let Some(self_path) = self_type_path {
for arg in &mut args {
arg.ty.replace_self_with(self_path);
}
ret.replace_self_with(self_path);
}
Ok(Function {
path,
self_type_path: self_type_path.cloned(),
ret,
args,
extern_decl,
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
documentation: Documentation::load(attrs),
never_return,
})
}
pub(crate) fn never_return(&self, config: &Config) -> bool {
self.never_return && config.language != Language::Cython
}
pub fn swift_name(&self, config: &Config) -> Option<String> {
if config.language == Language::Cython {
return None;
}
// If the symbol name starts with the type name, separate the two components with '.'
// so that Swift recognises the association between the method and the type
let (ref type_prefix, ref type_name) = match self.self_type_path {
Some(ref type_name) => {
let type_name = type_name.to_string();
if !self.path.name().starts_with(&type_name) {
return Some(self.path.to_string());
}
(format!("{}.", type_name), type_name)
}
None => ("".to_string(), "".to_string()),
};
let item_name = self
.path
.name()
.trim_start_matches(type_name)
.trim_start_matches('_');
let item_args = {
let mut items = Vec::with_capacity(self.args.len());
for arg in self.args.iter() {
items.push(format!("{}:", arg.name.as_ref()?.as_str()));
}
items.join("")
};
Some(format!("{}{}({})", type_prefix, item_name, item_args))
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.ret.simplify_standard_types(config);
for arg in &mut self.args {
arg.ty.simplify_standard_types(config);
}
}
pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ret.add_dependencies(library, out);
for arg in &self.args {
arg.ty.add_dependencies(library, out);
}
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
self.ret.add_monomorphs(library, out);
for arg in &self.args {
arg.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
self.ret.mangle_paths(monomorphs);
for arg in &mut self.args {
arg.ty.mangle_paths(monomorphs);
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ret.resolve_declaration_types(resolver);
for arg in &mut self.args {
arg.ty.resolve_declaration_types(resolver);
}
}
pub fn rename_for_config(&mut self, config: &Config) {
// Rename the types used in arguments
let generic_params = Default::default();
self.ret.rename_for_config(config, &generic_params);
// Apply rename rules to argument names
let rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.function.rename_args);
if let Some(r) = rules.not_none() {
let args = std::mem::take(&mut self.args);
self.args = args
.into_iter()
.map(|arg| {
let name = arg
.name
.map(|n| r.apply(&n, IdentifierType::FunctionArg).into_owned());
FunctionArgument {
name,
ty: arg.ty,
array_length: None,
}
})
.collect()
}
// Escape C/C++ reserved keywords used in argument names, and
// recursively rename argument types.
for arg in &mut self.args {
arg.ty.rename_for_config(config, &generic_params);
if let Some(ref mut name) = arg.name {
reserved::escape(name);
}
}
// Save the array length of the pointer arguments which need to use
// the C-array notation
if let Some(tuples) = self.annotations.list("ptrs-as-arrays") {
let mut ptrs_as_arrays: HashMap<String, String> = HashMap::new();
for str_tuple in tuples {
let parts: Vec<&str> = str_tuple[1..str_tuple.len() - 1]
.split(';')
.map(|x| x.trim())
.collect();
if parts.len() != 2 {
warn!(
"{:?} does not follow the correct syntax, so the annotation is being ignored",
parts
);
continue;
}
ptrs_as_arrays.insert(parts[0].to_string(), parts[1].to_string());
}
for arg in &mut self.args {
match arg.ty {
Type::Ptr { .. } => {}
_ => continue,
}
let name = match arg.name {
Some(ref name) => name,
None => continue,
};
arg.array_length = ptrs_as_arrays.get(name).cloned();
}
}
}
}
impl Source for Function {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
fn write_1<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
let prefix = config.function.prefix(&func.annotations);
let postfix = config.function.postfix(&func.annotations);
let condition = func.cfg.to_condition(config);
condition.write_before(config, out);
func.documentation.write(config, out);
if func.extern_decl {
out.write("extern ");
} else {
if let Some(ref prefix) = prefix {
write!(out, "{} ", prefix);
}
if func.annotations.must_use(config) {
if let Some(ref anno) = config.function.must_use {
write!(out, "{} ", anno);
}
}
}
cdecl::write_func(out, func, false, config);
if !func.extern_decl {
if let Some(ref postfix) = postfix {
write!(out, " {}", postfix);
}
}
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
if let Some(swift_name) = func.swift_name(config) {
write!(out, " {}({})", swift_name_macro, swift_name);
}
}
if func.never_return(config) {
if let Some(ref no_return_attr) = config.function.no_return {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
out.write(";");
condition.write_after(config, out);
}
fn write_2<W: Write>(func: &Function, config: &Config, out: &mut SourceWriter<W>) {
let prefix = config.function.prefix(&func.annotations);
let postfix = config.function.postfix(&func.annotations);
let condition = func.cfg.to_condition(config);
condition.write_before(config, out);
func.documentation.write(config, out);
if func.extern_decl {
out.write("extern ");
} else {
if let Some(ref prefix) = prefix {
write!(out, "{}", prefix);
out.new_line();
}
if func.annotations.must_use(config) {
if let Some(ref anno) = config.function.must_use {
write!(out, "{}", anno);
out.new_line();
}
}
}
cdecl::write_func(out, func, true, config);
if !func.extern_decl {
if let Some(ref postfix) = postfix {
out.new_line();
write!(out, "{}", postfix);
}
}
if let Some(ref swift_name_macro) = config.function.swift_name_macro {
if let Some(swift_name) = func.swift_name(config) {
write!(out, " {}({})", swift_name_macro, swift_name);
}
}
if func.never_return(config) {
if let Some(ref no_return_attr) = config.function.no_return {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
out.write(";");
condition.write_after(config, out);
}
let option_1 = out.measure(|out| write_1(self, config, out));
if (config.function.args == Layout::Auto && option_1 <= config.line_length)
|| config.function.args == Layout::Horizontal
{
write_1(self, config, out);
} else {
write_2(self, config, out);
}
}
}
trait SynFnArgHelpers {
fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
}
fn gen_self_type(receiver: &syn::Receiver) -> Type {
let self_ty = Type::Path(GenericPath::self_path());
if receiver.reference.is_none() {
return self_ty;
}
let is_const = receiver.mutability.is_none();
Type::Ptr {
ty: Box::new(self_ty),
is_const,
is_nullable: false,
is_ref: false,
}
}
impl SynFnArgHelpers for syn::FnArg {
fn as_argument(&self) -> Result<Option<FunctionArgument>, String> {
match *self {
syn::FnArg::Typed(syn::PatType {
ref pat, ref ty, ..
}) => {
let name = match **pat {
syn::Pat::Wild(..) => None,
syn::Pat::Ident(syn::PatIdent { ref ident, .. }) => Some(ident.to_string()),
_ => {
return Err(format!(
"Parameter has an unsupported argument name: {:?}",
pat
))
}
};
let ty = match Type::load(ty)? {
Some(x) => x,
None => return Ok(None),
};
if let Type::Array(..) = ty {
return Err("Array as function arguments are not supported".to_owned());
}
Ok(Some(FunctionArgument {
name,
ty,
array_length: None,
}))
}
syn::FnArg::Receiver(ref receiver) => Ok(Some(FunctionArgument {
name: Some("self".to_string()),
ty: gen_self_type(receiver),
array_length: None,
})),
}
}
}

View File

@@ -0,0 +1,169 @@
use std::io::Write;
use std::ops::Deref;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver};
use crate::bindgen::ir::{Path, Type};
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Default, Debug, Clone)]
pub struct GenericParams(pub Vec<Path>);
impl GenericParams {
pub fn new(generics: &syn::Generics) -> Self {
GenericParams(
generics
.params
.iter()
.filter_map(|x| match *x {
syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) => {
Some(Path::new(ident.to_string()))
}
_ => None,
})
.collect(),
)
}
fn write_internal<F: Write>(
&self,
config: &Config,
out: &mut SourceWriter<F>,
with_default: bool,
) {
if !self.0.is_empty() && config.language == Language::Cxx {
out.write("template<");
for (i, item) in self.0.iter().enumerate() {
if i != 0 {
out.write(", ");
}
write!(out, "typename {}", item);
if with_default {
write!(out, " = void");
}
}
out.write(">");
out.new_line();
}
}
pub fn write_with_default<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
self.write_internal(config, out, true);
}
}
impl Deref for GenericParams {
type Target = [Path];
fn deref(&self) -> &[Path] {
&self.0
}
}
impl Source for GenericParams {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
self.write_internal(config, out, false);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GenericPath {
path: Path,
export_name: String,
generics: Vec<Type>,
ctype: Option<DeclarationType>,
}
impl GenericPath {
pub fn new(path: Path, generics: Vec<Type>) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generics,
ctype: None,
}
}
pub fn self_path() -> Self {
Self::new(Path::new("Self"), vec![])
}
pub fn replace_self_with(&mut self, self_ty: &Path) {
if self.path.replace_self_with(self_ty) {
self.export_name = self_ty.name().to_owned();
}
// Caller deals with generics.
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn generics(&self) -> &[Type] {
&self.generics
}
pub fn generics_mut(&mut self) -> &mut [Type] {
&mut self.generics
}
pub fn ctype(&self) -> Option<&DeclarationType> {
self.ctype.as_ref()
}
pub fn name(&self) -> &str {
self.path.name()
}
pub fn export_name(&self) -> &str {
&self.export_name
}
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
for generic in &mut self.generics {
generic.rename_for_config(config, generic_params);
}
if !generic_params.contains(&self.path) {
config.export.rename(&mut self.export_name);
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ctype = resolver.type_for(&self.path);
}
pub fn load(path: &syn::Path) -> Result<Self, String> {
assert!(
!path.segments.is_empty(),
"{:?} doesn't have any segments",
path
);
let last_segment = path.segments.last().unwrap();
let name = last_segment.ident.to_string();
let path = Path::new(name);
let phantom_data_path = Path::new("PhantomData");
if path == phantom_data_path {
return Ok(Self::new(path, Vec::new()));
}
let generics = match last_segment.arguments {
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
ref args,
..
}) => args.iter().try_skip_map(|x| match *x {
syn::GenericArgument::Type(ref x) => Type::load(x),
syn::GenericArgument::Lifetime(_) => Ok(None),
_ => Err(format!("can't handle generic argument {:?}", x)),
})?,
syn::PathArguments::Parenthesized(_) => {
return Err("Path contains parentheses.".to_owned());
}
_ => Vec::new(),
};
Ok(Self::new(path, generics))
}
}

View File

@@ -0,0 +1,117 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::Config;
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, Item, ItemContainer, Path, Type};
use crate::bindgen::library::Library;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Static {
pub path: Path,
pub export_name: String,
pub ty: Type,
pub mutable: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Static {
pub fn load(item: &syn::ItemStatic, mod_cfg: Option<&Cfg>) -> Result<Static, String> {
let ty = Type::load(&item.ty)?;
if ty.is_none() {
return Err("Cannot have a zero sized static definition.".to_owned());
}
Ok(Static::new(
Path::new(item.ident.to_string()),
ty.unwrap(),
item.mutability.is_some(),
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
pub fn new(
path: Path,
ty: Type,
mutable: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
ty,
mutable,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.ty.simplify_standard_types(config);
}
}
impl Item for Static {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Static(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
self.ty.rename_for_config(config, &Default::default());
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.ty.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.ty.add_dependencies(library, out);
}
}
impl Source for Static {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
out.write("extern ");
if let Type::Ptr { is_const: true, .. } = self.ty {
} else if !self.mutable {
out.write("const ");
}
cdecl::write_field(out, &self.ty, &self.export_name, config);
out.write(";");
}
}

View File

@@ -0,0 +1,244 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use indexmap::IndexMap;
use std::mem;
use crate::bindgen::config::Config;
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
/// An item is any type of rust item besides a function
pub trait Item {
fn path(&self) -> &Path;
fn name(&self) -> &str {
self.path().name()
}
fn export_name(&self) -> &str {
self.name()
}
fn cfg(&self) -> Option<&Cfg>;
fn annotations(&self) -> &AnnotationSet;
fn annotations_mut(&mut self) -> &mut AnnotationSet;
fn container(&self) -> ItemContainer;
fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) {
unimplemented!()
}
fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) {
unimplemented!()
}
fn rename_for_config(&mut self, _config: &Config) {}
fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
fn instantiate_monomorph(&self, _generics: &[Type], _library: &Library, _out: &mut Monomorphs) {
unreachable!("Cannot instantiate {} as a generic.", self.name())
}
}
#[derive(Debug, Clone)]
pub enum ItemContainer {
Constant(Constant),
Static(Static),
OpaqueItem(OpaqueItem),
Struct(Struct),
Union(Union),
Enum(Enum),
Typedef(Typedef),
}
impl ItemContainer {
pub fn deref(&self) -> &dyn Item {
match *self {
ItemContainer::Constant(ref x) => x,
ItemContainer::Static(ref x) => x,
ItemContainer::OpaqueItem(ref x) => x,
ItemContainer::Struct(ref x) => x,
ItemContainer::Union(ref x) => x,
ItemContainer::Enum(ref x) => x,
ItemContainer::Typedef(ref x) => x,
}
}
}
#[derive(Debug, Clone)]
pub enum ItemValue<T: Item> {
Cfg(Vec<T>),
Single(T),
}
#[derive(Debug, Clone)]
pub struct ItemMap<T: Item> {
data: IndexMap<Path, ItemValue<T>>,
}
impl<T: Item> Default for ItemMap<T> {
fn default() -> ItemMap<T> {
ItemMap {
data: Default::default(),
}
}
}
impl<T: Item + Clone> ItemMap<T> {
pub fn rebuild(&mut self) {
let old = mem::take(self);
old.for_all_items(|x| {
self.try_insert(x.clone());
});
}
pub fn try_insert(&mut self, item: T) -> bool {
match (item.cfg().is_some(), self.data.get_mut(item.path())) {
(true, Some(&mut ItemValue::Cfg(ref mut items))) => {
items.push(item);
return true;
}
(false, Some(&mut ItemValue::Cfg(_))) => {
return false;
}
(true, Some(&mut ItemValue::Single(_))) => {
return false;
}
(false, Some(&mut ItemValue::Single(_))) => {
return false;
}
_ => {}
}
let path = item.path().clone();
if item.cfg().is_some() {
self.data.insert(path, ItemValue::Cfg(vec![item]));
} else {
self.data.insert(path, ItemValue::Single(item));
}
true
}
pub fn extend_with(&mut self, other: &ItemMap<T>) {
other.for_all_items(|x| {
self.try_insert(x.clone());
});
}
pub fn to_vec(&self) -> Vec<T> {
let mut result = Vec::with_capacity(self.data.len());
for container in self.data.values() {
match *container {
ItemValue::Cfg(ref items) => result.extend_from_slice(items),
ItemValue::Single(ref item) => {
result.push(item.clone());
}
}
}
result
}
pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
Some(match *self.data.get(path)? {
ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(),
ItemValue::Single(ref item) => vec![item.container()],
})
}
pub fn filter<F>(&mut self, callback: F)
where
F: Fn(&T) -> bool,
{
let data = mem::take(&mut self.data);
for (name, container) in data {
match container {
ItemValue::Cfg(items) => {
let mut new_items = Vec::new();
for item in items {
if !callback(&item) {
new_items.push(item);
}
}
if !new_items.is_empty() {
self.data.insert(name, ItemValue::Cfg(new_items));
}
}
ItemValue::Single(item) => {
if !callback(&item) {
self.data.insert(name, ItemValue::Single(item));
}
}
}
}
}
pub fn for_all_items<F>(&self, mut callback: F)
where
F: FnMut(&T),
{
for container in self.data.values() {
match *container {
ItemValue::Cfg(ref items) => {
for item in items {
callback(item);
}
}
ItemValue::Single(ref item) => callback(item),
}
}
}
pub fn for_all_items_mut<F>(&mut self, mut callback: F)
where
F: FnMut(&mut T),
{
for container in self.data.values_mut() {
match *container {
ItemValue::Cfg(ref mut items) => {
for item in items {
callback(item);
}
}
ItemValue::Single(ref mut item) => callback(item),
}
}
}
pub fn for_items<F>(&self, path: &Path, mut callback: F)
where
F: FnMut(&T),
{
match self.data.get(path) {
Some(&ItemValue::Cfg(ref items)) => {
for item in items {
callback(item);
}
}
Some(&ItemValue::Single(ref item)) => {
callback(item);
}
None => {}
}
}
pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
where
F: FnMut(&mut T),
{
match self.data.get_mut(path) {
Some(&mut ItemValue::Cfg(ref mut items)) => {
for item in items {
callback(item);
}
}
Some(&mut ItemValue::Single(ref mut item)) => {
callback(item);
}
None => {}
}
}
}

View File

@@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub mod annotation;
pub mod cfg;
pub mod constant;
pub mod documentation;
pub mod enumeration;
pub mod field;
pub mod function;
pub mod generic_path;
pub mod global;
pub mod item;
pub mod opaque;
pub mod path;
pub mod repr;
pub mod structure;
pub mod ty;
pub mod typedef;
pub mod union;
pub use self::annotation::{AnnotationSet, AnnotationValue};
pub use self::cfg::*;
pub use self::constant::*;
pub use self::documentation::Documentation;
pub use self::enumeration::*;
pub use self::field::*;
pub use self::function::*;
pub use self::generic_path::*;
pub use self::global::*;
pub use self::item::*;
pub use self::opaque::*;
pub use self::path::*;
pub use self::repr::*;
pub use self::structure::*;
pub use self::ty::*;
pub use self::typedef::*;
pub use self::union::*;

View File

@@ -0,0 +1,176 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct OpaqueItem {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl OpaqueItem {
pub fn load(
path: Path,
generics: &syn::Generics,
attrs: &[syn::Attribute],
mod_cfg: Option<&Cfg>,
) -> Result<OpaqueItem, String> {
Ok(Self::new(
path,
GenericParams::new(generics),
Cfg::append(mod_cfg, Cfg::load(attrs)),
AnnotationSet::load(attrs).unwrap_or_else(|_| AnnotationSet::new()),
Documentation::load(attrs),
))
}
pub fn new(
path: Path,
generic_params: GenericParams,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> OpaqueItem {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
cfg,
annotations,
documentation,
}
}
}
impl Item for OpaqueItem {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::OpaqueItem(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_struct(&self.path);
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
}
fn add_dependencies(&self, _: &Library, _: &mut Dependencies) {}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
!self.generic_params.is_empty(),
"{} is not generic",
self.path
);
// We can be instantiated with less generic params because of default
// template parameters, or because of empty types that we remove during
// parsing (`()`).
assert!(
self.generic_params.len() >= generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = OpaqueItem::new(
mangled_path,
GenericParams::default(),
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_opaque(self, monomorph, generic_values.to_owned());
}
}
impl Source for OpaqueItem {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write_with_default(config, out);
match config.language {
Language::C if config.style.generate_typedef() => {
write!(
out,
"typedef struct {} {};",
self.export_name(),
self.export_name()
);
}
Language::C | Language::Cxx => {
write!(out, "struct {};", self.export_name());
}
Language::Cython => {
write!(
out,
"{}struct {}",
config.style.cython_def(),
self.export_name()
);
out.open_brace();
out.write("pass");
out.close_brace(false);
}
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp::Ordering;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path {
name: String,
}
impl Path {
pub fn new<T>(name: T) -> Self
where
String: From<T>,
{
Self { name: name.into() }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn replace_self_with(&mut self, path: &Self) -> bool {
if self.name() != "Self" {
return false;
}
*self = path.clone();
true
}
}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Path {
fn cmp(&self, other: &Path) -> Ordering {
self.name.cmp(&other.name)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}

View File

@@ -0,0 +1,184 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::ir::ty::{IntKind, PrimitiveType};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ReprStyle {
Rust,
C,
Transparent,
}
impl Default for ReprStyle {
fn default() -> Self {
ReprStyle::Rust
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ReprType {
kind: IntKind,
signed: bool,
}
impl ReprType {
pub(crate) fn to_primitive(self) -> PrimitiveType {
PrimitiveType::Integer {
kind: self.kind,
signed: self.signed,
zeroable: true,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ReprAlign {
Packed,
Align(u64),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct Repr {
pub style: ReprStyle,
pub ty: Option<ReprType>,
pub align: Option<ReprAlign>,
}
impl Repr {
pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> {
let ids = attrs
.iter()
.filter_map(|attr| {
if let syn::Meta::List(syn::MetaList { path, nested, .. }) =
attr.parse_meta().ok()?
{
if path.is_ident("repr") {
return Some(nested.into_iter().collect::<Vec<_>>());
}
}
None
})
.flatten()
.filter_map(|meta| match meta {
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
Some((path.segments.first().unwrap().ident.to_string(), None))
}
syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
Some((
path.segments.first().unwrap().ident.to_string(),
Some(
nested
.iter()
.filter_map(|meta| match meta {
// Only used for #[repr(align(...))].
syn::NestedMeta::Lit(syn::Lit::Int(literal)) => {
Some(literal.base10_digits().to_string())
}
// Only single levels of nesting supported at the moment.
_ => None,
})
.collect::<Vec<_>>(),
),
))
}
_ => None,
});
let mut repr = Repr::default();
for id in ids {
let (int_kind, signed) = match (id.0.as_ref(), id.1) {
("u8", None) => (IntKind::B8, false),
("u16", None) => (IntKind::B16, false),
("u32", None) => (IntKind::B32, false),
("u64", None) => (IntKind::B64, false),
("usize", None) => (IntKind::Size, false),
("i8", None) => (IntKind::B8, true),
("i16", None) => (IntKind::B16, true),
("i32", None) => (IntKind::B32, true),
("i64", None) => (IntKind::B64, true),
("isize", None) => (IntKind::Size, true),
("C", None) => {
repr.style = ReprStyle::C;
continue;
}
("transparent", None) => {
repr.style = ReprStyle::Transparent;
continue;
}
("packed", args) => {
// #[repr(packed(n))] not supported because of some open questions about how
// to calculate the native alignment of types. See eqrion/cbindgen#433.
if args.is_some() {
return Err(
"Not-yet-implemented #[repr(packed(...))] encountered.".to_string()
);
}
let align = ReprAlign::Packed;
// Only permit a single alignment-setting repr.
if let Some(old_align) = repr.align {
return Err(format!(
"Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
old_align, align
));
}
repr.align = Some(align);
continue;
}
("align", Some(args)) => {
// #[repr(align(...))] only allows a single argument.
if args.len() != 1 {
return Err(format!(
"Unsupported #[repr(align({}))], align must have exactly one argument.",
args.join(", ")
));
}
// Must be a positive integer.
let align = match args.first().unwrap().parse::<u64>() {
Ok(align) => align,
Err(_) => {
return Err(format!("Non-numeric #[repr(align({}))].", args.join(", ")))
}
};
// Must be a power of 2.
if !align.is_power_of_two() || align == 0 {
return Err(format!("Invalid alignment to #[repr(align({}))].", align));
}
// Only permit a single alignment-setting repr.
if let Some(old_align) = repr.align {
return Err(format!(
"Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
old_align,
ReprAlign::Align(align)
));
}
repr.align = Some(ReprAlign::Align(align));
continue;
}
(path, args) => match args {
None => return Err(format!("Unsupported #[repr({})].", path)),
Some(args) => {
return Err(format!(
"Unsupported #[repr({}({}))].",
path,
args.join(", ")
));
}
},
};
let ty = ReprType {
kind: int_kind,
signed,
};
if let Some(old_ty) = repr.ty {
return Err(format!(
"Conflicting #[repr(...)] type hints {:?} and {:?}.",
old_ty, ty
));
}
repr.ty = Some(ty);
}
Ok(repr)
}
}

View File

@@ -0,0 +1,710 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language, LayoutConfig};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Constant, Documentation, Field, GenericParams, Item,
ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition, Type, Typedef,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{ListType, Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Struct {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub fields: Vec<Field>,
/// Whether there's a tag field on the body of this struct. When this is
/// true, is_enum_variant_body is also guaranteed to be true.
pub has_tag_field: bool,
/// Whether this is an enum variant body.
pub is_enum_variant_body: bool,
pub alignment: Option<ReprAlign>,
pub is_transparent: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub associated_constants: Vec<Constant>,
}
impl Struct {
/// Whether this struct can derive operator== / operator!=.
pub fn can_derive_eq(&self) -> bool {
!self.fields.is_empty() && self.fields.iter().all(|x| x.ty.can_cmp_eq())
}
pub fn add_associated_constant(&mut self, c: Constant) {
self.associated_constants.push(c);
}
pub fn load(
layout_config: &LayoutConfig,
item: &syn::ItemStruct,
mod_cfg: Option<&Cfg>,
) -> Result<Self, String> {
let repr = Repr::load(&item.attrs)?;
let is_transparent = match repr.style {
ReprStyle::C => false,
ReprStyle::Transparent => true,
_ => {
return Err("Struct is not marked #[repr(C)] or #[repr(transparent)].".to_owned());
}
};
let path = Path::new(item.ident.to_string());
// Ensure we can safely represent the struct given the configuration.
if let Some(align) = repr.align {
layout_config.ensure_safe_to_represent(&align)?;
}
let fields = match item.fields {
syn::Fields::Unit => Vec::new(),
syn::Fields::Named(ref fields) => fields
.named
.iter()
.try_skip_map(|field| Field::load(field, &path))?,
syn::Fields::Unnamed(ref fields) => {
let mut out = Vec::new();
let mut current = 0;
for field in fields.unnamed.iter() {
if let Some(mut ty) = Type::load(&field.ty)? {
ty.replace_self_with(&path);
out.push(Field {
name: format!("{}", current),
ty,
cfg: Cfg::load(&field.attrs),
annotations: AnnotationSet::load(&field.attrs)?,
documentation: Documentation::load(&field.attrs),
});
current += 1;
}
}
out
}
};
let has_tag_field = false;
let is_enum_variant_body = false;
Ok(Struct::new(
path,
GenericParams::new(&item.generics),
fields,
has_tag_field,
is_enum_variant_body,
repr.align,
is_transparent,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
#[allow(clippy::too_many_arguments)]
pub fn new(
path: Path,
generic_params: GenericParams,
fields: Vec<Field>,
has_tag_field: bool,
is_enum_variant_body: bool,
alignment: Option<ReprAlign>,
is_transparent: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
fields,
has_tag_field,
is_enum_variant_body,
alignment,
is_transparent,
cfg,
annotations,
documentation,
associated_constants: vec![],
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
for field in &mut self.fields {
field.ty.simplify_standard_types(config);
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic structs can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
for field in &self.fields {
field.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
for field in &mut self.fields {
field.ty.mangle_paths(monomorphs);
}
}
pub fn specialize(
&self,
generic_values: &[Type],
mappings: &[(&Path, &Type)],
config: &Config,
) -> Self {
let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle);
Struct::new(
mangled_path,
GenericParams::default(),
self.fields
.iter()
.map(|field| Field {
name: field.name.clone(),
ty: field.ty.specialize(mappings),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect(),
self.has_tag_field,
self.is_enum_variant_body,
self.alignment,
self.is_transparent,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
)
}
fn emit_bitflags_binop<F: Write>(
&self,
operator: char,
other: &str,
out: &mut SourceWriter<F>,
) {
out.new_line();
write!(
out,
"{} operator{}(const {}& {}) const",
self.export_name(),
operator,
self.export_name(),
other
);
out.open_brace();
write!(
out,
"return {{static_cast<decltype(bits)>(this->bits {} {}.bits)}};",
operator, other
);
out.close_brace(false);
out.new_line();
write!(
out,
"{}& operator{}=(const {}& {})",
self.export_name(),
operator,
self.export_name(),
other
);
out.open_brace();
write!(out, "*this = (*this {} {});", operator, other);
out.new_line();
write!(out, "return *this;");
out.close_brace(false);
}
}
impl Item for Struct {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Struct(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
if self.is_transparent {
resolver.add_none(&self.path);
} else {
resolver.add_struct(&self.path);
}
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for field in &mut self.fields {
field.ty.resolve_declaration_types(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
// Rename the name of the struct
if !(self.has_tag_field && config.language == Language::Cxx) {
config.export.rename(&mut self.export_name);
}
// Rename the types used in fields
{
let fields = self
.fields
.iter_mut()
.skip(if self.has_tag_field { 1 } else { 0 });
for field in fields {
field.ty.rename_for_config(config, &self.generic_params);
}
}
// Apply renaming rules to fields in the following order
// 1. `cbindgen::field-names` annotation
// 2. `cbindgen::rename-all` annotation
// 3. config struct rename rule
// If the struct is a tuple struct and we have not renamed the
// fields, then prefix each of them with an underscore.
// If any field is a reserved keyword, then postfix it with an
// underscore.
// Scope for mutable borrow of fields
{
let names = self.fields.iter_mut().map(|field| &mut field.name);
let field_rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.structure.rename_fields);
if let Some(o) = self.annotations.list("field-names") {
for (dest, src) in names.zip(o) {
*dest = src;
}
} else if let Some(r) = field_rules.not_none() {
for name in names {
*name = r.apply(name, IdentifierType::StructMember).into_owned();
}
} else {
// If we don't have any rules for a tuple struct, prefix them with
// an underscore so it still compiles.
for name in names {
if name.starts_with(|c: char| c.is_ascii_digit()) {
name.insert(0, '_');
}
}
}
}
for field in &mut self.fields {
reserved::escape(&mut field.name);
}
for c in self.associated_constants.iter_mut() {
c.rename_for_config(config);
}
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
let mut fields = self.fields.iter();
// If there is a tag field, skip it
if self.has_tag_field {
fields.next();
}
for field in fields {
field
.ty
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
for c in &self.associated_constants {
c.add_dependencies(library, out);
}
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let monomorph = self.specialize(generic_values, &mappings, library.get_config());
out.insert_struct(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Struct {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
if self.is_transparent {
let typedef = Typedef {
path: self.path.clone(),
export_name: self.export_name.to_owned(),
generic_params: self.generic_params.clone(),
aliased: self.fields[0].ty.clone(),
cfg: self.cfg.clone(),
annotations: self.annotations.clone(),
documentation: self.documentation.clone(),
};
typedef.write(config, out);
for constant in &self.associated_constants {
out.new_line();
constant.write(config, out, Some(self));
}
return;
}
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
if !self.is_enum_variant_body {
self.generic_params.write(config, out);
}
// The following results in
// C++ or C with Tag as style:
// struct Name {
// C with Type only style:
// typedef struct {
// C with Both as style:
// typedef struct Name {
match config.language {
Language::C if config.style.generate_typedef() => out.write("typedef "),
Language::C | Language::Cxx => {}
Language::Cython => out.write(config.style.cython_def()),
}
// Cython extern declarations don't manage layouts, layouts are defined entierly by the
// corresponding C code. So this `packed` is only for documentation, and missing
// `aligned(n)` is also not a problem.
if config.language == Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => out.write("packed "),
ReprAlign::Align(_) => {} // Not supported
}
}
}
out.write("struct");
if config.language != Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => {
if let Some(ref anno) = config.layout.packed {
write!(out, " {}", anno);
}
}
ReprAlign::Align(n) => {
if let Some(ref anno) = config.layout.aligned_n {
write!(out, " {}({})", anno, n);
}
}
}
}
}
if self.annotations.must_use(config) {
if let Some(ref anno) = config.structure.must_use {
write!(out, " {}", anno);
}
}
if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name());
}
out.open_brace();
// Emit the pre_body section, if relevant
if let Some(body) = config.export.pre_body(&self.path) {
out.write_raw_block(body);
out.new_line();
}
out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
if config.language == Language::Cython && self.fields.is_empty() {
out.write("pass");
}
if config.language == Language::Cxx {
let mut wrote_start_newline = false;
if config.structure.derive_constructor(&self.annotations) && !self.fields.is_empty() {
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
let arg_renamer = |name: &str| {
config
.function
.rename_args
.apply(name, IdentifierType::FunctionArg)
.into_owned()
};
write!(out, "{}(", self.export_name());
let vec: Vec<_> = self
.fields
.iter()
.map(|field| {
Field::from_name_and_type(
// const-ref args to constructor
format!("const& {}", arg_renamer(&field.name)),
field.ty.clone(),
)
})
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(","));
write!(out, ")");
out.new_line();
write!(out, " : ");
let vec: Vec<_> = self
.fields
.iter()
.map(|field| format!("{}({})", field.name, arg_renamer(&field.name)))
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(","));
out.new_line();
write!(out, "{{}}");
out.new_line();
}
let other = config
.function
.rename_args
.apply("other", IdentifierType::FunctionArg);
if self
.annotations
.bool("internal-derive-bitflags")
.unwrap_or(false)
{
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
write!(out, "explicit operator bool() const");
out.open_brace();
write!(out, "return !!bits;");
out.close_brace(false);
out.new_line();
write!(out, "{} operator~() const", self.export_name());
out.open_brace();
write!(out, "return {{static_cast<decltype(bits)>(~bits)}};");
out.close_brace(false);
self.emit_bitflags_binop('|', &other, out);
self.emit_bitflags_binop('&', &other, out);
self.emit_bitflags_binop('^', &other, out);
}
// Generate a serializer function that allows dumping this struct
// to an std::ostream. It's defined as a friend function inside the
// struct definition, and doesn't need the `inline` keyword even
// though it's implemented right in the generated header file.
if config.structure.derive_ostream(&self.annotations) {
if !wrote_start_newline {
wrote_start_newline = true;
out.new_line();
}
out.new_line();
let stream = config
.function
.rename_args
.apply("stream", IdentifierType::FunctionArg);
let instance = config
.function
.rename_args
.apply("instance", IdentifierType::FunctionArg);
write!(
out,
"friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
stream,
self.export_name(),
instance,
);
out.open_brace();
write!(out, "return {} << \"{{ \"", stream);
let vec: Vec<_> = self
.fields
.iter()
.map(|x| format!(" << \"{}=\" << {}.{}", x.name, instance, x.name))
.collect();
out.write_vertical_source_list(&vec[..], ListType::Join(" << \", \""));
out.write(" << \" }\";");
out.close_brace(false);
}
let skip_fields = if self.has_tag_field { 1 } else { 0 };
macro_rules! emit_op {
($op_name:expr, $op:expr, $conjuc:expr) => {{
if !wrote_start_newline {
#[allow(unused_assignments)]
{
wrote_start_newline = true;
}
out.new_line();
}
out.new_line();
if let Some(Some(attrs)) =
self.annotations.atom(concat!($op_name, "-attributes"))
{
write!(out, "{} ", attrs);
}
write!(
out,
"bool operator{}(const {}& {}) const",
$op,
self.export_name(),
other
);
out.open_brace();
out.write("return ");
let vec: Vec<_> = self
.fields
.iter()
.skip(skip_fields)
.map(|field| format!("{} {} {}.{}", field.name, $op, other, field.name))
.collect();
out.write_vertical_source_list(
&vec[..],
ListType::Join(&format!(" {}", $conjuc)),
);
out.write(";");
out.close_brace(false);
}};
}
if config.structure.derive_eq(&self.annotations) && self.can_derive_eq() {
emit_op!("eq", "==", "&&");
}
if config.structure.derive_neq(&self.annotations) && self.can_derive_eq() {
emit_op!("neq", "!=", "||");
}
if config.structure.derive_lt(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("lt", "<", "&&");
}
if config.structure.derive_lte(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("lte", "<=", "&&");
}
if config.structure.derive_gt(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("gt", ">", "&&");
}
if config.structure.derive_gte(&self.annotations)
&& self.fields.len() == 1
&& self.fields[0].ty.can_cmp_order()
{
emit_op!("gte", ">=", "&&");
}
}
// Emit the post_body section, if relevant
if let Some(body) = config.export.post_body(&self.path) {
out.new_line();
out.write_raw_block(body);
}
if config.language == Language::Cxx
&& config.structure.associated_constants_in_body
&& config.constant.allow_static_const
{
for constant in &self.associated_constants {
out.new_line();
constant.write_declaration(config, out, self);
}
}
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.export_name());
} else {
out.close_brace(true);
}
for constant in &self.associated_constants {
out.new_line();
constant.write(config, out, Some(self));
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,954 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::io::Write;
use crate::bindgen::cdecl;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{GenericParams, GenericPath, Path};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{Source, SourceWriter};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PrimitiveType {
Void,
Bool,
Char,
SChar,
UChar,
Char32,
Float,
Double,
VaList,
PtrDiffT,
Integer {
zeroable: bool,
signed: bool,
kind: IntKind,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum IntKind {
Short,
Int,
Long,
LongLong,
SizeT,
Size,
B8,
B16,
B32,
B64,
}
impl PrimitiveType {
pub fn maybe(path: &str) -> Option<PrimitiveType> {
Some(match path {
"c_void" => PrimitiveType::Void,
"c_char" => PrimitiveType::Char,
"c_schar" => PrimitiveType::SChar,
"c_uchar" => PrimitiveType::UChar,
"c_float" => PrimitiveType::Float,
"c_double" => PrimitiveType::Double,
"ptrdiff_t" => PrimitiveType::PtrDiffT,
"VaList" => PrimitiveType::VaList,
"bool" => PrimitiveType::Bool,
"char" => PrimitiveType::Char32,
"f32" => PrimitiveType::Float,
"f64" => PrimitiveType::Double,
_ => {
let (kind, signed) = match path {
"c_short" => (IntKind::Short, true),
"c_int" => (IntKind::Int, true),
"c_long" => (IntKind::Long, true),
"c_longlong" => (IntKind::LongLong, true),
"ssize_t" => (IntKind::SizeT, true),
"c_ushort" => (IntKind::Short, false),
"c_uint" => (IntKind::Int, false),
"c_ulong" => (IntKind::Long, false),
"c_ulonglong" => (IntKind::LongLong, false),
"size_t" => (IntKind::SizeT, false),
"isize" | "intptr_t" => (IntKind::Size, true),
"usize" | "uintptr_t" => (IntKind::Size, false),
"u8" | "uint8_t" => (IntKind::B8, false),
"u16" | "uint16_t" => (IntKind::B16, false),
"u32" | "uint32_t" => (IntKind::B32, false),
"u64" | "uint64_t" => (IntKind::B64, false),
"i8" | "int8_t" => (IntKind::B8, true),
"i16" | "int16_t" => (IntKind::B16, true),
"i32" | "int32_t" => (IntKind::B32, true),
"i64" | "int64_t" => (IntKind::B64, true),
_ => return None,
};
PrimitiveType::Integer {
zeroable: true,
signed,
kind,
}
}
})
}
pub fn to_repr_rust(&self) -> &'static str {
match *self {
PrimitiveType::Bool => "bool",
PrimitiveType::Void => "c_void",
PrimitiveType::Char => "c_char",
PrimitiveType::SChar => "c_schar",
PrimitiveType::UChar => "c_uchar",
PrimitiveType::Char32 => "char",
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match kind {
IntKind::Short => {
if signed {
"c_short"
} else {
"c_ushort"
}
}
IntKind::Int => {
if signed {
"c_int"
} else {
"c_uint"
}
}
IntKind::Long => {
if signed {
"c_long"
} else {
"c_ulong"
}
}
IntKind::LongLong => {
if signed {
"c_longlong"
} else {
"c_ulonglong"
}
}
IntKind::SizeT => {
if signed {
"ssize_t"
} else {
"size_t"
}
}
IntKind::Size => {
if signed {
"isize"
} else {
"usize"
}
}
IntKind::B8 => {
if signed {
"i8"
} else {
"u8"
}
}
IntKind::B16 => {
if signed {
"i16"
} else {
"u16"
}
}
IntKind::B32 => {
if signed {
"i32"
} else {
"u32"
}
}
IntKind::B64 => {
if signed {
"i64"
} else {
"u64"
}
}
},
PrimitiveType::Float => "f32",
PrimitiveType::Double => "f64",
PrimitiveType::PtrDiffT => "ptrdiff_t",
PrimitiveType::VaList => "va_list",
}
}
pub fn to_repr_c(&self, config: &Config) -> &'static str {
match *self {
PrimitiveType::Void => "void",
PrimitiveType::Bool => "bool",
PrimitiveType::Char => "char",
PrimitiveType::SChar => "signed char",
PrimitiveType::UChar => "unsigned char",
// NOTE: It'd be nice to use a char32_t, but:
//
// * uchar.h is not present on mac (see #423).
//
// * char32_t isn't required to be compatible with Rust's char, as
// the C++ spec only requires it to be the same size as
// uint_least32_t, which is _not_ guaranteed to be 4-bytes.
//
PrimitiveType::Char32 => "uint32_t",
PrimitiveType::Integer {
kind,
signed,
zeroable: _,
} => match kind {
IntKind::Short => {
if signed {
"short"
} else {
"unsigned short"
}
}
IntKind::Int => {
if signed {
"int"
} else {
"unsigned int"
}
}
IntKind::Long => {
if signed {
"long"
} else {
"unsigned long"
}
}
IntKind::LongLong => {
if signed {
"long long"
} else {
"unsigned long long"
}
}
IntKind::SizeT => {
if signed {
"ssize_t"
} else {
"size_t"
}
}
IntKind::Size => {
if config.usize_is_size_t {
if signed {
"ptrdiff_t"
} else {
"size_t"
}
} else if signed {
"intptr_t"
} else {
"uintptr_t"
}
}
IntKind::B8 => {
if signed {
"int8_t"
} else {
"uint8_t"
}
}
IntKind::B16 => {
if signed {
"int16_t"
} else {
"uint16_t"
}
}
IntKind::B32 => {
if signed {
"int32_t"
} else {
"uint32_t"
}
}
IntKind::B64 => {
if signed {
"int64_t"
} else {
"uint64_t"
}
}
},
PrimitiveType::Float => "float",
PrimitiveType::Double => "double",
PrimitiveType::PtrDiffT => "ptrdiff_t",
PrimitiveType::VaList => "va_list",
}
}
fn can_cmp_order(&self) -> bool {
match *self {
PrimitiveType::Bool => false,
_ => true,
}
}
fn can_cmp_eq(&self) -> bool {
true
}
}
// The `U` part of `[T; U]`
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ArrayLength {
Name(String),
Value(String),
}
impl ArrayLength {
pub fn as_str(&self) -> &str {
match self {
ArrayLength::Name(ref string) | ArrayLength::Value(ref string) => string,
}
}
fn rename_for_config(&mut self, config: &Config) {
if let ArrayLength::Name(ref mut name) = self {
config.export.rename(name);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Type {
Ptr {
ty: Box<Type>,
is_const: bool,
is_nullable: bool,
// FIXME: This is a bit of a hack, this is only to get us to codegen
// `T&` / `const T&`, but we should probably pass that down as an option
// to code generation or something.
is_ref: bool,
},
Path(GenericPath),
Primitive(PrimitiveType),
Array(Box<Type>, ArrayLength),
FuncPtr {
ret: Box<Type>,
args: Vec<(Option<String>, Type)>,
is_nullable: bool,
},
}
impl Type {
pub fn const_ref_to(ty: &Self) -> Self {
Type::Ptr {
ty: Box::new(ty.clone()),
is_const: true,
is_nullable: false,
is_ref: true,
}
}
pub fn load(ty: &syn::Type) -> Result<Option<Type>, String> {
let converted = match *ty {
syn::Type::Reference(ref reference) => {
let converted = Type::load(&reference.elem)?;
let converted = match converted {
Some(converted) => converted,
None => Type::Primitive(PrimitiveType::Void),
};
// TODO(emilio): we could make these use is_ref: true.
let is_const = reference.mutability.is_none();
Type::Ptr {
ty: Box::new(converted),
is_const,
is_nullable: false,
is_ref: false,
}
}
syn::Type::Ptr(ref pointer) => {
let converted = Type::load(&pointer.elem)?;
let converted = match converted {
Some(converted) => converted,
None => Type::Primitive(PrimitiveType::Void),
};
let is_const = pointer.mutability.is_none();
Type::Ptr {
ty: Box::new(converted),
is_const,
is_nullable: true,
is_ref: false,
}
}
syn::Type::Path(ref path) => {
let generic_path = GenericPath::load(&path.path)?;
if generic_path.name() == "PhantomData" || generic_path.name() == "PhantomPinned" {
return Ok(None);
}
if let Some(prim) = PrimitiveType::maybe(generic_path.name()) {
if !generic_path.generics().is_empty() {
return Err("Primitive has generics.".to_owned());
}
Type::Primitive(prim)
} else {
Type::Path(generic_path)
}
}
syn::Type::Array(syn::TypeArray {
ref elem,
len: syn::Expr::Path(ref path),
..
}) => {
let converted = Type::load(elem)?;
let converted = match converted {
Some(converted) => converted,
None => return Err("Cannot have an array of zero sized types.".to_owned()),
};
let generic_path = GenericPath::load(&path.path)?;
let len = ArrayLength::Name(generic_path.export_name().to_owned());
Type::Array(Box::new(converted), len)
}
syn::Type::Array(syn::TypeArray {
ref elem,
len:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Int(ref len),
..
}),
..
}) => {
let converted = Type::load(elem)?;
let converted = match converted {
Some(converted) => converted,
None => return Err("Cannot have an array of zero sized types.".to_owned()),
};
let len = ArrayLength::Value(len.base10_digits().to_string());
// panic!("panic -> value: {:?}", len);
Type::Array(Box::new(converted), len)
}
syn::Type::BareFn(ref function) => {
let mut wildcard_counter = 0;
let args = function.inputs.iter().try_skip_map(|x| {
Type::load(&x.ty).map(|opt_ty| {
opt_ty.map(|ty| {
(
x.name.as_ref().map(|(ref ident, _)| {
if ident == "_" {
wildcard_counter += 1;
if wildcard_counter == 1 {
"_".to_owned()
} else {
format!("_{}", wildcard_counter - 1)
}
} else {
ident.to_string()
}
}),
ty,
)
})
})
})?;
let ret = match function.output {
syn::ReturnType::Default => Type::Primitive(PrimitiveType::Void),
syn::ReturnType::Type(_, ref ty) => {
if let Some(x) = Type::load(ty)? {
x
} else {
Type::Primitive(PrimitiveType::Void)
}
}
};
Type::FuncPtr {
ret: Box::new(ret),
args,
is_nullable: false,
}
}
syn::Type::Tuple(ref tuple) => {
if tuple.elems.is_empty() {
return Ok(None);
}
return Err("Tuples are not supported types.".to_owned());
}
_ => return Err(format!("Unsupported type: {:?}", ty)),
};
Ok(Some(converted))
}
pub fn is_primitive_or_ptr_primitive(&self) -> bool {
match *self {
Type::Primitive(..) => true,
Type::Ptr { ref ty, .. } => match ty.as_ref() {
Type::Primitive(..) => true,
_ => false,
},
_ => false,
}
}
pub fn make_zeroable(&self) -> Option<Self> {
let (kind, signed) = match *self {
Type::Primitive(PrimitiveType::Integer {
zeroable: false,
kind,
signed,
}) => (kind, signed),
_ => return None,
};
Some(Type::Primitive(PrimitiveType::Integer {
kind,
signed,
zeroable: true,
}))
}
pub fn make_nullable(&self) -> Option<Self> {
match *self {
Type::Ptr {
ref ty,
is_const,
is_ref,
is_nullable: false,
} => Some(Type::Ptr {
ty: ty.clone(),
is_const,
is_ref,
is_nullable: true,
}),
Type::FuncPtr {
ref ret,
ref args,
is_nullable: false,
} => Some(Type::FuncPtr {
ret: ret.clone(),
args: args.clone(),
is_nullable: true,
}),
_ => None,
}
}
fn nonzero_to_primitive(&self) -> Option<Self> {
let path = match *self {
Type::Path(ref p) => p,
_ => return None,
};
if !path.generics().is_empty() {
return None;
}
let name = path.name();
if !name.starts_with("NonZero") {
return None;
}
let (kind, signed) = match path.name() {
"NonZeroU8" => (IntKind::B8, false),
"NonZeroU16" => (IntKind::B16, false),
"NonZeroU32" => (IntKind::B32, false),
"NonZeroU64" => (IntKind::B64, false),
"NonZeroUSize" => (IntKind::Size, false),
"NonZeroI8" => (IntKind::B8, true),
"NonZeroI16" => (IntKind::B16, true),
"NonZeroI32" => (IntKind::B32, true),
"NonZeroI64" => (IntKind::B64, true),
"NonZeroISize" => (IntKind::Size, true),
_ => return None,
};
Some(Type::Primitive(PrimitiveType::Integer {
zeroable: false,
signed,
kind,
}))
}
fn simplified_type(&self, config: &Config) -> Option<Self> {
let path = match *self {
Type::Path(ref p) => p,
_ => return None,
};
if path.generics().is_empty() {
return self.nonzero_to_primitive();
}
if path.generics().len() != 1 {
return None;
}
let unsimplified_generic = &path.generics()[0];
let generic = match unsimplified_generic.simplified_type(config) {
Some(generic) => Cow::Owned(generic),
None => Cow::Borrowed(unsimplified_generic),
};
match path.name() {
"Option" => {
if let Some(nullable) = generic.make_nullable() {
return Some(nullable);
}
if let Some(zeroable) = generic.make_zeroable() {
return Some(zeroable);
}
None
}
"NonNull" => Some(Type::Ptr {
ty: Box::new(generic.into_owned()),
is_const: false,
is_nullable: false,
is_ref: false,
}),
"Box" if config.language != Language::Cxx => Some(Type::Ptr {
ty: Box::new(generic.into_owned()),
is_const: false,
is_nullable: false,
is_ref: false,
}),
"Cell" => Some(generic.into_owned()),
"ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => {
Some(generic.into_owned())
}
_ => None,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.visit_types(|ty| ty.simplify_standard_types(config));
if let Some(ty) = self.simplified_type(config) {
*self = ty;
}
}
pub fn replace_self_with(&mut self, self_ty: &Path) {
if let Type::Path(ref mut generic_path) = *self {
generic_path.replace_self_with(self_ty);
}
self.visit_types(|ty| ty.replace_self_with(self_ty))
}
fn visit_types(&mut self, mut visitor: impl FnMut(&mut Type)) {
match *self {
Type::Array(ref mut ty, ..) | Type::Ptr { ref mut ty, .. } => visitor(ty),
Type::Path(ref mut path) => {
for generic in path.generics_mut() {
visitor(generic);
}
}
Type::Primitive(..) => {}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
visitor(ret);
for arg in args {
visitor(&mut arg.1)
}
}
}
}
pub fn get_root_path(&self) -> Option<Path> {
let mut current = self;
loop {
match *current {
Type::Ptr { ref ty, .. } => current = ty,
Type::Path(ref generic) => {
return Some(generic.path().clone());
}
Type::Primitive(..) => {
return None;
}
Type::Array(..) => {
return None;
}
Type::FuncPtr { .. } => {
return None;
}
};
}
}
pub fn specialize(&self, mappings: &[(&Path, &Type)]) -> Type {
match *self {
Type::Ptr {
ref ty,
is_const,
is_nullable,
is_ref,
} => Type::Ptr {
ty: Box::new(ty.specialize(mappings)),
is_const,
is_nullable,
is_ref,
},
Type::Path(ref generic_path) => {
for &(param, value) in mappings {
if generic_path.path() == param {
return value.clone();
}
}
let specialized = GenericPath::new(
generic_path.path().clone(),
generic_path
.generics()
.iter()
.map(|x| x.specialize(mappings))
.collect(),
);
Type::Path(specialized)
}
Type::Primitive(ref primitive) => Type::Primitive(primitive.clone()),
Type::Array(ref ty, ref constant) => {
Type::Array(Box::new(ty.specialize(mappings)), constant.clone())
}
Type::FuncPtr {
ref ret,
ref args,
is_nullable,
} => Type::FuncPtr {
ret: Box::new(ret.specialize(mappings)),
args: args
.iter()
.cloned()
.map(|(name, ty)| (name, ty.specialize(mappings)))
.collect(),
is_nullable,
},
}
}
pub fn add_dependencies_ignoring_generics(
&self,
generic_params: &GenericParams,
library: &Library,
out: &mut Dependencies,
) {
match *self {
Type::Ptr { ref ty, .. } => {
ty.add_dependencies_ignoring_generics(generic_params, library, out);
}
Type::Path(ref generic) => {
for generic_value in generic.generics() {
generic_value.add_dependencies_ignoring_generics(generic_params, library, out);
}
let path = generic.path();
if !generic_params.contains(path) {
if let Some(items) = library.get_items(path) {
if !out.items.contains(path) {
out.items.insert(path.clone());
for item in &items {
item.deref().add_dependencies(library, out);
}
for item in items {
out.order.push(item);
}
}
} else {
warn!(
"Can't find {}. This usually means that this type was incompatible or \
not found.",
path
);
}
}
}
Type::Primitive(_) => {}
Type::Array(ref ty, _) => {
ty.add_dependencies_ignoring_generics(generic_params, library, out);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
ret.add_dependencies_ignoring_generics(generic_params, library, out);
for (_, ref arg) in args {
arg.add_dependencies_ignoring_generics(generic_params, library, out);
}
}
}
}
pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.add_dependencies_ignoring_generics(&GenericParams::default(), library, out)
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
match *self {
Type::Ptr { ref ty, .. } => {
ty.add_monomorphs(library, out);
}
Type::Path(ref generic) => {
if generic.generics().is_empty() || out.contains(generic) {
return;
}
let path = generic.path();
if let Some(items) = library.get_items(path) {
for item in items {
item.deref()
.instantiate_monomorph(generic.generics(), library, out);
}
}
}
Type::Primitive(_) => {}
Type::Array(ref ty, _) => {
ty.add_monomorphs(library, out);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
ret.add_monomorphs(library, out);
for (_, ref arg) in args {
arg.add_monomorphs(library, out);
}
}
}
}
pub fn rename_for_config(&mut self, config: &Config, generic_params: &GenericParams) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.rename_for_config(config, generic_params);
}
Type::Path(ref mut ty) => {
ty.rename_for_config(config, generic_params);
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, ref mut len) => {
ty.rename_for_config(config, generic_params);
len.rename_for_config(config);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.rename_for_config(config, generic_params);
for (_, arg) in args {
arg.rename_for_config(config, generic_params);
}
}
}
}
pub fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.resolve_declaration_types(resolver);
}
Type::Path(ref mut generic_path) => {
generic_path.resolve_declaration_types(resolver);
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, _) => {
ty.resolve_declaration_types(resolver);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.resolve_declaration_types(resolver);
for (_, ref mut arg) in args {
arg.resolve_declaration_types(resolver);
}
}
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
match *self {
Type::Ptr { ref mut ty, .. } => {
ty.mangle_paths(monomorphs);
}
Type::Path(ref mut generic_path) => {
if generic_path.generics().is_empty() {
return;
}
if let Some(mangled_path) = monomorphs.mangle_path(generic_path) {
*generic_path = GenericPath::new(mangled_path.clone(), vec![]);
} else {
warn!(
"Cannot find a mangling for generic path {:?}. This usually means that a \
type referenced by this generic was incompatible or not found.",
generic_path
);
}
}
Type::Primitive(_) => {}
Type::Array(ref mut ty, _) => {
ty.mangle_paths(monomorphs);
}
Type::FuncPtr {
ref mut ret,
ref mut args,
..
} => {
ret.mangle_paths(monomorphs);
for (_, ref mut arg) in args {
arg.mangle_paths(monomorphs);
}
}
}
}
pub fn can_cmp_order(&self) -> bool {
match *self {
// FIXME: Shouldn't this look at ty.can_cmp_order() as well?
Type::Ptr { is_ref, .. } => !is_ref,
Type::Path(..) => true,
Type::Primitive(ref p) => p.can_cmp_order(),
Type::Array(..) => false,
Type::FuncPtr { .. } => false,
}
}
pub fn can_cmp_eq(&self) -> bool {
match *self {
Type::Ptr { ref ty, is_ref, .. } => !is_ref || ty.can_cmp_eq(),
Type::Path(..) => true,
Type::Primitive(ref p) => p.can_cmp_eq(),
Type::Array(..) => false,
Type::FuncPtr { .. } => true,
}
}
}
impl Source for String {
fn write<F: Write>(&self, _config: &Config, out: &mut SourceWriter<F>) {
write!(out, "{}", self);
}
}
impl Source for Type {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
cdecl::write_type(out, self, config);
}
}

View File

@@ -0,0 +1,223 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::io::Write;
use crate::bindgen::config::{Config, Language};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer,
Path, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::writer::{Source, SourceWriter};
/// A type alias that is represented as a C typedef
#[derive(Debug, Clone)]
pub struct Typedef {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub aliased: Type,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Typedef {
pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result<Typedef, String> {
if let Some(x) = Type::load(&item.ty)? {
let path = Path::new(item.ident.to_string());
Ok(Typedef::new(
path,
GenericParams::new(&item.generics),
x,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
} else {
Err("Cannot have a typedef of a zero sized type.".to_owned())
}
}
pub fn new(
path: Path,
generic_params: GenericParams,
aliased: Type,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
aliased,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
self.aliased.simplify_standard_types(config);
}
pub fn transfer_annotations(&mut self, out: &mut HashMap<Path, AnnotationSet>) {
if self.annotations.is_empty() {
return;
}
if let Some(alias_path) = self.aliased.get_root_path() {
if out.contains_key(&alias_path) {
warn!(
"Multiple typedef's with annotations for {}. Ignoring annotations from {}.",
alias_path, self.path
);
return;
}
out.insert(alias_path, self.annotations.clone());
self.annotations = AnnotationSet::new();
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic structs can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
self.aliased.add_monomorphs(library, out);
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
self.aliased.mangle_paths(monomorphs);
}
}
impl Item for Typedef {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Typedef(self.clone())
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
self.aliased.rename_for_config(config, &self.generic_params);
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_none(&self.path);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
self.aliased.resolve_declaration_types(resolver);
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
self.aliased
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = Typedef::new(
mangled_path,
GenericParams::default(),
self.aliased.specialize(&mappings),
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_typedef(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Typedef {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write(config, out);
match config.language {
Language::Cxx => {
write!(out, "using {} = ", self.export_name());
self.aliased.write(config, out);
}
Language::C | Language::Cython => {
write!(out, "{} ", config.language.typedef());
Field::from_name_and_type(self.export_name().to_owned(), self.aliased.clone())
.write(config, out);
}
}
out.write(";");
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,351 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io::Write;
use crate::bindgen::config::{Config, Language, LayoutConfig};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::ir::{
AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericParams, Item, ItemContainer,
Path, Repr, ReprAlign, ReprStyle, ToCondition, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::utilities::IterHelpers;
use crate::bindgen::writer::{ListType, Source, SourceWriter};
#[derive(Debug, Clone)]
pub struct Union {
pub path: Path,
pub export_name: String,
pub generic_params: GenericParams,
pub fields: Vec<Field>,
pub tuple_union: bool,
pub alignment: Option<ReprAlign>,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
}
impl Union {
pub fn load(
layout_config: &LayoutConfig,
item: &syn::ItemUnion,
mod_cfg: Option<&Cfg>,
) -> Result<Union, String> {
let repr = Repr::load(&item.attrs)?;
if repr.style != ReprStyle::C {
return Err("Union is not marked #[repr(C)].".to_owned());
}
// Ensure we can safely represent the union given the configuration.
if let Some(align) = repr.align {
layout_config.ensure_safe_to_represent(&align)?;
}
let path = Path::new(item.ident.to_string());
let (fields, tuple_union) = {
let out = item
.fields
.named
.iter()
.try_skip_map(|field| Field::load(field, &path))?;
(out, false)
};
Ok(Union::new(
path,
GenericParams::new(&item.generics),
fields,
repr.align,
tuple_union,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
AnnotationSet::load(&item.attrs)?,
Documentation::load(&item.attrs),
))
}
#[allow(clippy::too_many_arguments)]
pub fn new(
path: Path,
generic_params: GenericParams,
fields: Vec<Field>,
alignment: Option<ReprAlign>,
tuple_union: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
) -> Self {
let export_name = path.name().to_owned();
Self {
path,
export_name,
generic_params,
fields,
tuple_union,
alignment,
cfg,
annotations,
documentation,
}
}
pub fn simplify_standard_types(&mut self, config: &Config) {
for field in &mut self.fields {
field.ty.simplify_standard_types(config);
}
}
pub fn is_generic(&self) -> bool {
self.generic_params.len() > 0
}
pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
// Generic unions can instantiate monomorphs only once they've been
// instantiated. See `instantiate_monomorph` for more details.
if self.is_generic() {
return;
}
for field in &self.fields {
field.ty.add_monomorphs(library, out);
}
}
pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
for field in &mut self.fields {
field.ty.mangle_paths(monomorphs);
}
}
}
impl Item for Union {
fn path(&self) -> &Path {
&self.path
}
fn export_name(&self) -> &str {
&self.export_name
}
fn cfg(&self) -> Option<&Cfg> {
self.cfg.as_ref()
}
fn annotations(&self) -> &AnnotationSet {
&self.annotations
}
fn annotations_mut(&mut self) -> &mut AnnotationSet {
&mut self.annotations
}
fn container(&self) -> ItemContainer {
ItemContainer::Union(self.clone())
}
fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
resolver.add_union(&self.path);
}
fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
for field in &mut self.fields {
field.ty.resolve_declaration_types(resolver);
}
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
for field in &mut self.fields {
field.ty.rename_for_config(config, &self.generic_params);
}
let rules = self
.annotations
.parse_atom::<RenameRule>("rename-all")
.unwrap_or(config.structure.rename_fields);
if let Some(o) = self.annotations.list("field-names") {
let mut overriden_fields = Vec::new();
for (i, field) in self.fields.iter().enumerate() {
if i >= o.len() {
overriden_fields.push(field.clone());
} else {
overriden_fields.push(Field {
name: o[i].clone(),
ty: field.ty.clone(),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
});
}
}
self.fields = overriden_fields;
} else if let Some(r) = rules.not_none() {
self.fields = self
.fields
.iter()
.map(|field| Field {
name: r
.apply(&field.name, IdentifierType::StructMember)
.into_owned(),
ty: field.ty.clone(),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect();
} else if self.tuple_union {
// If we don't have any rules for a tuple union, prefix them with
// an underscore so it still compiles
for field in &mut self.fields {
field.name.insert(0, '_');
}
}
}
fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
for field in &self.fields {
field
.ty
.add_dependencies_ignoring_generics(&self.generic_params, library, out);
}
}
fn instantiate_monomorph(
&self,
generic_values: &[Type],
library: &Library,
out: &mut Monomorphs,
) {
assert!(
self.generic_params.len() > 0,
"{} is not generic",
self.path
);
assert!(
self.generic_params.len() == generic_values.len(),
"{} has {} params but is being instantiated with {} values",
self.path,
self.generic_params.len(),
generic_values.len(),
);
let mappings = self
.generic_params
.iter()
.zip(generic_values.iter())
.collect::<Vec<_>>();
let mangled_path = mangle::mangle_path(
&self.path,
generic_values,
&library.get_config().export.mangle,
);
let monomorph = Union::new(
mangled_path,
GenericParams::default(),
self.fields
.iter()
.map(|field| Field {
name: field.name.clone(),
ty: field.ty.specialize(&mappings),
cfg: field.cfg.clone(),
annotations: field.annotations.clone(),
documentation: field.documentation.clone(),
})
.collect(),
self.alignment,
self.tuple_union,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);
out.insert_union(library, self, monomorph, generic_values.to_owned());
}
}
impl Source for Union {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let condition = self.cfg.to_condition(config);
condition.write_before(config, out);
self.documentation.write(config, out);
self.generic_params.write(config, out);
// The following results in
// C++ or C with Tag as style:
// union Name {
// C with Type only style:
// typedef union {
// C with Both as style:
// typedef union Name {
match config.language {
Language::C if config.style.generate_typedef() => out.write("typedef "),
Language::C | Language::Cxx => {}
Language::Cython => out.write(config.style.cython_def()),
}
out.write("union");
// Cython supports `packed` on structs (see comments there), but not on unions.
if config.language != Language::Cython {
if let Some(align) = self.alignment {
match align {
ReprAlign::Packed => {
if let Some(ref anno) = config.layout.packed {
write!(out, " {}", anno);
}
}
ReprAlign::Align(n) => {
if let Some(ref anno) = config.layout.aligned_n {
write!(out, " {}({})", anno, n);
}
}
}
}
}
if config.language != Language::C || config.style.generate_tag() {
write!(out, " {}", self.export_name);
}
out.open_brace();
// Emit the pre_body section, if relevant
if let Some(body) = config.export.pre_body(&self.path) {
out.write_raw_block(body);
out.new_line();
}
out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
if config.language == Language::Cython && self.fields.is_empty() {
out.write("pass");
}
// Emit the post_body section, if relevant
if let Some(body) = config.export.post_body(&self.path) {
out.new_line();
out.write_raw_block(body);
}
if config.language == Language::C && config.style.generate_typedef() {
out.close_brace(false);
write!(out, " {};", self.export_name);
} else {
out.close_brace(true);
}
condition.write_after(config, out);
}
}

View File

@@ -0,0 +1,436 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use crate::bindgen::bindings::Bindings;
use crate::bindgen::config::{Config, Language, SortKey};
use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
use crate::bindgen::dependencies::Dependencies;
use crate::bindgen::error::Error;
use crate::bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap};
use crate::bindgen::ir::{OpaqueItem, Path, Static, Struct, Typedef, Union};
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::ItemType;
#[derive(Debug, Clone)]
pub struct Library {
config: Config,
constants: ItemMap<Constant>,
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
}
impl Library {
#[allow(clippy::too_many_arguments)]
pub fn new(
config: Config,
constants: ItemMap<Constant>,
globals: ItemMap<Static>,
enums: ItemMap<Enum>,
structs: ItemMap<Struct>,
unions: ItemMap<Union>,
opaque_items: ItemMap<OpaqueItem>,
typedefs: ItemMap<Typedef>,
functions: Vec<Function>,
) -> Library {
Library {
config,
constants,
globals,
enums,
structs,
unions,
opaque_items,
typedefs,
functions,
}
}
pub fn generate(mut self) -> Result<Bindings, Error> {
self.transfer_annotations();
self.simplify_standard_types();
match self.config.function.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => self.functions.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
if self.config.language != Language::Cxx {
self.instantiate_monomorphs();
}
self.remove_excluded();
if self.config.language == Language::C {
self.resolve_declaration_types();
}
self.rename_items();
let mut dependencies = Dependencies::new();
for function in &self.functions {
function.add_dependencies(&self, &mut dependencies);
}
self.globals.for_all_items(|global| {
global.add_dependencies(&self, &mut dependencies);
});
self.constants.for_all_items(|constant| {
constant.add_dependencies(&self, &mut dependencies);
});
for name in &self.config.export.include {
let path = Path::new(name.clone());
if let Some(items) = self.get_items(&path) {
if dependencies.items.insert(path) {
for item in &items {
item.deref().add_dependencies(&self, &mut dependencies);
}
for item in items {
dependencies.order.push(item);
}
}
}
}
dependencies.sort();
let items = dependencies.order;
let constants = if self.config.export.should_generate(ItemType::Constants) {
let mut constants = self.constants.to_vec();
match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => constants.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
constants
} else {
vec![]
};
let globals = if self.config.export.should_generate(ItemType::Globals) {
let mut globals = self.globals.to_vec();
match self.config.constant.sort_by.unwrap_or(self.config.sort_by) {
SortKey::Name => globals.sort_by(|x, y| x.path.cmp(&y.path)),
SortKey::None => { /* keep input order */ }
}
globals
} else {
vec![]
};
let functions = if self.config.export.should_generate(ItemType::Functions) {
self.functions
} else {
vec![]
};
Ok(Bindings::new(
self.config,
self.structs,
constants,
globals,
items,
functions,
))
}
pub fn get_items(&self, p: &Path) -> Option<Vec<ItemContainer>> {
macro_rules! find {
($field:ident, $kind:ident) => {
if self.config.export.should_generate(ItemType::$kind) {
if let Some(x) = self.$field.get_items(p) {
return Some(x);
}
}
};
}
find!(enums, Enums);
find!(structs, Structs);
find!(unions, Unions);
find!(opaque_items, OpaqueItems);
find!(typedefs, Typedefs);
None
}
pub fn get_config(&self) -> &Config {
&self.config
}
fn remove_excluded(&mut self) {
let config = &self.config;
// FIXME: interpret `config.export.exclude` as `Path`s.
self.functions
.retain(|x| !config.export.exclude.iter().any(|y| y == x.path().name()));
self.enums
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.structs
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.unions
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.opaque_items
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.typedefs
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.globals
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
self.constants
.filter(|x| config.export.exclude.iter().any(|y| y == x.path().name()));
}
fn transfer_annotations(&mut self) {
let mut annotations = HashMap::new();
self.typedefs.for_all_items_mut(|x| {
x.transfer_annotations(&mut annotations);
});
for (alias_path, annotations) in annotations {
// TODO
let mut transferred = false;
self.enums.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.structs.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.unions.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.opaque_items.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
self.typedefs.for_items_mut(&alias_path, |x| {
if x.annotations().is_empty() {
*x.annotations_mut() = annotations.clone();
transferred = true;
} else {
warn!(
"Can't transfer annotations from typedef to alias ({}) \
that already has annotations.",
alias_path
);
}
});
if transferred {
continue;
}
}
}
fn rename_items(&mut self) {
let config = &self.config;
self.globals
.for_all_items_mut(|x| x.rename_for_config(config));
self.globals.rebuild();
self.constants
.for_all_items_mut(|x| x.rename_for_config(config));
self.constants.rebuild();
self.structs
.for_all_items_mut(|x| x.rename_for_config(config));
self.structs.rebuild();
self.unions
.for_all_items_mut(|x| x.rename_for_config(config));
self.unions.rebuild();
self.enums
.for_all_items_mut(|x| x.rename_for_config(config));
self.enums.rebuild();
self.opaque_items
.for_all_items_mut(|x| x.rename_for_config(config));
self.opaque_items.rebuild();
self.typedefs
.for_all_items_mut(|x| x.rename_for_config(config));
self.typedefs.rebuild();
for item in &mut self.functions {
item.rename_for_config(&self.config);
}
}
fn resolve_declaration_types(&mut self) {
if !self.config.style.generate_tag() {
return;
}
let mut resolver = DeclarationTypeResolver::default();
self.structs.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.unions.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.typedefs.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
// NOTE: Intentionally last, so that in case there's an opaque type
// which is conflicting with a non-opaque one, the later wins.
self.opaque_items.for_all_items(|x| {
x.collect_declaration_types(&mut resolver);
});
self.enums
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.structs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.unions
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.typedefs
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
self.globals
.for_all_items_mut(|x| x.resolve_declaration_types(&resolver));
for item in &mut self.functions {
item.resolve_declaration_types(&resolver);
}
}
fn simplify_standard_types(&mut self) {
let config = &self.config;
self.structs.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.unions.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.globals.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
self.typedefs.for_all_items_mut(|x| {
x.simplify_standard_types(config);
});
for x in &mut self.functions {
x.simplify_standard_types(config);
}
}
fn instantiate_monomorphs(&mut self) {
// Collect a list of monomorphs
let mut monomorphs = Monomorphs::default();
self.structs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.unions.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.enums.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
self.typedefs.for_all_items(|x| {
x.add_monomorphs(self, &mut monomorphs);
});
for x in &self.functions {
x.add_monomorphs(self, &mut monomorphs);
}
// Insert the monomorphs into self
for monomorph in monomorphs.drain_structs() {
self.structs.try_insert(monomorph);
}
for monomorph in monomorphs.drain_unions() {
self.unions.try_insert(monomorph);
}
for monomorph in monomorphs.drain_opaques() {
self.opaque_items.try_insert(monomorph);
}
for monomorph in monomorphs.drain_typedefs() {
self.typedefs.try_insert(monomorph);
}
for monomorph in monomorphs.drain_enums() {
self.enums.try_insert(monomorph);
}
// Remove structs and opaque items that are generic
self.opaque_items.filter(|x| x.generic_params.len() > 0);
self.structs.filter(|x| x.generic_params.len() > 0);
self.unions.filter(|x| x.generic_params.len() > 0);
self.enums.filter(|x| x.generic_params.len() > 0);
self.typedefs.filter(|x| x.generic_params.len() > 0);
// Mangle the paths that remain
self.unions
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.structs
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.enums
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
self.typedefs
.for_all_items_mut(|x| x.mangle_paths(&monomorphs));
for x in &mut self.functions {
x.mangle_paths(&monomorphs);
}
}
}

View File

@@ -0,0 +1,291 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bindgen::config::MangleConfig;
use crate::bindgen::ir::{Path, Type};
use crate::bindgen::rename::IdentifierType;
pub fn mangle_path(path: &Path, generic_values: &[Type], config: &MangleConfig) -> Path {
Path::new(mangle_name(path.name(), generic_values, config))
}
pub fn mangle_name(name: &str, generic_values: &[Type], config: &MangleConfig) -> String {
Mangler::new(name, generic_values, /* last = */ true, config).mangle()
}
enum Separator {
OpeningAngleBracket = 1,
Comma,
ClosingAngleBracket,
BeginMutPtr,
BeginConstPtr,
BeginFn,
BetweenFnArg,
EndFn,
}
struct Mangler<'a> {
input: &'a str,
generic_values: &'a [Type],
output: String,
last: bool,
config: &'a MangleConfig,
}
impl<'a> Mangler<'a> {
fn new(
input: &'a str,
generic_values: &'a [Type],
last: bool,
config: &'a MangleConfig,
) -> Self {
Self {
input,
generic_values,
output: String::new(),
last,
config,
}
}
fn mangle(mut self) -> String {
self.mangle_internal();
self.output
}
fn push(&mut self, id: Separator) {
let count = id as usize;
let separator = if self.config.remove_underscores {
""
} else {
"_"
};
self.output.extend(std::iter::repeat(separator).take(count));
}
fn append_mangled_type(&mut self, ty: &Type, last: bool) {
match *ty {
Type::Path(ref generic) => {
let sub_path =
Mangler::new(generic.export_name(), generic.generics(), last, self.config)
.mangle();
self.output.push_str(
&self
.config
.rename_types
.apply(&sub_path, IdentifierType::Type),
);
}
Type::Primitive(ref primitive) => {
self.output.push_str(
&self
.config
.rename_types
.apply(primitive.to_repr_rust(), IdentifierType::Type),
);
}
Type::Ptr {
ref ty, is_const, ..
} => {
self.push(if is_const {
Separator::BeginConstPtr
} else {
Separator::BeginMutPtr
});
self.append_mangled_type(&**ty, last);
}
Type::FuncPtr {
ref ret, ref args, ..
} => {
self.push(Separator::BeginFn);
self.append_mangled_type(&**ret, args.is_empty());
for (i, arg) in args.iter().enumerate() {
self.push(Separator::BetweenFnArg);
let last = last && i == args.len() - 1;
self.append_mangled_type(&arg.1, last);
}
if !self.last {
self.push(Separator::EndFn);
}
}
Type::Array(..) => {
unimplemented!(
"Unable to mangle generic parameter {:?} for '{}'",
ty,
self.input
);
}
}
}
fn mangle_internal(&mut self) {
debug_assert!(self.output.is_empty());
self.output = self.input.to_owned();
if self.generic_values.is_empty() {
return;
}
self.push(Separator::OpeningAngleBracket);
for (i, ty) in self.generic_values.iter().enumerate() {
if i != 0 {
self.push(Separator::Comma);
}
let last = self.last && i == self.generic_values.len() - 1;
self.append_mangled_type(ty, last);
}
// Skip writing the trailing '>' mangling when possible
if !self.last {
self.push(Separator::ClosingAngleBracket)
}
}
}
#[test]
fn generics() {
use crate::bindgen::ir::{GenericPath, PrimitiveType};
use crate::bindgen::rename::RenameRule::{self, PascalCase};
fn float() -> Type {
Type::Primitive(PrimitiveType::Float)
}
fn c_char() -> Type {
Type::Primitive(PrimitiveType::Char)
}
fn path(path: &str) -> Type {
generic_path(path, &[])
}
fn generic_path(path: &str, generics: &[Type]) -> Type {
let path = Path::new(path);
let generic_path = GenericPath::new(path, generics.to_owned());
Type::Path(generic_path)
}
// Foo<f32> => Foo_f32
assert_eq!(
mangle_path(&Path::new("Foo"), &[float()], &MangleConfig::default()),
Path::new("Foo_f32")
);
// Foo<Bar<f32>> => Foo_Bar_f32
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[float()])],
&MangleConfig::default(),
),
Path::new("Foo_Bar_f32")
);
// Foo<Bar> => Foo_Bar
assert_eq!(
mangle_path(&Path::new("Foo"), &[path("Bar")], &MangleConfig::default()),
Path::new("Foo_Bar")
);
// Foo<Bar> => FooBar
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[path("Bar")],
&MangleConfig {
remove_underscores: true,
rename_types: RenameRule::None,
}
),
Path::new("FooBar")
);
// Foo<Bar<f32>> => FooBarF32
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[float()])],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarF32")
);
// Foo<Bar<c_char>> => FooBarCChar
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[c_char()])],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarCChar")
);
// Foo<Bar<T>> => Foo_Bar_T
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")])],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T")
);
// Foo<Bar<T>, E> => Foo_Bar_T_____E
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")]), path("E")],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T_____E")
);
// Foo<Bar<T>, Bar<E>> => Foo_Bar_T_____Bar_E
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[
generic_path("Bar", &[path("T")]),
generic_path("Bar", &[path("E")]),
],
&MangleConfig::default(),
),
Path::new("Foo_Bar_T_____Bar_E")
);
// Foo<Bar<T>, E> => FooBarTE
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[generic_path("Bar", &[path("T")]), path("E")],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarTE")
);
// Foo<Bar<T>, Bar<E>> => FooBarTBarE
assert_eq!(
mangle_path(
&Path::new("Foo"),
&[
generic_path("Bar", &[path("T")]),
generic_path("Bar", &[path("E")]),
],
&MangleConfig {
remove_underscores: true,
rename_types: PascalCase,
},
),
Path::new("FooBarTBarE")
);
}

View File

@@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// A helper macro for deriving deserialize for an enum to be used in toml-rs.
/// This macro works be relying on an existing FromStr implementation for the
/// desired type.
macro_rules! deserialize_enum_str {
($name:ident) => {
impl<'de> ::serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
struct Visitor;
impl<'de> ::serde::de::Visitor<'de> for Visitor {
type Value = $name;
fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.write_str("$name")
}
fn visit_str<E>(self, v: &str) -> Result<$name, E>
where
E: ::serde::de::Error,
{
match v.parse::<$name>() {
Ok(v) => Ok(v),
Err(m) => Err(E::custom(m)),
}
}
}
deserializer.deserialize_str(Visitor)
}
}
};
}
mod bindings;
mod bitflags;
mod builder;
mod cargo;
mod cdecl;
mod config;
mod declarationtyperesolver;
mod dependencies;
mod error;
mod ir;
mod library;
mod mangle;
mod monomorph;
mod parser;
mod rename;
mod reserved;
mod utilities;
mod writer;
#[allow(unused)]
pub(crate) use self::cargo::*;
pub use self::bindings::Bindings;
pub use self::builder::Builder;
pub use self::config::Profile; // disambiguate with cargo::Profile
pub use self::config::*;
pub use self::error::Error;

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use std::mem;
use crate::bindgen::ir::{Enum, GenericPath, OpaqueItem, Path, Struct, Type, Typedef, Union};
use crate::bindgen::library::Library;
#[derive(Default, Clone, Debug)]
pub struct Monomorphs {
replacements: HashMap<GenericPath, Path>,
opaques: Vec<OpaqueItem>,
structs: Vec<Struct>,
unions: Vec<Union>,
typedefs: Vec<Typedef>,
enums: Vec<Enum>,
}
impl Monomorphs {
pub fn contains(&self, path: &GenericPath) -> bool {
self.replacements.contains_key(path)
}
pub fn insert_struct(
&mut self,
library: &Library,
generic: &Struct,
monomorph: Struct,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.structs.push(monomorph);
}
pub fn insert_enum(
&mut self,
library: &Library,
generic: &Enum,
monomorph: Enum,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.enums.push(monomorph);
}
pub fn insert_union(
&mut self,
library: &Library,
generic: &Union,
monomorph: Union,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.unions.push(monomorph);
}
pub fn insert_opaque(
&mut self,
generic: &OpaqueItem,
monomorph: OpaqueItem,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
self.opaques.push(monomorph);
}
pub fn insert_typedef(
&mut self,
library: &Library,
generic: &Typedef,
monomorph: Typedef,
parameters: Vec<Type>,
) {
let replacement_path = GenericPath::new(generic.path.clone(), parameters);
debug_assert!(generic.generic_params.len() > 0);
debug_assert!(!self.contains(&replacement_path));
self.replacements
.insert(replacement_path, monomorph.path.clone());
monomorph.add_monomorphs(library, self);
self.typedefs.push(monomorph);
}
pub fn mangle_path(&self, path: &GenericPath) -> Option<&Path> {
self.replacements.get(path)
}
pub fn drain_opaques(&mut self) -> Vec<OpaqueItem> {
mem::take(&mut self.opaques)
}
pub fn drain_structs(&mut self) -> Vec<Struct> {
mem::take(&mut self.structs)
}
pub fn drain_unions(&mut self) -> Vec<Union> {
mem::take(&mut self.unions)
}
pub fn drain_typedefs(&mut self) -> Vec<Typedef> {
mem::take(&mut self.typedefs)
}
pub fn drain_enums(&mut self) -> Vec<Enum> {
mem::take(&mut self.enums)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::borrow::Cow;
use std::str::FromStr;
/// The type of identifier to be renamed.
#[derive(Debug, Clone, Copy)]
pub enum IdentifierType<'a> {
StructMember,
EnumVariant { prefix: &'a str },
FunctionArg,
Type,
Enum,
}
impl<'a> IdentifierType<'a> {
fn to_str(self) -> &'static str {
match self {
IdentifierType::StructMember => "m",
IdentifierType::EnumVariant { .. } => "",
IdentifierType::FunctionArg => "a",
IdentifierType::Type => "",
IdentifierType::Enum => "",
}
}
}
/// A rule to apply to an identifier when generating bindings.
#[derive(Debug, Clone, Copy)]
pub enum RenameRule {
/// Do not apply any renaming. The default.
None,
/// Converts the identifier to PascalCase and adds a context dependent prefix
GeckoCase,
/// Converts the identifier to lower case.
LowerCase,
/// Converts the identifier to upper case.
UpperCase,
/// Converts the identifier to PascalCase.
PascalCase,
/// Converts the identifier to camelCase.
CamelCase,
/// Converts the identifier to snake_case.
SnakeCase,
/// Converts the identifier to SCREAMING_SNAKE_CASE.
ScreamingSnakeCase,
/// Converts the identifier to SCREAMING_SNAKE_CASE and prefixes enum variants
/// with the enum name.
QualifiedScreamingSnakeCase,
}
impl RenameRule {
pub(crate) fn not_none(self) -> Option<Self> {
match self {
RenameRule::None => None,
other => Some(other),
}
}
/// Applies the rename rule to a string
pub fn apply<'a>(self, text: &'a str, context: IdentifierType) -> Cow<'a, str> {
use heck::*;
if text.is_empty() {
return Cow::Borrowed(text);
}
Cow::Owned(match self {
RenameRule::None => return Cow::Borrowed(text),
RenameRule::GeckoCase => context.to_str().to_owned() + &text.to_camel_case(),
RenameRule::LowerCase => text.to_lowercase(),
RenameRule::UpperCase => text.to_uppercase(),
RenameRule::PascalCase => text.to_camel_case(),
RenameRule::CamelCase => text.to_mixed_case(),
RenameRule::SnakeCase => text.to_snake_case(),
RenameRule::ScreamingSnakeCase => text.to_shouty_snake_case(),
RenameRule::QualifiedScreamingSnakeCase => {
let mut result = String::new();
if let IdentifierType::EnumVariant { prefix } = context {
result.push_str(
&RenameRule::ScreamingSnakeCase.apply(prefix, IdentifierType::Enum),
);
result.push('_');
}
result.push_str(&RenameRule::ScreamingSnakeCase.apply(text, context));
result
}
})
}
}
impl Default for RenameRule {
fn default() -> RenameRule {
RenameRule::None
}
}
impl FromStr for RenameRule {
type Err = String;
fn from_str(s: &str) -> Result<RenameRule, Self::Err> {
match s {
"none" => Ok(RenameRule::None),
"None" => Ok(RenameRule::None),
"mGeckoCase" => Ok(RenameRule::GeckoCase),
"GeckoCase" => Ok(RenameRule::GeckoCase),
"gecko_case" => Ok(RenameRule::GeckoCase),
"lowercase" => Ok(RenameRule::LowerCase),
"LowerCase" => Ok(RenameRule::LowerCase),
"lower_case" => Ok(RenameRule::LowerCase),
"UPPERCASE" => Ok(RenameRule::UpperCase),
"UpperCase" => Ok(RenameRule::UpperCase),
"upper_case" => Ok(RenameRule::UpperCase),
"PascalCase" => Ok(RenameRule::PascalCase),
"pascal_case" => Ok(RenameRule::PascalCase),
"camelCase" => Ok(RenameRule::CamelCase),
"CamelCase" => Ok(RenameRule::CamelCase),
"camel_case" => Ok(RenameRule::CamelCase),
"snake_case" => Ok(RenameRule::SnakeCase),
"SnakeCase" => Ok(RenameRule::SnakeCase),
"SCREAMING_SNAKE_CASE" => Ok(RenameRule::ScreamingSnakeCase),
"ScreamingSnakeCase" => Ok(RenameRule::ScreamingSnakeCase),
"screaming_snake_case" => Ok(RenameRule::ScreamingSnakeCase),
"QUALIFIED_SCREAMING_SNAKE_CASE" => Ok(RenameRule::QualifiedScreamingSnakeCase),
"QualifiedScreamingSnakeCase" => Ok(RenameRule::QualifiedScreamingSnakeCase),
"qualified_screaming_snake_case" => Ok(RenameRule::QualifiedScreamingSnakeCase),
_ => Err(format!("Unrecognized RenameRule: '{}'.", s)),
}
}
}
deserialize_enum_str!(RenameRule);

View File

@@ -0,0 +1,91 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Taken from `https://en.cppreference.com/w/cpp/keyword`
/// Some experimental keywords were filtered out and the resulting list was
/// sorted using a rust program.
const RESERVED_KEYWORDS: &[&str] = &[
"alignas",
"alignof",
"auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"char8_t",
"class",
"const",
"const_cast",
"consteval",
"constexpr",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
];
pub fn escape(rust_identifier: &mut String) {
if RESERVED_KEYWORDS
.binary_search(&rust_identifier.as_ref())
.is_ok()
{
rust_identifier.push('_');
}
}

View File

@@ -0,0 +1,293 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(clippy::redundant_closure_call)]
pub trait IterHelpers: Iterator {
fn try_skip_map<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
where
F: FnMut(&Self::Item) -> Result<Option<T>, E>;
}
impl<I> IterHelpers for I
where
I: Iterator,
{
fn try_skip_map<F, T, E>(&mut self, mut f: F) -> Result<Vec<T>, E>
where
F: FnMut(&Self::Item) -> Result<Option<T>, E>,
{
let mut out = Vec::new();
for item in self {
if let Some(x) = f(&item)? {
out.push(x);
}
}
Ok(out)
}
}
pub trait SynItemFnHelpers: SynAttributeHelpers {
fn exported_name(&self) -> Option<String>;
}
impl SynItemFnHelpers for syn::ItemFn {
fn exported_name(&self) -> Option<String> {
self.attrs
.attr_name_value_lookup("export_name")
.or_else(|| {
if self.is_no_mangle() {
Some(self.sig.ident.to_string())
} else {
None
}
})
}
}
impl SynItemFnHelpers for syn::ImplItemMethod {
fn exported_name(&self) -> Option<String> {
self.attrs
.attr_name_value_lookup("export_name")
.or_else(|| {
if self.is_no_mangle() {
Some(self.sig.ident.to_string())
} else {
None
}
})
}
}
/// Returns whether this attribute causes us to skip at item. This basically
/// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and
/// variations thereof.
fn is_skip_item_attr(attr: &syn::Meta) -> bool {
match *attr {
syn::Meta::Path(ref path) => {
// TODO(emilio): It'd be great if rustc allowed us to use a syntax
// like `#[cbindgen::ignore]` or such.
path.is_ident("test")
}
syn::Meta::List(ref list) => {
if !list.path.is_ident("cfg") {
return false;
}
list.nested.iter().any(|nested| match *nested {
syn::NestedMeta::Meta(ref meta) => is_skip_item_attr(meta),
syn::NestedMeta::Lit(..) => false,
})
}
syn::Meta::NameValue(ref name_value) => {
if name_value.path.is_ident("doc") {
if let syn::Lit::Str(ref content) = name_value.lit {
// FIXME(emilio): Maybe should use the general annotation
// mechanism, but it seems overkill for this.
if content.value().trim() == "cbindgen:ignore" {
return true;
}
}
}
false
}
}
}
pub trait SynAttributeHelpers {
/// Returns the list of attributes for an item.
fn attrs(&self) -> &[syn::Attribute];
/// Searches for attributes like `#[test]`.
/// Example:
/// - `item.has_attr_word("test")` => `#[test]`
fn has_attr_word(&self, name: &str) -> bool {
self.attrs()
.iter()
.filter_map(|x| x.parse_meta().ok())
.any(|attr| {
if let syn::Meta::Path(ref path) = attr {
path.is_ident(name)
} else {
false
}
})
}
fn is_no_mangle(&self) -> bool {
self.has_attr_word("no_mangle")
}
/// Sees whether we should skip parsing a given item.
fn should_skip_parsing(&self) -> bool {
for attr in self.attrs() {
let meta = match attr.parse_meta() {
Ok(attr) => attr,
Err(..) => return false,
};
if is_skip_item_attr(&meta) {
return true;
}
}
false
}
fn attr_name_value_lookup(&self, name: &str) -> Option<String> {
self.attrs()
.iter()
.filter_map(|attr| {
let attr = attr.parse_meta().ok()?;
if let syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(lit),
..
}) = attr
{
if path.is_ident(name) {
return Some(lit.value());
}
}
None
})
.next()
}
fn get_comment_lines(&self) -> Vec<String> {
let mut comment = Vec::new();
for attr in self.attrs() {
if attr.style == syn::AttrStyle::Outer {
if let Ok(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(content),
..
})) = attr.parse_meta()
{
if path.is_ident("doc") {
comment.extend(split_doc_attr(&content.value()));
}
}
}
}
comment
}
}
macro_rules! syn_item_match_helper {
($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => {
match *$s {
syn::Item::Const(ref $i) => $a,
syn::Item::Enum(ref $i) => $a,
syn::Item::ExternCrate(ref $i) => $a,
syn::Item::Fn(ref $i) => $a,
syn::Item::ForeignMod(ref $i) => $a,
syn::Item::Impl(ref $i) => $a,
syn::Item::Macro(ref $i) => $a,
syn::Item::Macro2(ref $i) => $a,
syn::Item::Mod(ref $i) => $a,
syn::Item::Static(ref $i) => $a,
syn::Item::Struct(ref $i) => $a,
syn::Item::Trait(ref $i) => $a,
syn::Item::Type(ref $i) => $a,
syn::Item::Union(ref $i) => $a,
syn::Item::Use(ref $i) => $a,
syn::Item::TraitAlias(ref $i) => $a,
syn::Item::Verbatim(_) => $b,
_ => panic!("Unhandled syn::Item: {:?}", $s),
}
};
}
impl SynAttributeHelpers for syn::Item {
fn attrs(&self) -> &[syn::Attribute] {
syn_item_match_helper!(self =>
has_attrs: |item| { &item.attrs },
otherwise: || { &[] }
)
}
}
macro_rules! impl_syn_item_helper {
($t:ty) => {
impl SynAttributeHelpers for $t {
fn attrs(&self) -> &[syn::Attribute] {
&self.attrs
}
}
};
}
impl_syn_item_helper!(syn::ItemExternCrate);
impl_syn_item_helper!(syn::ItemUse);
impl_syn_item_helper!(syn::ItemStatic);
impl_syn_item_helper!(syn::ItemConst);
impl_syn_item_helper!(syn::ItemFn);
impl_syn_item_helper!(syn::ImplItemMethod);
impl_syn_item_helper!(syn::ItemMod);
impl_syn_item_helper!(syn::ItemForeignMod);
impl_syn_item_helper!(syn::ItemType);
impl_syn_item_helper!(syn::ItemStruct);
impl_syn_item_helper!(syn::ItemEnum);
impl_syn_item_helper!(syn::ItemUnion);
impl_syn_item_helper!(syn::ItemTrait);
impl_syn_item_helper!(syn::ItemImpl);
impl_syn_item_helper!(syn::ItemMacro);
impl_syn_item_helper!(syn::ItemMacro2);
impl_syn_item_helper!(syn::ItemTraitAlias);
/// Helper function for accessing Abi information
pub trait SynAbiHelpers {
fn is_c(&self) -> bool;
fn is_omitted(&self) -> bool;
}
impl SynAbiHelpers for Option<syn::Abi> {
fn is_c(&self) -> bool {
if let Some(ref abi) = *self {
if let Some(ref lit_string) = abi.name {
return lit_string.value() == "C";
}
}
false
}
fn is_omitted(&self) -> bool {
if let Some(ref abi) = *self {
abi.name.is_none()
} else {
false
}
}
}
impl SynAbiHelpers for syn::Abi {
fn is_c(&self) -> bool {
if let Some(ref lit_string) = self.name {
lit_string.value() == "C"
} else {
false
}
}
fn is_omitted(&self) -> bool {
self.name.is_none()
}
}
impl SynAttributeHelpers for [syn::Attribute] {
fn attrs(&self) -> &[syn::Attribute] {
self
}
}
fn split_doc_attr(input: &str) -> Vec<String> {
input
// Convert two newline (indicate "new paragraph") into two line break.
.replace("\n\n", " \n \n")
// Convert newline after two spaces (indicate "line break") into line break.
.split(" \n")
// Convert single newline (indicate hard-wrapped) into space.
.map(|s| s.replace('\n', " "))
.map(|s| s.trim_end().to_string())
.collect()
}

View File

@@ -0,0 +1,258 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cmp;
use std::io;
use std::io::Write;
use crate::bindgen::config::{Braces, Config, Language};
use crate::bindgen::Bindings;
/// A type of way to format a list.
pub enum ListType<'a> {
/// Join each adjacent item with a str.
Join(&'a str),
/// End each item with a str.
Cap(&'a str),
}
/// An empty file used for creating a null source writer and measuring line
/// metrics for various code layouts.
pub struct NullFile;
impl Write for NullFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
/// A utility wrapper to write unbuffered data and correctly adjust positions.
struct InnerWriter<'a, 'b: 'a, F: 'a + Write>(&'a mut SourceWriter<'b, F>);
impl<'a, 'b, F: Write> Write for InnerWriter<'a, 'b, F> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let writer = &mut self.0;
if !writer.line_started {
for _ in 0..writer.spaces() {
write!(writer.out, " ").unwrap();
}
writer.line_started = true;
writer.line_length += writer.spaces();
}
let written = writer.out.write(buf)?;
writer.line_length += written;
writer.max_line_length = cmp::max(writer.max_line_length, writer.line_length);
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
self.0.out.flush()
}
}
/// A utility writer for generating code easier.
pub struct SourceWriter<'a, F: Write> {
out: F,
bindings: &'a Bindings,
spaces: Vec<usize>,
line_started: bool,
line_length: usize,
line_number: usize,
max_line_length: usize,
}
pub type MeasureWriter<'a> = SourceWriter<'a, NullFile>;
impl<'a, F: Write> SourceWriter<'a, F> {
pub fn new(out: F, bindings: &'a Bindings) -> Self {
SourceWriter {
out,
bindings,
spaces: vec![0],
line_started: false,
line_length: 0,
line_number: 1,
max_line_length: 0,
}
}
pub fn bindings(&self) -> &Bindings {
self.bindings
}
/// Takes a function that writes source and returns the maximum line length
/// written.
pub fn measure<T>(&self, func: T) -> usize
where
T: Fn(&mut MeasureWriter),
{
let mut measurer = SourceWriter {
out: NullFile,
bindings: self.bindings,
spaces: self.spaces.clone(),
line_started: self.line_started,
line_length: self.line_length,
line_number: self.line_number,
max_line_length: self.line_length,
};
func(&mut measurer);
measurer.max_line_length
}
fn spaces(&self) -> usize {
*self.spaces.last().unwrap()
}
pub fn push_set_spaces(&mut self, spaces: usize) {
self.spaces.push(spaces);
}
pub fn pop_set_spaces(&mut self) {
self.pop_tab()
}
pub fn line_length_for_align(&self) -> usize {
if self.line_started {
self.line_length
} else {
self.line_length + self.spaces()
}
}
pub fn push_tab(&mut self) {
let spaces = self.spaces() - (self.spaces() % self.bindings.config.tab_width)
+ self.bindings.config.tab_width;
self.spaces.push(spaces);
}
pub fn pop_tab(&mut self) {
assert!(!self.spaces.is_empty());
self.spaces.pop();
}
pub fn new_line(&mut self) {
self.out
.write_all(self.bindings.config.line_endings.as_str().as_bytes())
.unwrap();
self.line_started = false;
self.line_length = 0;
self.line_number += 1;
}
pub fn new_line_if_not_start(&mut self) {
if self.line_number != 1 {
self.new_line();
}
}
pub fn open_brace(&mut self) {
match self.bindings.config.language {
Language::Cxx | Language::C => match self.bindings.config.braces {
Braces::SameLine => {
self.write(" {");
self.push_tab();
self.new_line();
}
Braces::NextLine => {
self.new_line();
self.write("{");
self.push_tab();
self.new_line();
}
},
Language::Cython => {
self.write(":");
self.new_line();
self.push_tab();
}
}
}
pub fn close_brace(&mut self, semicolon: bool) {
self.pop_tab();
match self.bindings.config.language {
Language::Cxx | Language::C => {
self.new_line();
if semicolon {
self.write("};");
} else {
self.write("}");
}
}
Language::Cython => {}
}
}
pub fn write(&mut self, text: &'static str) {
write!(self, "{}", text);
}
pub fn write_raw_block(&mut self, block: &str) {
self.line_started = true;
write!(self, "{}", block);
}
pub fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) {
InnerWriter(self).write_fmt(fmt).unwrap();
}
pub fn write_horizontal_source_list<'b, S: Source>(
&mut self,
items: &[S],
list_type: ListType<'b>,
) {
for (i, item) in items.iter().enumerate() {
item.write(&self.bindings.config, self);
match list_type {
ListType::Join(text) => {
if i != items.len() - 1 {
write!(self, "{}", text);
}
}
ListType::Cap(text) => {
write!(self, "{}", text);
}
}
}
}
pub fn write_vertical_source_list<'b, S: Source>(
&mut self,
items: &[S],
list_type: ListType<'b>,
) {
let align_length = self.line_length_for_align();
self.push_set_spaces(align_length);
for (i, item) in items.iter().enumerate() {
item.write(&self.bindings.config, self);
match list_type {
ListType::Join(text) => {
if i != items.len() - 1 {
write!(self, "{}", text);
}
}
ListType::Cap(text) => {
write!(self, "{}", text);
}
}
if i != items.len() - 1 {
self.new_line();
}
}
self.pop_tab();
}
}
pub trait Source {
fn write<F: Write>(&self, config: &Config, _: &mut SourceWriter<F>);
}

41
zeroidc/vendor/cbindgen/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,41 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;
mod bindgen;
pub use crate::bindgen::*;
use std::path::Path;
/// A utility function for build scripts to generate bindings for a crate, using
/// a `cbindgen.toml` if it exists.
pub fn generate<P: AsRef<Path>>(crate_dir: P) -> Result<Bindings, Error> {
let config = Config::from_root_or_default(crate_dir.as_ref());
generate_with_config(crate_dir, config)
}
/// A utility function for build scripts to generate bindings for a crate with a
/// custom config.
pub fn generate_with_config<P: AsRef<Path>>(
crate_dir: P,
config: Config,
) -> Result<Bindings, Error> {
Builder::new()
.with_config(config)
.with_crate(crate_dir)
.generate()
}

105
zeroidc/vendor/cbindgen/src/logging.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::io;
use std::io::Write;
use log::*;
pub struct TraceLogger;
pub struct WarnLogger;
pub struct InfoLogger;
pub struct ErrorLogger;
impl TraceLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Trace);
Ok(())
}
}
impl log::Log for TraceLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl WarnLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Warn);
Ok(())
}
}
impl log::Log for WarnLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Warn
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl ErrorLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Error);
Ok(())
}
}
impl log::Log for ErrorLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Error
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}
impl InfoLogger {
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&InfoLogger)?;
log::set_max_level(LevelFilter::Info);
Ok(())
}
}
impl log::Log for InfoLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
eprintln!("{}: {}", record.level(), record.args());
}
}
fn flush(&self) {
io::stderr().flush().unwrap();
}
}

314
zeroidc/vendor/cbindgen/src/main.rs vendored Normal file
View File

@@ -0,0 +1,314 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::env;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
extern crate clap;
#[macro_use]
extern crate log;
extern crate proc_macro2;
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate toml;
use clap::{App, Arg, ArgMatches};
mod bindgen;
mod logging;
use crate::bindgen::{Bindings, Builder, Cargo, Config, Error, Profile, Style};
fn apply_config_overrides<'a>(config: &mut Config, matches: &ArgMatches<'a>) {
// We allow specifying a language to override the config default. This is
// used by compile-tests.
if let Some(lang) = matches.value_of("lang") {
config.language = match lang.parse() {
Ok(lang) => lang,
Err(reason) => {
error!("{}", reason);
return;
}
}
}
if matches.is_present("cpp-compat") {
config.cpp_compat = true;
}
if matches.is_present("only-target-dependencies") {
config.only_target_dependencies = true;
}
if let Some(style) = matches.value_of("style") {
config.style = match style {
"Both" => Style::Both,
"both" => Style::Both,
"Tag" => Style::Tag,
"tag" => Style::Tag,
"Type" => Style::Type,
"type" => Style::Type,
_ => {
error!("Unknown style specified.");
return;
}
}
}
if let Some(profile) = matches.value_of("profile") {
config.parse.expand.profile = match Profile::from_str(profile) {
Ok(p) => p,
Err(e) => {
error!("{}", e);
return;
}
}
}
if matches.is_present("d") {
config.parse.parse_deps = true;
}
}
fn load_bindings<'a>(input: &Path, matches: &ArgMatches<'a>) -> Result<Bindings, Error> {
// If a file is specified then we load it as a single source
if !input.is_dir() {
// Load any config specified or search in the input directory
let mut config = match matches.value_of("config") {
Some(c) => Config::from_file(c).unwrap(),
None => Config::from_root_or_default(input),
};
apply_config_overrides(&mut config, matches);
return Builder::new()
.with_config(config)
.with_src(input)
.generate();
}
// We have to load a whole crate, so we use cargo to gather metadata
let lib = Cargo::load(
input,
matches.value_of("lockfile"),
matches.value_of("crate"),
true,
matches.is_present("clean"),
matches.is_present("only-target-dependencies"),
matches.value_of("metadata").map(Path::new),
)?;
// Load any config specified or search in the binding crate directory
let mut config = match matches.value_of("config") {
Some(c) => Config::from_file(c).unwrap(),
None => {
let binding_crate_dir = lib.find_crate_dir(&lib.binding_crate_ref());
if let Some(binding_crate_dir) = binding_crate_dir {
Config::from_root_or_default(&binding_crate_dir)
} else {
// This shouldn't happen
Config::from_root_or_default(input)
}
}
};
apply_config_overrides(&mut config, matches);
Builder::new()
.with_config(config)
.with_cargo(lib)
.generate()
}
fn main() {
let matches = App::new("cbindgen")
.version(bindgen::VERSION)
.about("Generate C bindings for a Rust library")
.arg(
Arg::with_name("v")
.short("v")
.multiple(true)
.help("Enable verbose logging"),
)
.arg(
Arg::with_name("verify")
.long("verify")
.help("Generate bindings and compare it to the existing bindings file and error if they are different"),
)
.arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("PATH")
.help("Specify path to a `cbindgen.toml` config to use"),
)
.arg(
Arg::with_name("lang")
.short("l")
.long("lang")
.value_name("LANGUAGE")
.help("Specify the language to output bindings in")
.possible_values(&["c++", "C++", "c", "C", "cython", "Cython"]),
)
.arg(
Arg::with_name("cpp-compat")
.long("cpp-compat")
.help("Whether to add C++ compatibility to generated C bindings")
)
.arg(
Arg::with_name("only-target-dependencies")
.long("only-target-dependencies")
.help("Only fetch dependencies needed by the target platform. \
The target platform defaults to the host platform; set TARGET to override.")
)
.arg(
Arg::with_name("style")
.short("s")
.long("style")
.value_name("STYLE")
.help("Specify the declaration style to use for bindings")
.possible_values(&["Both", "both", "Tag", "tag", "Type", "type"]),
)
.arg(
Arg::with_name("d")
.short("d")
.long("parse-dependencies")
.help("Whether to parse dependencies when generating bindings"),
)
.arg(
Arg::with_name("clean")
.long("clean")
.help(
"Whether to use a new temporary directory for expanding macros. \
Affects performance, but might be required in certain build processes.")
.required(false)
)
.arg(
Arg::with_name("INPUT")
.help(
"A crate directory or source file to generate bindings for. \
In general this is the folder where the Cargo.toml file of \
source Rust library resides.")
.required(false)
.index(1),
)
.arg(
Arg::with_name("crate")
.long("crate")
.value_name("CRATE_NAME")
.help(
"If generating bindings for a crate, \
the specific crate to generate bindings for",
)
.required(false),
)
.arg(
Arg::with_name("out")
.short("o")
.long("output")
.value_name("PATH")
.help("The file to output the bindings to")
.required(false),
)
.arg(
Arg::with_name("lockfile")
.long("lockfile")
.value_name("PATH")
.help(
"Specify the path to the Cargo.lock file explicitly. If this \
is not specified, the Cargo.lock file is searched for in the \
same folder as the Cargo.toml file. This option is useful for \
projects that use workspaces.")
.required(false),
)
.arg(
Arg::with_name("metadata")
.long("metadata")
.value_name("PATH")
.help(
"Specify the path to the output of a `cargo metadata` \
command that allows to get dependency information. \
This is useful because cargo metadata may be the longest \
part of cbindgen runtime, and you may want to share it \
across cbindgen invocations. By default cbindgen will run \
`cargo metadata --all-features --format-version 1 \
--manifest-path <path/to/crate/Cargo.toml>"
)
.required(false),
)
.arg(
Arg::with_name("profile")
.long("profile")
.value_name("PROFILE")
.help(
"Specify the profile to use when expanding macros. \
Has no effect otherwise."
)
.possible_values(&["Debug", "debug", "Release", "release"]),
)
.arg(
Arg::with_name("quiet")
.short("q")
.long("quiet")
.help("Report errors only (overrides verbosity options).")
.required(false),
)
.get_matches();
if !matches.is_present("out") && matches.is_present("verify") {
error!(
"Cannot verify bindings against `stdout`, please specify a file to compare against."
);
std::process::exit(2);
}
// Initialize logging
if matches.is_present("quiet") {
logging::ErrorLogger::init().unwrap();
} else {
match matches.occurrences_of("v") {
0 => logging::WarnLogger::init().unwrap(),
1 => logging::InfoLogger::init().unwrap(),
_ => logging::TraceLogger::init().unwrap(),
}
}
// Find the input directory
let input = match matches.value_of("INPUT") {
Some(input) => PathBuf::from(input),
None => env::current_dir().unwrap(),
};
let bindings = match load_bindings(&input, &matches) {
Ok(bindings) => bindings,
Err(msg) => {
error!("{}", msg);
error!("Couldn't generate bindings for {}.", input.display());
std::process::exit(1);
}
};
// Write the bindings file
match matches.value_of("out") {
Some(file) => {
let changed = bindings.write_to_file(file);
if matches.is_present("verify") && changed {
error!("Bindings changed: {}", file);
std::process::exit(2);
}
}
_ => {
bindings.write(io::stdout());
}
}
}