diff options
-rw-r--r-- | src/gen.rs | 333 | ||||
-rw-r--r-- | src/parser.rs | 137 | ||||
-rw-r--r-- | src/types.rs | 23 | ||||
-rw-r--r-- | tests/headers/struct_with_anon_struct.h | 6 | ||||
-rw-r--r-- | tests/headers/struct_with_anon_union.h | 6 | ||||
-rw-r--r-- | tests/headers/struct_with_anon_unnamed_struct.h | 6 | ||||
-rw-r--r-- | tests/headers/struct_with_anon_unnamed_union.h | 6 | ||||
-rw-r--r-- | tests/headers/struct_with_nesting.h | 17 | ||||
-rw-r--r-- | tests/headers/struct_with_struct.h | 6 | ||||
-rw-r--r-- | tests/headers/union_with_anon_struct.h | 6 | ||||
-rw-r--r-- | tests/headers/union_with_anon_union.h | 6 | ||||
-rw-r--r-- | tests/headers/union_with_anon_unnamed_struct.h | 9 | ||||
-rw-r--r-- | tests/headers/union_with_anon_unnamed_union.h | 7 | ||||
-rw-r--r-- | tests/headers/union_with_nesting.h | 14 | ||||
-rw-r--r-- | tests/struct.rs | 75 | ||||
-rw-r--r-- | tests/union.rs | 84 |
16 files changed, 555 insertions, 186 deletions
@@ -9,7 +9,7 @@ use std::collections::hash_map::{Occupied, Vacant}; use syntax::abi; use syntax::ast; -use syntax::codemap::{Span, respan, ExpnInfo, NameAndSpan, MacroBang}; +use syntax::codemap::{Span, Spanned, respan, ExpnInfo, NameAndSpan, MacroBang}; use syntax::ext::base; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; @@ -93,15 +93,22 @@ fn unnamed_name(ctx: &mut GenCtx, name: String) -> String { }; } -fn struct_name(name: String) -> String { +fn comp_name(kind: CompKind, name: &String) -> String { + match kind { + CompKind::Struct => struct_name(name), + CompKind::Union => union_name(name), + } +} + +fn struct_name(name: &String) -> String { format!("Struct_{}", name) } -fn union_name(name: String) -> String { +fn union_name(name: &String) -> String { format!("Union_{}", name) } -fn enum_name(name: String) -> String { +fn enum_name(name: &String) -> String { format!("Enum_{}", name) } @@ -156,11 +163,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa c.name = unnamed_name(&mut ctx, c.name.clone()); } let c = ci.borrow().clone(); - if c.cstruct { - defs.push(opaque_to_rs(&mut ctx, struct_name(c.name))); - } else { - defs.push(opaque_to_rs(&mut ctx, union_name(c.name))); - } + defs.push(opaque_to_rs(&mut ctx, comp_name(c.kind, &c.name))); }, GComp(ci) => { { @@ -168,16 +171,8 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa c.name = unnamed_name(&mut ctx, c.name.clone()); } let c = ci.borrow().clone(); - if c.cstruct { - defs.push(cstruct_to_rs(&mut ctx, struct_name(c.name.clone()), - // this clone is necessary to prevent dynamic borrow - // check errors. - // FIXME: remove the @mut in types.rs to fix this - c.fields.clone())) - } else { - defs.extend(cunion_to_rs(&mut ctx, union_name(c.name.clone()), - c.layout, c.fields).into_iter()) - } + defs.extend(comp_to_rs(&mut ctx, c.kind, comp_name(c.kind, &c.name), + c.layout, c.members).into_iter()) }, GEnumDecl(ei) => { { @@ -185,7 +180,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa 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))); + defs.push(opaque_to_rs(&mut ctx, enum_name(&e.name))); }, GEnum(ei) => { { @@ -193,7 +188,7 @@ pub fn gen_mod(links: &[(String, Option<String>)], globs: Vec<Global>, span: Spa e.name = unnamed_name(&mut ctx, e.name.clone()); } let e = ei.borrow().clone(); - defs.extend(cenum_to_rs(&mut ctx, enum_name(e.name.clone()), e.kind, e.items).into_iter()) + defs.extend(cenum_to_rs(&mut ctx, enum_name(&e.name), e.kind, e.items).into_iter()) }, _ => { } } @@ -436,11 +431,7 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item> if is_empty { ci.borrow_mut().name = name.clone(); let c = ci.borrow().clone(); - if c.cstruct { - vec!(cstruct_to_rs(ctx, name, c.fields)) - } else { - cunion_to_rs(ctx, name, c.layout, c.fields) - } + comp_to_rs(ctx, c.kind, name, c.layout, c.members) } else { vec!(mk_item(ctx, name, ty)) } @@ -459,46 +450,98 @@ fn ctypedef_to_rs(ctx: &mut GenCtx, name: String, ty: &Type) -> Vec<P<ast::Item> } } -fn cstruct_to_rs(ctx: &mut GenCtx, name: String, fields: Vec<FieldInfo>) -> P<ast::Item> { +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, members), + CompKind::Union => cunion_to_rs(ctx, name, layout, members), + } +} + +fn cstruct_to_rs(ctx: &mut GenCtx, name: String, members: Vec<CompMember>) -> Vec<P<ast::Item>> { let mut unnamed: uint = 0; - let fs: Vec<ast::StructField> = fields.iter().map(|f| { - let f_name = if f.name.is_empty() || "_" == f.name.as_slice() { - unnamed += 1; - format!("unnamed_field{}", unnamed) - } else { - rust_type_id(ctx, f.name.clone()) + + 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!(); + + for m in members.iter() { + 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)) } }; - let f_ty = P(cty_to_rs(ctx, &f.ty)); + if let Some(f) = opt_f { + let f_name = rust_type_id(ctx, f.name.clone()); + let f_ty = P(cty_to_rs(ctx, &f.ty)); - respan(ctx.span, ast::StructField_ { - kind: ast::NamedField( - ctx.ext_cx.ident_of(f_name.as_slice()), - ast::Public, - ), - id: ast::DUMMY_NODE_ID, - ty: f_ty, - attrs: Vec::new() - }) - }).collect(); + fields.push(respan(ctx.span, ast::StructField_ { + kind: ast::NamedField( + ctx.ext_cx.ident_of(f_name.as_slice()), + ast::Public, + ), + id: ast::DUMMY_NODE_ID, + ty: f_ty, + attrs: Vec::new() + })); + } - let ctor_id = if fs.is_empty() { Some(ast::DUMMY_NODE_ID) } else { None }; + 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.as_slice(), c.layout)); + methods.extend(gen_comp_methods(ctx, field_name.as_slice(), 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 ctor_id = if fields.is_empty() { Some(ast::DUMMY_NODE_ID) } else { None }; let def = ast::ItemStruct( P(ast::StructDef { - fields: fs, + fields: fields, ctor_id: ctor_id, }), empty_generics() ); - let id = rust_type_id(ctx, name); - return P(ast::Item { ident: ctx.ext_cx.ident_of(id.as_slice()), + let id = rust_type_id(ctx, name.clone()); + let struct_def = P(ast::Item { ident: ctx.ext_cx.ident_of(id.as_slice()), attrs: vec!(mk_repr_attr(ctx), mk_deriving_copy_attr(ctx)), 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, + empty_generics(), + None, + P(mk_ty(ctx, false, vec!(id))), + methods + ); + items.push( + P(ast::Item { + ident: ctx.ext_cx.ident_of(name.as_slice()), + attrs: vec!(), + id: ast::DUMMY_NODE_ID, + node: impl_, + vis: ast::Inherited, + span: ctx.span})); + } + items.extend(extra.into_iter()); + items } fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> { @@ -519,7 +562,7 @@ fn opaque_to_rs(ctx: &mut GenCtx, name: String) -> P<ast::Item> { }); } -fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<FieldInfo>) -> Vec<P<ast::Item>> { +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> { return P(ast::Item { @@ -532,32 +575,20 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<Fiel }); } - let ci = Rc::new(RefCell::new(CompInfo::new(name.clone(), false, fields.clone(), layout))); + 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))))); - 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_string())); - let data_ty = P(mk_arrty(ctx, &base_ty, data_len)); - let data = respan(ctx.span, ast::StructField_ { - kind: ast::NamedField( - ctx.ext_cx.ident_of("data"), - ast::Public, - ), - id: ast::DUMMY_NODE_ID, - ty: data_ty, - attrs: Vec::new() - }); + // 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( P(ast::StructDef { - fields: Vec::from_elem(1, data), + fields: vec!(data_field), ctor_id: None, }), empty_generics() @@ -566,72 +597,20 @@ fn cunion_to_rs(ctx: &mut GenCtx, name: String, layout: Layout, fields: Vec<Fiel let union_attrs = vec!(mk_repr_attr(ctx), mk_deriving_copy_attr(ctx)); let union_def = mk_item(ctx, union_id, def, ast::Public, union_attrs); - let expr = quote_expr!( - &ctx.ext_cx, - unsafe { ::std::mem::transmute(self) } - ); - let mut unnamed: uint = 0; - let fs = fields.iter().map(|f| { - let f_name = if f.name.is_empty() || "_" == f.name.as_slice() { - unnamed += 1; - format!("unnamed_field{}", unnamed) - } else { - first(rust_id(ctx, f.name.clone())) - }; - - let ret_ty = P(cty_to_rs(ctx, &TPtr(box f.ty.clone(), false, Layout::zero()))); - let body = P(ast::Block { - view_items: Vec::new(), - stmts: Vec::new(), - expr: Some(expr.clone()), - id: ast::DUMMY_NODE_ID, - rules: ast::DefaultBlock, - span: ctx.span - }); - - let decl = ast::MethDecl( - ctx.ext_cx.ident_of(f_name.as_slice()), - empty_generics(), - abi::Rust, - respan( - ctx.span, - ast::SelfRegion(None, ast::MutMutable, ctx.ext_cx.ident_of("self")) - ), - ast::Unsafety::Normal, - P(ast::FnDecl { - inputs: Vec::from_elem( - 1, ast::Arg::new_self( - ctx.span, - ast::MutImmutable, - ctx.ext_cx.ident_of("self") - )), - output: ast::Return(ret_ty), - variadic: false - }), - body, - ast::Public - ); - - ast::MethodImplItem(P(ast::Method { - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - span: ctx.span, - node: decl, - })) - }).collect(); - - let methods = ast::ItemImpl( + let union_impl = ast::ItemImpl( ast::Unsafety::Normal, empty_generics(), None, P(cty_to_rs(ctx, &union)), - fs + gen_comp_methods(ctx, data_field_name, 0, CompKind::Union, &members, &mut extra), ); - return vec!( + let mut items = vec!( union_def, - mk_item(ctx, "".to_string(), methods, ast::Inherited, Vec::new()) + mk_item(ctx, "".to_string(), union_impl, ast::Inherited, Vec::new()) ); + items.extend(extra.into_iter()); + items } fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, items: Vec<EnumItem>) -> Vec<P<ast::Item>> { @@ -670,6 +649,92 @@ fn cenum_to_rs(ctx: &mut GenCtx, name: String, kind: IKind, items: Vec<EnumItem> return def; } +/// 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: uint, + kind: CompKind, members: &Vec<CompMember>, + extra: &mut Vec<P<ast::Item>>) -> Vec<ast::ImplItem> { + let data_ident = ctx.ext_cx.ident_of(data_field); + + let mk_field_method = |ctx: &mut GenCtx, f: &FieldInfo, offset: uint| { + let (f_name, _) = rust_id(ctx, f.name.clone()); + let f_name_ident = ctx.ext_cx.ident_of(f_name.as_slice()); + let ret_ty = P(cty_to_rs(ctx, &TPtr(box f.ty.clone(), false, Layout::zero()))); + + // When the offset is zero, generate slightly prettier code. + let method = if offset == 0 { + quote_method!(&ctx.ext_cx, + pub unsafe fn $f_name_ident(&mut self) -> $ret_ty { + ::std::mem::transmute(&self.$data_ident) + } + ) + } else { + let offset_expr = &ctx.ext_cx.expr_int(ctx.span, offset.to_int().unwrap()); + quote_method!(&ctx.ext_cx, + pub unsafe fn $f_name_ident(&mut self) -> $ret_ty { + let raw: *mut u8 = ::std::mem::transmute(&self.$data_ident); + ::std::mem::transmute(raw.offset($offset_expr)) + } + ) + }; + ast::MethodImplItem(method) + }; + + let mut offset = data_offset; + let mut methods = vec!(); + for m in members.iter() { + let advance_by = match m { + &CompMember::Field(ref f) => { + methods.push(mk_field_method(ctx, f, offset)); + f.ty.size() + } + &CompMember::Comp(ref rc_c) => { + let ref 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.push(mk_field_method(ctx, f, offset)); + + 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 +} + +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_string())); + 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), @@ -859,21 +924,17 @@ fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty { &TComp(ref ci) => { let mut c = ci.borrow_mut(); c.name = unnamed_name(ctx, c.name.clone()); - if c.cstruct { - mk_ty(ctx, false, vec!(struct_name(c.name.clone()))) - } else { - mk_ty(ctx, false, vec!(union_name(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.clone()))) + mk_ty(ctx, false, vec!(enum_name(&e.name))) } }; } -fn mk_ty(ctx: &mut GenCtx, global: bool, segments: Vec<String>) -> ast::Ty { +fn mk_ty(ctx: &GenCtx, global: bool, segments: Vec<String>) -> ast::Ty { let ty = ast::TyPath( ast::Path { span: ctx.span, @@ -912,7 +973,7 @@ fn mk_ptrty(ctx: &mut GenCtx, base: &ast::Ty, is_const: bool) -> ast::Ty { }; } -fn mk_arrty(ctx: &mut GenCtx, base: &ast::Ty, n: uint) -> ast::Ty { +fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: uint) -> ast::Ty { let int_lit = ast::LitInt(n as u64, ast::UnsignedIntLit(ast::TyU)); let sz = ast::ExprLit(P(respan(ctx.span, int_lit))); let ty = ast::TyFixedLengthVec( diff --git a/src/parser.rs b/src/parser.rs index e26ce4f5..363e0b1a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -72,11 +72,11 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { let glob_decl = match cursor.kind() { CXCursor_StructDecl => { - let ci = Rc::new(RefCell::new(CompInfo::new(spelling, true, vec!(), layout))); + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Struct, vec!(), layout))); GCompDecl(ci) } CXCursor_UnionDecl => { - let ci = Rc::new(RefCell::new(CompInfo::new(spelling, false, vec!(), layout))); + let ci = Rc::new(RefCell::new(CompInfo::new(spelling, CompKind::Union, vec!(), layout))); GCompDecl(ci) } CXCursor_EnumDecl => { @@ -279,41 +279,83 @@ fn opaque_ty(ctx: &mut ClangParserCtx, ty: &cx::Type) { } } -fn visit_struct(cursor: &Cursor, - parent: &Cursor, - ctx: &mut ClangParserCtx, - fields: &mut Vec<FieldInfo>) -> Enum_CXVisitorResult { - if cursor.kind() == CXCursor_FieldDecl { - let ty = conv_ty(ctx, &cursor.cur_type(), cursor); - let name = cursor.spelling(); - let bit = cursor.bit_width(); - // If we encounter a bitfield, and fail_on_bitfield is set, throw an - // error and exit entirely. - if bit != None { - let fail = ctx.options.fail_on_bitfield; - log_err_warn(ctx, - format!("unsupported bitfield `{}` in struct `{}` ({})", - name.as_slice(), parent.spelling().as_slice(), cursor.location() - ).as_slice(), - fail - ); - } - let field = FieldInfo::new(name, ty, bit); - fields.push(field); - } - return CXChildVisit_Continue; -} +/// Recursively visits a cursor that represents a composite (struct or union) +/// type and fills members with CompMember instances representing the fields and +/// nested composites that make up the visited composite. +fn visit_composite(cursor: &Cursor, parent: &Cursor, + ctx: &mut ClangParserCtx, + members: &mut Vec<CompMember>) -> Enum_CXVisitorResult { + match cursor.kind() { + CXCursor_FieldDecl => { + let ty = conv_ty(ctx, &cursor.cur_type(), cursor); + let name = cursor.spelling(); + let bit = cursor.bit_width(); + // If we encounter a bitfield, and fail_on_bitfield is set, throw an + // error and exit entirely. + if bit != None { + let fail = ctx.options.fail_on_bitfield; + log_err_warn(ctx, + format!("unsupported bitfield `{}` in `{}` ({})", + name, parent.spelling(), cursor.location() + ).as_slice(), + fail + ); + } -fn visit_union(cursor: &Cursor, - ctx: &mut ClangParserCtx, - fields: &mut Vec<FieldInfo>) -> Enum_CXVisitorResult { - if cursor.kind() == CXCursor_FieldDecl { - let ty = conv_ty(ctx, &cursor.cur_type(), cursor); - let name = cursor.spelling(); - let field = FieldInfo::new(name, ty, None); - fields.push(field); + // The Clang C api does not fully expose composite fields, but it + // does expose them in a way that can be detected. When the current + // field kind is CXType_Unexposed and the previous member is a + // composite type--the same type as this field-- then this is a + // composite field. e.g.: + // + // struct foo { + // union { + // int a; + // char b; + // } bar; + // }; + let is_composite = if cursor.cur_type().kind() == CXType_Unexposed { + if let TComp(ref ty_compinfo) = ty { + match members.last() { + Some(&CompMember::Comp(ref c)) => c == ty_compinfo, + _ => false + } + } else { false } + } else { false }; + + + let field = FieldInfo::new(name, ty.clone(), bit); + if is_composite { + if let Some(CompMember::Comp(c)) = members.pop() { + members.push(CompMember::CompField(c, field)); + } else { + panic!(); // Checks in is_composite make this unreachable. + } + } else { + members.push(CompMember::Field(field)); + } + } + CXCursor_StructDecl | CXCursor_UnionDecl => { + let decl = decl_name(ctx, cursor); + let ci = decl.compinfo(); + cursor.visit(|c, p| { + let mut ci_ = ci.borrow_mut(); + visit_composite(c, p, ctx, &mut ci_.members) + }); + members.push(CompMember::Comp(ci)); + } + _ => { + // XXX: Some kind of warning would be nice, but this produces far + // too many. + //log_err_warn(ctx, + // format!("unhandled composite member `{}` (kind {}) in `{}` ({})", + // cursor.spelling(), cursor.kind(), parent.spelling(), cursor.location() + // ).as_slice(), + // false + //); + } } - return CXChildVisit_Continue; + CXChildVisit_Continue } fn visit_enum(cursor: &Cursor, @@ -347,27 +389,32 @@ fn visit_top<'r>(cur: &'r Cursor, let ci = decl.compinfo(); cursor.visit(|c, p| { let mut ci_ = ci.borrow_mut(); - visit_struct(c, p, ctx_, &mut ci_.fields) + visit_composite(c, p, ctx_, &mut ci_.members) }); ctx_.globals.push(GComp(ci)); }); - return if cur.kind() == CXCursor_FieldDecl { - CXChildVisit_Break - } else { - CXChildVisit_Recurse - }; + + // XXX: Review this condition. I think it is no longer necessary. + // @chris-chambers + //return if cur.kind() == CXCursor_FieldDecl { + // CXChildVisit_Break + //} else { + // CXChildVisit_Continue + //}; + + CXChildVisit_Continue } CXCursor_UnionDecl => { fwd_decl(ctx, cursor, |ctx_| { let decl = decl_name(ctx_, cursor); let ci = decl.compinfo(); - cursor.visit(|c, _| { + cursor.visit(|c, p| { let mut ci_ = ci.borrow_mut(); - visit_union(c, ctx_, &mut ci_.fields) + visit_composite(c, p, ctx_, &mut ci_.members) }); ctx_.globals.push(GComp(ci)); }); - return CXChildVisit_Recurse; + return CXChildVisit_Continue; } CXCursor_EnumDecl => { fwd_decl(ctx, cursor, |ctx_| { @@ -440,7 +487,7 @@ fn visit_top<'r>(cur: &'r Cursor, CXCursor_FieldDecl => { return CXChildVisit_Continue; } - _ => return CXChildVisit_Recurse, + _ => return CXChildVisit_Continue, } } diff --git a/src/types.rs b/src/types.rs index 37312d7a..1a0b353b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -150,19 +150,32 @@ pub enum FKind { } #[deriving(Clone, PartialEq)] +pub enum CompMember { + Field(FieldInfo), + Comp(Rc<RefCell<CompInfo>>), + CompField(Rc<RefCell<CompInfo>>, FieldInfo), +} + +#[deriving(Copy, Clone, PartialEq)] +pub enum CompKind { + Struct, + Union, +} + +#[deriving(Clone, PartialEq)] pub struct CompInfo { - pub cstruct: bool, + pub kind: CompKind, pub name: String, - pub fields: Vec<FieldInfo>, + pub members: Vec<CompMember>, pub layout: Layout, } impl CompInfo { - pub fn new(name: String, cstruct: bool, fields: Vec<FieldInfo>, layout: Layout) -> CompInfo { + pub fn new(name: String, kind: CompKind, members: Vec<CompMember>, layout: Layout) -> CompInfo { CompInfo { - cstruct: cstruct, + kind: kind, name: name, - fields: fields, + members: members, layout: layout, } } diff --git a/tests/headers/struct_with_anon_struct.h b/tests/headers/struct_with_anon_struct.h new file mode 100644 index 00000000..1617d7a8 --- /dev/null +++ b/tests/headers/struct_with_anon_struct.h @@ -0,0 +1,6 @@ +struct foo { + struct { + int a; + int b; + } bar; +}; diff --git a/tests/headers/struct_with_anon_union.h b/tests/headers/struct_with_anon_union.h new file mode 100644 index 00000000..3a92b940 --- /dev/null +++ b/tests/headers/struct_with_anon_union.h @@ -0,0 +1,6 @@ +struct foo { + union { + unsigned int a; + unsigned short b; + } bar; +}; diff --git a/tests/headers/struct_with_anon_unnamed_struct.h b/tests/headers/struct_with_anon_unnamed_struct.h new file mode 100644 index 00000000..f8ac4225 --- /dev/null +++ b/tests/headers/struct_with_anon_unnamed_struct.h @@ -0,0 +1,6 @@ +struct foo { + struct { + unsigned int a; + unsigned int b; + }; +}; diff --git a/tests/headers/struct_with_anon_unnamed_union.h b/tests/headers/struct_with_anon_unnamed_union.h new file mode 100644 index 00000000..7158e727 --- /dev/null +++ b/tests/headers/struct_with_anon_unnamed_union.h @@ -0,0 +1,6 @@ +struct foo { + union { + unsigned int a; + unsigned short b; + }; +}; diff --git a/tests/headers/struct_with_nesting.h b/tests/headers/struct_with_nesting.h new file mode 100644 index 00000000..9d7fa176 --- /dev/null +++ b/tests/headers/struct_with_nesting.h @@ -0,0 +1,17 @@ +struct foo { + unsigned int a; + union { + unsigned int b; + struct { + unsigned short c1; + unsigned short c2; + }; + + struct { + unsigned char d1; + unsigned char d2; + unsigned char d3; + unsigned char d4; + }; + }; +}; diff --git a/tests/headers/struct_with_struct.h b/tests/headers/struct_with_struct.h new file mode 100644 index 00000000..78b1cc81 --- /dev/null +++ b/tests/headers/struct_with_struct.h @@ -0,0 +1,6 @@ +struct foo { + struct { + unsigned int x; + unsigned int y; + } bar; +}; diff --git a/tests/headers/union_with_anon_struct.h b/tests/headers/union_with_anon_struct.h new file mode 100644 index 00000000..7f8dec95 --- /dev/null +++ b/tests/headers/union_with_anon_struct.h @@ -0,0 +1,6 @@ +union foo { + struct { + unsigned int a; + unsigned int b; + } bar; +}; diff --git a/tests/headers/union_with_anon_union.h b/tests/headers/union_with_anon_union.h new file mode 100644 index 00000000..212431b8 --- /dev/null +++ b/tests/headers/union_with_anon_union.h @@ -0,0 +1,6 @@ +union foo { + union { + unsigned int a; + unsigned short b; + } bar; +}; diff --git a/tests/headers/union_with_anon_unnamed_struct.h b/tests/headers/union_with_anon_unnamed_struct.h new file mode 100644 index 00000000..79558049 --- /dev/null +++ b/tests/headers/union_with_anon_unnamed_struct.h @@ -0,0 +1,9 @@ +union pixel { + unsigned int rgba; + struct { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; + }; +}; diff --git a/tests/headers/union_with_anon_unnamed_union.h b/tests/headers/union_with_anon_unnamed_union.h new file mode 100644 index 00000000..7580771a --- /dev/null +++ b/tests/headers/union_with_anon_unnamed_union.h @@ -0,0 +1,7 @@ +union foo { + unsigned int a; + union { + unsigned short b; + unsigned char c; + }; +}; diff --git a/tests/headers/union_with_nesting.h b/tests/headers/union_with_nesting.h new file mode 100644 index 00000000..cd907d57 --- /dev/null +++ b/tests/headers/union_with_nesting.h @@ -0,0 +1,14 @@ +union foo { + unsigned int a; + struct { + union { + unsigned short b1; + unsigned short b2; + }; + + union { + unsigned short c1; + unsigned short c2; + }; + }; +}; diff --git a/tests/struct.rs b/tests/struct.rs new file mode 100644 index 00000000..ec1faa4f --- /dev/null +++ b/tests/struct.rs @@ -0,0 +1,75 @@ +#![feature(phase)] + +#[phase(plugin)] +extern crate bindgen; + +extern crate libc; + +#[test] +fn test_struct_with_anon_struct() { + mod ffi { bindgen!("headers/struct_with_anon_struct.h") } + let mut x = ffi::Struct_foo { bar: ffi::Struct_Unnamed1 { a: 0, b: 0 } }; + + x.bar.a = 1; + x.bar.b = 2; + + assert_eq!(x.bar.a, 1); + assert_eq!(x.bar.b, 2); +} + +#[test] +fn test_struct_with_anon_union() { + mod ffi { bindgen!("headers/struct_with_anon_union.h") } + let mut x = ffi::Struct_foo { bar: ffi::Union_Unnamed1 { _bindgen_data_: [0] } }; + + unsafe { + *x.bar.a() = 0x12345678; + assert_eq!(*x.bar.a(), 0x12345678); + assert_eq!(*x.bar.b(), 0x5678); + } +} + +#[test] +fn test_struct_with_anon_unnamed_struct() { + mod ffi { bindgen!("headers/struct_with_anon_unnamed_struct.h") } + let mut x = ffi::Struct_foo { _bindgen_data_1_: [0, 0] }; + + unsafe { + *x.a() = 0x12345678; + *x.b() = 0x87654321; + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b(), 0x87654321); + } +} + +#[test] +fn test_struct_with_anon_unnamed_union() { + mod ffi { bindgen!("headers/struct_with_anon_unnamed_union.h") } + let mut x = ffi::Struct_foo { _bindgen_data_1_: [0] }; + + unsafe { + *x.a() = 0x12345678; + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b(), 0x5678); + } +} + +#[test] +fn test_struct_with_nesting() { + mod ffi { bindgen!("headers/struct_with_nesting.h") } + let mut x = ffi::Struct_foo { a: 0, _bindgen_data_1_: [0] }; + + unsafe { + x.a = 0x12345678; + *x.b() = 0x87654321; + + assert_eq!(x.a, 0x12345678); + assert_eq!(*x.b(), 0x87654321); + assert_eq!(*x.c1(), 0x4321); + assert_eq!(*x.c2(), 0x8765); + assert_eq!(*x.d1(), 0x21); + assert_eq!(*x.d2(), 0x43); + assert_eq!(*x.d3(), 0x65); + assert_eq!(*x.d4(), 0x87); + } +} diff --git a/tests/union.rs b/tests/union.rs new file mode 100644 index 00000000..aae1e65d --- /dev/null +++ b/tests/union.rs @@ -0,0 +1,84 @@ +#![feature(phase)] + +#[phase(plugin)] +extern crate bindgen; + +extern crate libc; + +#[test] +fn test_union_with_anon_struct() { + // XXX: Rustc thinks that the anonymous struct, bar, is unused. + #[allow(dead_code)] + mod ffi { bindgen!("headers/union_with_anon_struct.h") } + let mut x = ffi::Union_foo { _bindgen_data_: [0, 0] }; + + unsafe { + (*x.bar()).a = 0x12345678; + (*x.bar()).b = 0x87654321; + + assert_eq!((*x.bar()).a, 0x12345678); + assert_eq!((*x.bar()).b, 0x87654321); + } +} + +#[test] +fn test_union_with_anon_union() { + mod ffi { bindgen!("headers/union_with_anon_union.h") } + let mut x = ffi::Union_foo { _bindgen_data_: [0] }; + + unsafe { + *(*x.bar()).a() = 0x12345678; + + assert_eq!(*(*x.bar()).a(), 0x12345678); + assert_eq!(*(*x.bar()).b(), 0x5678); + } +} + +#[test] +fn test_union_with_anon_unnamed_struct() { + mod ffi { bindgen!("headers/union_with_anon_unnamed_struct.h") } + let mut x = ffi::Union_pixel { _bindgen_data_: [0] }; + + unsafe { + *x.r() = 0xca; + *x.g() = 0xcb; + *x.b() = 0xcc; + *x.a() = 0xcd; + + assert_eq!(*x.rgba(), 0xcdcccbca); + assert_eq!(*x.r(), 0xca); + assert_eq!(*x.g(), 0xcb); + assert_eq!(*x.b(), 0xcc); + assert_eq!(*x.a(), 0xcd); + } +} + +#[test] +fn test_union_with_anon_unnamed_union() { + mod ffi { bindgen!("headers/union_with_anon_unnamed_union.h") } + let mut x = ffi::Union_foo { _bindgen_data_: [0] }; + + unsafe { + *x.a() = 0x12345678; + + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b(), 0x5678); + assert_eq!(*x.c(), 0x78); + } +} + +#[test] +fn test_union_with_nesting() { + mod ffi { bindgen!("headers/union_with_nesting.h") } + let mut x = ffi::Union_foo { _bindgen_data_: [0] }; + + unsafe { + *x.a() = 0x12345678; + + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b1(), 0x5678); + assert_eq!(*x.b2(), 0x5678); + assert_eq!(*x.c1(), 0x1234); + assert_eq!(*x.c2(), 0x1234); + } +} |