blob: 7cf298e577387b0cf1b641b860f82f76d9bc171e [file] [log] [blame]
use std::cell::RefCell;
use std::vec::Vec;
use std::rc::Rc;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use syntax::abi;
use syntax::ast;
use syntax::codemap::{Span, Spanned, respan, ExpnInfo, NameAndSpan, MacroBang};
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::quote::rt::ToTokens;
use syntax::feature_gate::Features;
use syntax::owned_slice::OwnedSlice;
use syntax::parse;
use syntax::parse::token::InternedString;
use syntax::attr::mk_attr_id;
use syntax::ptr::P;
use syntax::print::pprust::tts_to_string;
use super::LinkType;
use types::*;
struct GenCtx<'r> {
ext_cx: base::ExtCtxt<'r>,
unnamed_ty: usize,
span: Span
}
fn first<A, B>((val, _): (A, B)) -> A {
val
}
fn ref_eq<T>(thing: &T, other: &T) -> bool {
(thing as *const T) == (other as *const T)
}
fn to_intern_str(ctx: &mut GenCtx, s: String) -> parse::token::InternedString {
let id = ctx.ext_cx.ident_of(&s[..]);
id.name.as_str()
}
fn empty_generics() -> ast::Generics {
ast::Generics {
lifetimes: Vec::new(),
ty_params: OwnedSlice::empty(),
where_clause: ast::WhereClause {
id: ast::DUMMY_NODE_ID,
predicates: Vec::new()
}
}
}
fn rust_id(ctx: &mut GenCtx, name: String) -> (String, bool) {
let token = parse::token::Ident(ctx.ext_cx.ident_of(&name[..]), parse::token::Plain);
if token.is_any_keyword() || "bool" == &name[..] {
let mut s = "_".to_owned();
s.push_str(&name[..]);
(s, true)
} else {
(name, false)
}
}
fn rust_type_id(ctx: &mut GenCtx, name: String) -> String {
if "bool" == &name[..] ||
"uint" == &name[..] ||
"u8" == &name[..] ||
"u16" == &name[..] ||
"u32" == &name[..] ||
"f32" == &name[..] ||
"f64" == &name[..] ||
"i8" == &name[..] ||
"i16" == &name[..] ||
"i32" == &name[..] ||
"i64" == &name[..] ||
"Self" == &name[..] ||
"str" == &name[..] {
let mut s = "_".to_owned();
s.push_str(&name[..]);
s
} else {
let (n, _) = rust_id(ctx, name);
n
}
}
fn unnamed_name(ctx: &mut GenCtx, name: String) -> String {
if name.is_empty() {
ctx.unnamed_ty += 1;
format!("Unnamed{}", ctx.unnamed_ty)
} else {
name
}
}
fn comp_name(kind: CompKind, name: &str) -> String {
match kind {
CompKind::Struct => struct_name(name),
CompKind::Union => union_name(name),
}
}
fn struct_name(name: &str) -> String {
format!("Struct_{}", name)
}
fn union_name(name: &str) -> String {
format!("Union_{}", name)
}
fn enum_name(name: &str) -> String {
format!("Enum_{}", name)
}
pub fn gen_mod(links: &[(String, LinkType)], globs: Vec<Global>, span: Span) -> Vec<P<ast::Item>> {
// Create a dummy ExtCtxt. We only need this for string interning and that uses TLS.
let mut features = Features::new();
features.allow_quote = true;
let cfg = ExpansionConfig {
crate_name: "xxx".to_owned(),
features: Some(&features),
recursion_limit: 64,
trace_mac: false,
};
let sess = &parse::ParseSess::new();
let mut feature_gated_cfgs = Vec::new();
let mut ctx = GenCtx {
ext_cx: base::ExtCtxt::new(
sess,
Vec::new(),
cfg,
&mut feature_gated_cfgs,
),
unnamed_ty: 0,
span: span
};
ctx.ext_cx.bt_push(ExpnInfo {
call_site: ctx.span,
callee: NameAndSpan {
format: MacroBang(parse::token::intern("")),
allow_internal_unstable: false,
span: None
}
});
let uniq_globs = tag_dup_decl(globs);
let mut fs = vec!();
let mut vs = vec!();
let mut gs = vec!();
for g in uniq_globs.into_iter() {
match g {
GOther => {}
GFunc(_) => fs.push(g),
GVar(_) => {
let is_int_const = {
match g {
GVar(ref vi) => {
let v = vi.borrow();
v.is_const && v.val.is_some()
}
_ => unreachable!()
}
};
if is_int_const {
gs.push(g);
} else {
vs.push(g);
}
}
_ => gs.push(g)
}
}
let mut defs = vec!();
gs = remove_redundant_decl(gs);
for g in gs.into_iter() {
match g {
GType(ti) => {
let t = ti.borrow().clone();
defs.extend(ctypedef_to_rs(&mut ctx, t.name.clone(), &t.ty).into_iter())
},
GCompDecl(ci) => {
{
let mut c = ci.borrow_mut();
c.name = unnamed_name(&mut ctx, c.name.clone());
}
let c = ci.borrow().clone();
defs.push(opaque_to_rs(&mut ctx, comp_name(c.kind, &c.name)));
},
GComp(ci) => {
{
let mut c = ci.borrow_mut();
c.name = unnamed_name(&mut ctx, c.name.clone());
}
let c = ci.borrow().clone();
defs.extend(comp_to_rs(&mut ctx, c.kind, comp_name(c.kind, &c.name),
c.layout, c.members).into_iter())
},
GEnumDecl(ei) => {
{
let mut e = ei.borrow_mut();
e.name = unnamed_name(&mut ctx, e.name.clone());
}
let e = ei.borrow().clone();
defs.push(opaque_to_rs(&mut ctx, enum_name(&e.name)));
},
GEnum(ei) => {
{
let mut e = ei.borrow_mut();
e.name = unnamed_name(&mut ctx, e.name.clone());
}
let e = ei.borrow();
defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, &e.items).into_iter())
},
GVar(vi) => {
let v = vi.borrow();
let ty = cty_to_rs(&mut ctx, &v.ty);
defs.push(const_to_rs(&mut ctx, v.name.clone(), v.val.unwrap(), ty));
},
_ => { }
}
}
let vars = vs.into_iter().map(|v| {
match v {
GVar(vi) => {
let v = vi.borrow();
cvar_to_rs(&mut ctx, v.name.clone(), &v.ty, v.is_const)
},
_ => unreachable!()
}
}).collect();
let funcs = {
let func_list = fs.into_iter().map(|f| {
match f {
GFunc(vi) => {
let v = vi.borrow();
match v.ty {
TFuncPtr(ref sig) => {
let decl = cfunc_to_rs(&mut ctx, v.name.clone(),
&*sig.ret_ty, &sig.args[..],
sig.is_variadic);
(sig.abi, decl)
}
_ => unreachable!()
}
},
_ => unreachable!()
}
});
let mut map: HashMap<abi::Abi, Vec<_>> = HashMap::new();
for (abi, func) in func_list {
match map.entry(abi) {
Entry::Occupied(mut occ) => {
occ.get_mut().push(func);
}
Entry::Vacant(vac) => {
vac.insert(vec!(func));
}
}
}
map
};
if !Vec::is_empty(&vars) {
defs.push(mk_extern(&mut ctx, links, vars, abi::C));
}
for (abi, funcs) in funcs.into_iter() {
defs.push(mk_extern(&mut ctx, links, funcs, abi));
}
//let attrs = vec!(mk_attr_list(&mut ctx, "allow", ["dead_code", "non_camel_case_types", "uppercase_variables"]));
defs
}
fn mk_extern(ctx: &mut GenCtx, links: &[(String, LinkType)],
foreign_items: Vec<P<ast::ForeignItem>>,
abi: abi::Abi) -> P<ast::Item> {
let attrs = if links.is_empty() {
Vec::new()
} else {
links.iter().map(|&(ref l, ref k)| {
let k = match *k {
LinkType::Default => None,
LinkType::Static => Some("static"),
LinkType::Framework => Some("framework")
};
let link_name = P(respan(ctx.span, ast::MetaNameValue(
to_intern_str(ctx, "name".to_owned()),
respan(ctx.span, ast::LitStr(
to_intern_str(ctx, l.to_owned()),
ast::CookedStr
))
)));
let link_args = match k {
None => vec!(link_name),
Some(ref k) => vec!(link_name, P(respan(ctx.span, ast::MetaNameValue(
to_intern_str(ctx, "kind".to_owned()),
respan(ctx.span, ast::LitStr(
to_intern_str(ctx, (*k).to_owned()),
ast::CookedStr
))
))))
};
respan(ctx.span, ast::Attribute_ {
id: mk_attr_id(),
style: ast::AttrStyle::Outer,
value: P(respan(ctx.span, ast::MetaList(
to_intern_str(ctx, "link".to_owned()),
link_args)
)),
is_sugared_doc: false
})
}).collect()
};
let mut items = Vec::new();
items.extend(foreign_items.into_iter());
let ext = ast::ItemForeignMod(ast::ForeignMod {
abi: abi,
items: items
});
P(ast::Item {
ident: ctx.ext_cx.ident_of(""),
attrs: attrs,
id: ast::DUMMY_NODE_ID,
node: ext,
vis: ast::Inherited,
span: ctx.span
})
}
fn remove_redundant_decl(gs: Vec<Global>) -> Vec<Global> {
fn check_decl(a: &Global, ty: &Type) -> bool {
match *a {
GComp(ref ci1) => match *ty {
TComp(ref ci2) => {
ref_eq(ci1, ci2) && ci1.borrow().name.is_empty()
},
_ => false
},
GEnum(ref ei1) => match *ty {
TEnum(ref ei2) => {
ref_eq(ei1, ei2) && ei1.borrow().name.is_empty()
},
_ => false
},
_ => false
}
}
let typedefs: Vec<Type> = gs.iter().filter_map(|g|
match *g {
GType(ref ti) => Some(ti.borrow().ty.clone()),
_ => None
}
).collect();
gs.into_iter().filter(|g|
!typedefs.iter().any(|t| check_decl(g, t))
).collect()
}
fn tag_dup_decl(gs: Vec<Global>) -> Vec<Global> {
fn check(name1: &str, name2: &str) -> bool {
!name1.is_empty() && name1 == name2
}
fn check_dup(g1: &Global, g2: &Global) -> bool {
match (g1, g2) {
(&GType(ref ti1), &GType(ref ti2)) => {
let a = ti1.borrow();
let b = ti2.borrow();
check(&a.name[..], &b.name[..])
},
(&GComp(ref ci1), &GComp(ref ci2)) => {
let a = ci1.borrow();
let b = ci2.borrow();
check(&a.name[..], &b.name[..])
},
(&GCompDecl(ref ci1), &GCompDecl(ref ci2)) => {
let a = ci1.borrow();
let b = ci2.borrow();
check(&a.name[..], &b.name[..])
},
(&GEnum(ref ei1), &GEnum(ref ei2)) => {
let a = ei1.borrow();
let b = ei2.borrow();
check(&a.name[..], &b.name[..])
},
(&GEnumDecl(ref ei1), &GEnumDecl(ref ei2)) => {
let a = ei1.borrow();
let b = ei2.borrow();
check(&a.name[..], &b.name[..])
},
(&GVar(ref vi1), &GVar(ref vi2)) => {
let a = vi1.borrow();
let b = vi2.borrow();
check(&a.name[..], &b.name[..])
},
(&GFunc(ref vi1), &GFunc(ref vi2)) => {
let a = vi1.borrow();
let b = vi2.borrow();
check(&a.name[..], &b.name[..])
},
_ => false
}
}
if gs.is_empty() {
return gs;
}
let mut res: Vec<Global> = vec!();
res.push(gs[0].clone());
for (i, gsi) in gs.iter().enumerate().skip(1) {
let mut dup = false;
for gsj in gs.iter().take(i) {
if check_dup(&gsi, &gsj) {
dup = true;
break;
}
}
if !dup {
res.push(gsi.clone());
}
}
res
}
fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item>> {
fn mk_item(ctx: &mut GenCtx, name: String, ty: &Type) -> P<ast::Item> {
let rust_name = rust_type_id(ctx, name);
let rust_ty = cty_to_rs(ctx, ty);
let base = ast::ItemTy(
P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: rust_ty.node,
span: ctx.span,
}),
empty_generics()
);
P(ast::Item {
ident: ctx.ext_cx.ident_of(&rust_name[..]),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: base,
vis: ast::Public,
span: ctx.span
})
}
match *ty {
TComp(ref ci) => {
let is_empty = ci.borrow().name.is_empty();
if is_empty {
ci.borrow_mut().name = name.clone();
let c = ci.borrow().clone();
comp_to_rs(ctx, c.kind, name, c.layout, c.members)
} else {
vec!(mk_item(ctx, name, ty))
}
},
TEnum(ref ei) => {
let is_empty = ei.borrow().name.is_empty();
if is_empty {
ei.borrow_mut().name = name.clone();
let e = ei.borrow();
cenum_to_rs(ctx, name, e.kind, &e.items)
} else {
vec!(mk_item(ctx, name, ty))
}
},
_ => vec!(mk_item(ctx, name, ty))
}
}
fn comp_to_rs(ctx: &mut GenCtx, kind: CompKind, name: String,
layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
match kind {
CompKind::Struct => cstruct_to_rs(ctx, name, layout, members),
CompKind::Union => cunion_to_rs(ctx, name, layout, members),
}
}
fn cstruct_to_rs(ctx: &mut GenCtx, name: String,
layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
let mut fields = vec!();
let mut methods = vec!();
// Nested composites may need to emit declarations and implementations as
// they are encountered. The declarations end up in 'extra' and are emitted
// after the current struct.
let mut extra = vec!();
let mut unnamed: u32 = 0;
let mut bitfields: u32 = 0;
for m in &members {
let (opt_rc_c, opt_f) = match *m {
CompMember::Field(ref f) => { (None, Some(f)) }
CompMember::Comp(ref rc_c) => { (Some(rc_c), None) }
CompMember::CompField(ref rc_c, ref f) => { (Some(rc_c), Some(f)) }
};
if let Some(f) = opt_f {
let f_name = match f.bitfields {
Some(_) => {
bitfields += 1;
format!("_bindgen_bitfield_{}_", bitfields)
}
None => rust_type_id(ctx, f.name.clone())
};
let f_ty = P(cty_to_rs(ctx, &f.ty));
fields.push(respan(ctx.span, ast::StructField_ {
kind: ast::NamedField(
ctx.ext_cx.ident_of(&f_name[..]),
ast::Public,
),
id: ast::DUMMY_NODE_ID,
ty: f_ty,
attrs: Vec::new()
}));
}
if let Some(rc_c) = opt_rc_c {
let c = rc_c.borrow();
if c.name.is_empty() {
unnamed += 1;
let field_name = format!("_bindgen_data_{}_", unnamed);
fields.push(mk_blob_field(ctx, &field_name[..], c.layout));
methods.extend(gen_comp_methods(ctx, &field_name[..], 0, c.kind, &c.members, &mut extra).into_iter());
} else {
extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name),
c.layout, c.members.clone()).into_iter());
}
}
}
let def = ast::ItemStruct(
ast::VariantData::Struct(fields, ast::DUMMY_NODE_ID),
empty_generics()
);
let id = rust_type_id(ctx, name.clone());
let struct_def = P(ast::Item { ident: ctx.ext_cx.ident_of(&id[..]),
attrs: vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false)),
id: ast::DUMMY_NODE_ID,
node: def,
vis: ast::Public,
span: ctx.span
});
let mut items = vec!(struct_def);
if !methods.is_empty() {
let impl_ = ast::ItemImpl(
ast::Unsafety::Normal,
ast::ImplPolarity::Positive,
empty_generics(),
None,
P(mk_ty(ctx, false, vec!(id))),
methods
);
items.push(
P(ast::Item {
ident: ctx.ext_cx.ident_of(&name[..]),
attrs: vec!(),
id: ast::DUMMY_NODE_ID,
node: impl_,
vis: ast::Inherited,
span: ctx.span}));
}
items.push(mk_clone_impl(ctx, &name[..]));
items.push(mk_default_impl(ctx, &name[..]));
items.extend(extra.into_iter());
items
}
fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> {
let def = ast::ItemEnum(
ast::EnumDef {
variants: vec!()
},
empty_generics()
);
let id = rust_type_id(ctx, name);
P(ast::Item {
ident: ctx.ext_cx.ident_of(&id[..]),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: def,
vis: ast::Public,
span: ctx.span
})
}
fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, members: Vec<CompMember>) -> Vec<P<ast::Item>> {
fn mk_item(ctx: &mut GenCtx, name: String, item: ast::Item_, vis:
ast::Visibility, attrs: Vec<ast::Attribute>) -> P<ast::Item> {
P(ast::Item {
ident: ctx.ext_cx.ident_of(&name[..]),
attrs: attrs,
id: ast::DUMMY_NODE_ID,
node: item,
vis: vis,
span: ctx.span
})
}
let ci = Rc::new(RefCell::new(CompInfo::new(name.clone(), CompKind::Union, members.clone(), layout)));
let union = TNamed(Rc::new(RefCell::new(TypeInfo::new(name.clone(), TComp(ci)))));
// Nested composites may need to emit declarations and implementations as
// they are encountered. The declarations end up in 'extra' and are emitted
// after the current union.
let mut extra = vec!();
let data_field_name = "_bindgen_data_";
let data_field = mk_blob_field(ctx, data_field_name, layout);
let def = ast::ItemStruct(
ast::VariantData::Struct(
vec!(data_field),
ast::DUMMY_NODE_ID),
empty_generics()
);
let union_id = rust_type_id(ctx, name.clone());
let union_attrs = vec!(mk_repr_attr(ctx, layout), mk_deriving_copy_attr(ctx, false));
let union_def = mk_item(ctx, union_id, def, ast::Public, union_attrs);
let union_impl = ast::ItemImpl(
ast::Unsafety::Normal,
ast::ImplPolarity::Positive,
empty_generics(),
None,
P(cty_to_rs(ctx, &union)),
gen_comp_methods(ctx, data_field_name, 0, CompKind::Union, &members, &mut extra),
);
let mut items = vec!(
union_def,
mk_item(ctx, "".to_owned(), union_impl, ast::Inherited, Vec::new())
);
items.push(mk_clone_impl(ctx, &name[..]));
items.push(mk_default_impl(ctx, &name[..]));
items.extend(extra.into_iter());
items
}
fn const_to_rs(ctx: &mut GenCtx, name: String, val: i64, val_ty: ast::Ty) -> P<ast::Item> {
let int_lit = ast::LitInt(
val.abs() as u64,
ast::UnsuffixedIntLit(if val < 0 { ast::Minus } else { ast::Plus })
);
let cst = ast::ItemConst(
P(val_ty),
ctx.ext_cx.expr_lit(ctx.span, int_lit)
);
let id = first(rust_id(ctx, name.clone()));
P(ast::Item {
ident: ctx.ext_cx.ident_of(&id[..]),
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: cst,
vis: ast::Public,
span: ctx.span
})
}
fn enum_kind_to_rust_type_name(kind: IKind) -> &'static str {
match kind {
ISChar => "i8",
IUChar => "u8",
IShort => "i16",
IUShort => "u16",
IInt => "i32",
IUInt => "u32",
ILong => "i64",
IULong => "u64",
_ => unreachable!(),
}
}
fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, enum_items: &[EnumItem])
-> Vec<P<ast::Item>> {
let enum_name = ctx.ext_cx.ident_of(&name);
let enum_ty = ctx.ext_cx.ty_ident(ctx.span, enum_name);
let mut variants = vec![];
let mut found_values = HashMap::new();
let mut items = vec![];
for item in enum_items {
let name = ctx.ext_cx.ident_of(&item.name);
if let Some(orig) = found_values.get(&item.val) {
let value = ctx.ext_cx.expr_path(
ctx.ext_cx.path(ctx.span, vec![enum_name, *orig]));
items.push(P(ast::Item {
ident: name,
attrs: vec![],
id: ast::DUMMY_NODE_ID,
node: ast::ItemConst(enum_ty.clone(), value),
vis: ast::Public,
span: ctx.span,
}));
continue;
}
found_values.insert(item.val, name);
let sign = ast::UnsuffixedIntLit(if item.val < 0 { ast::Minus } else { ast::Plus });
let value = ctx.ext_cx.expr_lit(ctx.span, ast::LitInt(item.val.abs() as u64, sign));
variants.push(P(respan(ctx.span, ast::Variant_ {
name: name,
attrs: vec![],
data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
disr_expr: Some(value),
})));
}
let enum_repr = InternedString::new(enum_kind_to_rust_type_name(kind));
let repr_arg = ctx.ext_cx.meta_word(ctx.span, enum_repr);
let repr_list = ctx.ext_cx.meta_list(ctx.span, InternedString::new("repr"), vec![repr_arg]);
let repr_attr = respan(ctx.span, ast::Attribute_ {
id: mk_attr_id(),
style: ast::AttrStyle::Outer,
value: repr_list,
is_sugared_doc: false,
});
items.push(P(ast::Item {
ident: enum_name,
attrs: vec![mk_deriving_copy_attr(ctx, true), repr_attr],
id: ast::DUMMY_NODE_ID,
node: ast::ItemEnum(ast::EnumDef { variants: variants }, empty_generics()),
vis: ast::Public,
span: ctx.span,
}));
items
}
/// Generates accessors for fields in nested structs and unions which must be
/// represented in Rust as an untyped array. This process may generate
/// declarations and implementations that must be placed at the root level.
/// These are emitted into `extra`.
fn gen_comp_methods(ctx: &mut GenCtx, data_field: &str, data_offset: usize,
kind: CompKind, members: &[CompMember],
extra: &mut Vec<P<ast::Item>>) -> Vec<P<ast::ImplItem>> {
let mk_field_method = |ctx: &mut GenCtx, f: &FieldInfo, offset: usize| {
// TODO: Implement bitfield accessors
if f.bitfields.is_some() { return None; }
let (f_name, _) = rust_id(ctx, f.name.clone());
let ret_ty = P(cty_to_rs(ctx, &TPtr(Box::new(f.ty.clone()), false, Layout::zero())));
// When the offset is zero, generate slightly prettier code.
let method = {
let impl_str = format!(r"
impl X {{
pub unsafe fn {}(&mut self) -> {} {{
let raw: *mut u8 = ::std::mem::transmute(&self.{});
::std::mem::transmute(raw.offset({}))
}}
}}
", f_name, tts_to_string(&ret_ty.to_tokens(&ctx.ext_cx)[..]), data_field, offset);
parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(),
ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap()
};
method.and_then(|i| {
match i.node {
ast::ItemImpl(_, _, _, _, _, mut items) => {
items.pop()
}
_ => unreachable!("impl parsed to something other than impl")
}
})
};
let mut offset = data_offset;
let mut methods = vec!();
for m in members.into_iter() {
let advance_by = match *m {
CompMember::Field(ref f) => {
methods.extend(mk_field_method(ctx, f, offset).into_iter());
f.ty.size()
}
CompMember::Comp(ref rc_c) => {
let c = &rc_c.borrow();
methods.extend(gen_comp_methods(ctx, data_field, offset, c.kind,
&c.members, extra).into_iter());
c.layout.size
}
CompMember::CompField(ref rc_c, ref f) => {
methods.extend(mk_field_method(ctx, f, offset).into_iter());
let c = rc_c.borrow();
extra.extend(comp_to_rs(ctx, c.kind, comp_name(c.kind, &c.name),
c.layout, c.members.clone()).into_iter());
f.ty.size()
}
};
match kind {
CompKind::Struct => { offset += advance_by; }
CompKind::Union => { }
}
}
methods
}
// Implements std::default::Default using std::mem::zeroed.
fn mk_default_impl(ctx: &GenCtx, ty_name: &str) -> P<ast::Item> {
let impl_str = format!(r"
impl ::std::default::Default for {} {{
fn default() -> Self {{ unsafe {{ ::std::mem::zeroed() }} }}
}}
", ty_name);
parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(),
ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap()
}
// Implements std::clone::Clone using dereferencing
fn mk_clone_impl(ctx: &GenCtx, ty_name: &str) -> P<ast::Item> {
let impl_str = format!(r"
impl ::std::clone::Clone for {} {{
fn clone(&self) -> Self {{ *self }}
}}
", ty_name);
parse::new_parser_from_source_str(ctx.ext_cx.parse_sess(),
ctx.ext_cx.cfg(), "".to_owned(), impl_str).parse_item().unwrap().unwrap()
}
fn mk_blob_field(ctx: &GenCtx, name: &str, layout: Layout) -> Spanned<ast::StructField_> {
let ty_name = match layout.align {
1 => "u8",
2 => "u16",
4 => "u32",
8 => "u64",
_ => "u8",
};
let data_len = if ty_name == "u8" { layout.size } else { layout.size / layout.align };
let base_ty = mk_ty(ctx, false, vec!(ty_name.to_owned()));
let data_ty = P(mk_arrty(ctx, &base_ty, data_len));
respan(ctx.span, ast::StructField_ {
kind: ast::NamedField(
ctx.ext_cx.ident_of(name),
ast::Public,
),
id: ast::DUMMY_NODE_ID,
ty: data_ty,
attrs: Vec::new()
})
}
fn mk_link_name_attr(ctx: &mut GenCtx, name: String) -> ast::Attribute {
let lit = respan(ctx.span, ast::LitStr(
to_intern_str(ctx, name),
ast::CookedStr
));
let attr_val = P(respan(ctx.span, ast::MetaNameValue(
to_intern_str(ctx, "link_name".to_owned()), lit
)));
let attr = ast::Attribute_ {
id: mk_attr_id(),
style: ast::AttrStyle::Outer,
value: attr_val,
is_sugared_doc: false
};
respan(ctx.span, attr)
}
fn mk_repr_attr(ctx: &mut GenCtx, layout: Layout) -> ast::Attribute {
let mut values = vec!(P(respan(ctx.span, ast::MetaWord(to_intern_str(ctx, "C".to_owned())))));
if layout.packed {
values.push(P(respan(ctx.span, ast::MetaWord(to_intern_str(ctx, "packed".to_owned())))));
}
let attr_val = P(respan(ctx.span, ast::MetaList(
to_intern_str(ctx, "repr".to_owned()),
values
)));
respan(ctx.span, ast::Attribute_ {
id: mk_attr_id(),
style: ast::AttrStyle::Outer,
value: attr_val,
is_sugared_doc: false
})
}
fn mk_deriving_copy_attr(ctx: &mut GenCtx, clone: bool) -> ast::Attribute {
let mut words = vec!();
if clone {
words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Clone")));
}
words.push(ctx.ext_cx.meta_word(ctx.span, InternedString::new("Copy")));
let attr_val = ctx.ext_cx.meta_list(ctx.span, InternedString::new("derive"), words);
respan(ctx.span, ast::Attribute_ {
id: mk_attr_id(),
style: ast::AttrStyle::Outer,
value: attr_val,
is_sugared_doc: false
})
}
fn cvar_to_rs(ctx: &mut GenCtx, name: String,
ty: &Type,
is_const: bool) -> P<ast::ForeignItem> {
let (rust_name, was_mangled) = rust_id(ctx, name.clone());
let mut attrs = Vec::new();
if was_mangled {
attrs.push(mk_link_name_attr(ctx, name));
}
let val_ty = P(cty_to_rs(ctx, ty));
P(ast::ForeignItem {
ident: ctx.ext_cx.ident_of(&rust_name[..]),
attrs: attrs,
node: ast::ForeignItemStatic(val_ty, !is_const),
id: ast::DUMMY_NODE_ID,
span: ctx.span,
vis: ast::Public,
})
}
fn cfuncty_to_rs(ctx: &mut GenCtx,
rty: &Type,
aty: &[(String, Type)],
var: bool) -> ast::FnDecl {
let ret = match *rty {
TVoid => ast::DefaultReturn(ctx.span),
_ => ast::Return(P(cty_to_rs(ctx, rty)))
};
let mut unnamed: usize = 0;
let args: Vec<ast::Arg> = aty.iter().map(|arg| {
let (ref n, ref t) = *arg;
let arg_name = if n.is_empty() {
unnamed += 1;
format!("arg{}", unnamed)
} else {
first(rust_id(ctx, n.clone()))
};
// From the C90 standard (http://c0x.coding-guidelines.com/6.7.5.3.html)
// 1598 - A declaration of a parameter as “array of type” shall be
// adjusted to “qualified pointer to type”, where the type qualifiers
// (if any) are those specified within the [ and ] of the array type
// derivation.
let arg_ty = P(match *t {
TArray(ref typ, _, ref l) => cty_to_rs(ctx, &TPtr(typ.clone(), false, l.clone())),
_ => cty_to_rs(ctx, t),
});
ast::Arg {
ty: arg_ty,
pat: P(ast::Pat {
id: ast::DUMMY_NODE_ID,
node: ast::PatIdent(
ast::BindByValue(ast::MutImmutable),
respan(ctx.span, ctx.ext_cx.ident_of(&arg_name[..])),
None
),
span: ctx.span
}),
id: ast::DUMMY_NODE_ID,
}
}).collect();
let var = !args.is_empty() && var;
ast::FnDecl {
inputs: args,
output: ret,
variadic: var
}
}
fn cfunc_to_rs(ctx: &mut GenCtx, name: String, rty: &Type,
aty: &[(String, Type)],
var: bool) -> P<ast::ForeignItem> {
let var = !aty.is_empty() && var;
let decl = ast::ForeignItemFn(
P(cfuncty_to_rs(ctx, rty, aty, var)),
empty_generics()
);
let (rust_name, was_mangled) = rust_id(ctx, name.clone());
let mut attrs = Vec::new();
if was_mangled {
attrs.push(mk_link_name_attr(ctx, name));
}
P(ast::ForeignItem {
ident: ctx.ext_cx.ident_of(&rust_name[..]),
attrs: attrs,
node: decl,
id: ast::DUMMY_NODE_ID,
span: ctx.span,
vis: ast::Public,
})
}
fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty {
let prefix = vec!["std".to_owned(), "os".to_owned(), "raw".to_owned()];
let raw = |fragment: &str| {
let mut path = prefix.clone();
path.push(fragment.to_owned());
path
};
match *ty {
TVoid => mk_ty(ctx, true, raw("c_void")),
TInt(i, ref layout) => match i {
IBool => {
let ty_name = match layout.size {
1 => "u8",
2 => "u16",
4 => "u32",
8 => "u64",
_ => "u8",
};
mk_ty(ctx, false, vec!(ty_name.to_owned()))
},
ISChar => mk_ty(ctx, true, raw("c_char")),
IUChar => mk_ty(ctx, true, raw("c_uchar")),
IInt => mk_ty(ctx, true, raw("c_int")),
IUInt => mk_ty(ctx, true, raw("c_uint")),
IShort => mk_ty(ctx, true, raw("c_short")),
IUShort => mk_ty(ctx, true, raw("c_ushort")),
ILong => mk_ty(ctx, true, raw("c_long")),
IULong => mk_ty(ctx, true, raw("c_ulong")),
ILongLong => mk_ty(ctx, true, raw("c_longlong")),
IULongLong => mk_ty(ctx, true, raw("c_ulonglong"))
},
TFloat(f, _) => match f {
FFloat => mk_ty(ctx, true, raw("c_float")),
FDouble => mk_ty(ctx, true, raw("c_double"))
},
TPtr(ref t, is_const, _) => {
let id = cty_to_rs(ctx, &**t);
mk_ptrty(ctx, &id, is_const)
},
TArray(ref t, s, _) => {
let ty = cty_to_rs(ctx, &**t);
mk_arrty(ctx, &ty, s)
},
TFuncPtr(ref sig) => {
let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, &sig.args[..], sig.is_variadic);
let unsafety = if sig.is_safe { ast::Unsafety::Normal } else { ast::Unsafety::Unsafe };
mk_fnty(ctx, &decl, unsafety, sig.abi)
},
TFuncProto(ref sig) => {
let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, &sig.args[..], sig.is_variadic);
let unsafety = if sig.is_safe { ast::Unsafety::Normal } else { ast::Unsafety::Unsafe };
mk_fn_proto_ty(ctx, &decl, unsafety, sig.abi)
},
TNamed(ref ti) => {
let id = rust_type_id(ctx, ti.borrow().name.clone());
mk_ty(ctx, false, vec!(id))
},
TComp(ref ci) => {
let mut c = ci.borrow_mut();
c.name = unnamed_name(ctx, c.name.clone());
mk_ty(ctx, false, vec!(comp_name(c.kind, &c.name)))
},
TEnum(ref ei) => {
let mut e = ei.borrow_mut();
e.name = unnamed_name(ctx, e.name.clone());
mk_ty(ctx, false, vec!(enum_name(&e.name)))
}
}
}
fn mk_ty(ctx: &GenCtx, global: bool, segments: Vec<String>) -> ast::Ty {
let ty = ast::TyPath(
None,
ast::Path {
span: ctx.span,
global: global,
segments: segments.iter().map(|s| {
ast::PathSegment {
identifier: ctx.ext_cx.ident_of(&s[..]),
parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: Vec::new(),
types: OwnedSlice::empty(),
bindings: OwnedSlice::empty(),
}),
}
}).collect()
},
);
ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ty,
span: ctx.span
}
}
fn mk_ptrty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty {
let ty = ast::TyPtr(ast::MutTy {
ty: P(base.clone()),
mutbl: if is_const { ast::MutImmutable } else { ast::MutMutable }
});
ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ty,
span: ctx.span
}
}
fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: usize) -> ast::Ty {
let int_lit = ast::LitInt(n as u64, ast::UnsignedIntLit(ast::TyUs));
let sz = ast::ExprLit(P(respan(ctx.span, int_lit)));
let ty = ast::TyFixedLengthVec(
P(base.clone()),
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: sz,
span: ctx.span,
attrs: None,
})
);
ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ty,
span: ctx.span
}
}
fn mk_fn_proto_ty(ctx: &mut GenCtx,
decl: &ast::FnDecl,
unsafety: ast::Unsafety,
abi: abi::Abi) -> ast::Ty {
let fnty = ast::TyBareFn(P(ast::BareFnTy {
unsafety: unsafety,
abi: abi,
lifetimes: Vec::new(),
decl: P(decl.clone())
}));
ast::Ty {
id: ast::DUMMY_NODE_ID,
node: fnty,
span: ctx.span,
}
}
fn mk_fnty(ctx: &mut GenCtx,
decl: &ast::FnDecl,
unsafety: ast::Unsafety,
abi: abi::Abi) -> ast::Ty {
let fnty = ast::TyBareFn(P(ast::BareFnTy {
unsafety: unsafety,
abi: abi,
lifetimes: Vec::new(),
decl: P(decl.clone())
}));
let segs = vec![
ast::PathSegment {
identifier: ctx.ext_cx.ident_of("std"),
parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: Vec::new(),
types: OwnedSlice::empty(),
bindings: OwnedSlice::empty(),
}),
},
ast::PathSegment {
identifier: ctx.ext_cx.ident_of("option"),
parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: Vec::new(),
types: OwnedSlice::empty(),
bindings: OwnedSlice::empty(),
}),
},
ast::PathSegment {
identifier: ctx.ext_cx.ident_of("Option"),
parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: Vec::new(),
types: OwnedSlice::from_vec(vec!(
P(ast::Ty {
id: ast::DUMMY_NODE_ID,
node: fnty,
span: ctx.span
})
)),
bindings: OwnedSlice::empty(),
}),
}
];
ast::Ty {
id: ast::DUMMY_NODE_ID,
node: ast::TyPath(
None,
ast::Path {
span: ctx.span,
global: true,
segments: segs
},
),
span: ctx.span
}
}