Files
cbindgen/src/main.rs
T
2017-04-05 11:08:46 -04:00

254 lines
8.5 KiB
Rust

use std::env;
use std::fmt;
use std::io;
use std::io::Write;
extern crate syn;
use syn::*;
mod rust_lib;
#[derive(Debug)]
struct ConvertedType {
/// The type converted to C (e.g. `uint32_t`)
prefix: String,
/// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
postfix: String,
}
impl ConvertedType {
fn new<T: Into<String>>(pre: T, post: String) -> Self {
ConvertedType {
prefix: pre.into(),
postfix: post
}
}
}
impl Into<String> for ConvertedType {
fn into(self) -> String {
let mut str = String::from(self.prefix);
str.push_str(&self.postfix);
str
}
}
impl From<String> for ConvertedType {
fn from(str: String) -> ConvertedType {
ConvertedType {
prefix: str,
postfix: String::new()
}
}
}
impl fmt::Display for ConvertedType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", &self.prefix, &self.postfix)
}
}
fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
return attrs
.iter()
.any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}
fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}
fn wr_func_body(attrs: &Vec<Attribute>) -> String {
if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
String::from("WR_DESTRUCTOR_SAFE_FUNC")
} else {
String::from("WR_FUNC")
}
}
fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}
fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}
fn is_c_abi(abi: &Option<Abi>) -> bool {
abi == &Some(Abi::Named(String::from("C")))
}
fn map_path(p: &Path) -> String {
let l = p.segments[0].ident.to_string();
let mut c = match l.as_ref() {
"usize" => "size_t".to_string(),
"u8" => "uint8_t".to_string(),
"u32" => "uint32_t".to_string(),
"u64" => "uint64_t".to_string(),
"i8" => "int8_t".to_string(),
"i32" => "int32_t".to_string(),
"i64" => "int64_t".to_string(),
"f32" => "float".to_string(),
"c_void" => "void".to_string(),
_ => l,
};
if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
let template_args = d.types
.iter()
.map(|ty| map_ty(ty).into())
.collect::<Vec<String>>()
.join(", ");
if !template_args.is_empty() {
c.push('<');
c.push_str(&template_args);
c.push('>');
}
}
c
}
fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
map_ty(&mut_ty.ty)
}
fn map_ty(ty: &Ty) -> ConvertedType {
match ty {
&Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
&Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
&Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
&Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
_ => ConvertedType::from(format!("unknown {:?}", ty)),
}
}
fn map_return_type(ret: &FunctionRetTy) -> String {
match ret {
&FunctionRetTy::Default => "void".to_string(),
&FunctionRetTy::Ty(ref ty) => map_ty(ty).into(),
}
}
fn map_pat(pat: &Pat) -> String {
match pat {
&Pat::Ident(_, ref ident, _) => ident.to_string(),
_ => format!("unknown {:?}", pat),
}
}
fn map_arg(f: &FnArg) -> String {
match f {
&FnArg::Captured(ref pat, ref ty) => format!("{} {}", map_ty(ty), map_pat(pat)),
_ => "unknown".to_string(),
}
}
fn map_field(f: &Field) -> String {
let mut ret = String::from(" ");
let converted = map_ty(&f.ty);
ret.push_str(&converted.prefix);
ret.push(' ');
ret.push_str(&f.ident.as_ref().expect("Struct fields must have idents").to_string());
ret.push_str(&converted.postfix);
ret.push_str(";\n");
ret
}
fn map_generic_param(t: &TyParam) -> String {
let mut ret = String::from("typename ");
ret.push_str(&t.ident.to_string());
ret
}
fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
// `accum` contains the combined string of converted enum variants so far, to which
// we will append the converted version of `v`. The other thing in `accum` is the
// value of the previous variant. If `v` has an explicit value we keep that, otherwise
// we increment the value of the previous variant to get the new one. This is all
// so that we properly support enums with explicitly-specified and discontinuous
// values.
let mut ret = accum.0;
ret.push_str(" ");
ret.push_str(&v.ident.to_string());
ret.push_str(" = ");
let new_value = match &v.discriminant {
&None => accum.1 + 1,
&Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
&Some(_) => {
// we don't handle this yet, so just put in something that will fail C compilation
writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
ret.push_str("???");
accum.1 + 1
}
};
ret.push_str(&new_value.to_string());
ret.push_str(",\n");
(ret, new_value)
}
fn main() {
let p = env::args().nth(1).unwrap();
rust_lib::parse(p, &|mod_name, items| {
for item in items {
match item.node {
ItemKind::Fn(ref decl,
ref _unsafe,
ref _const,
ref abi,
ref _generic,
ref _block) => {
writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
println!("WR_INLINE {}\n{}({})\n{};\n",
map_return_type(&decl.output),
item.ident,
decl.inputs
.iter()
.map(map_arg)
.collect::<Vec<_>>()
.join(", "),
wr_func_body(&item.attrs));
}
}
ItemKind::Struct(ref variant,
ref generics) => {
writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
if is_repr_c(&item.attrs) {
if !generics.ty_params.is_empty() {
println!("template<{}>",
generics.ty_params
.iter()
.map(map_generic_param)
.collect::<Vec<_>>()
.join(", "));
}
if let &VariantData::Struct(ref fields) = variant {
println!("struct {} {{\n{}}};\n",
item.ident,
fields
.iter()
.map(map_field)
.collect::<String>());
}
}
}
ItemKind::Enum(ref variants, ref _generics) => {
writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
if is_repr_u32(&item.attrs) {
println!("enum class {}: uint32_t {{\n{}\n Sentinel /* this must be last for serialization purposes. */\n}};\n",
item.ident,
variants
.iter()
.fold((String::new(), -1), fold_enum_variants)
.0);
}
}
_ => {}
}
}
});
}