diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-04-03 18:11:11 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-03 18:11:11 -0500 |
commit | 5e85271697255498603e852eaa0723da5f41f1bf (patch) | |
tree | 300e99521f0ff6baf2677363fb59696d283283e8 | |
parent | d4fce37939fbcebd04e7b7efec6930cfd717b13d (diff) | |
parent | 1f53966ee6b872b7443b335a84d9cf1b57394f13 (diff) |
Auto merge of #608 - emilio:destructor-codegen, r=fitzgen
Destructor codegen
Based on #542, and on top of #606, with a bunch more tests and fixes.
24 files changed, 307 insertions, 30 deletions
diff --git a/bindgen-integration/cpp/Test.cc b/bindgen-integration/cpp/Test.cc index 1d962406..7b0ec4ad 100644 --- a/bindgen-integration/cpp/Test.cc +++ b/bindgen-integration/cpp/Test.cc @@ -21,6 +21,15 @@ Test::Test(double foo) , m_double(foo) {} +AutoRestoreBool::AutoRestoreBool(bool* ptr) + : m_ptr(ptr) + , m_value(*ptr) +{} + +AutoRestoreBool::~AutoRestoreBool() { + *m_ptr = m_value; +} + namespace bitfields { bool @@ -47,4 +56,4 @@ Third::assert(int first, bool second, ItemKind third) kind == third; } -} +} // namespace bitfields diff --git a/bindgen-integration/cpp/Test.h b/bindgen-integration/cpp/Test.h index 310478bb..01c7aea1 100644 --- a/bindgen-integration/cpp/Test.h +++ b/bindgen-integration/cpp/Test.h @@ -64,3 +64,11 @@ struct Third { }; } // namespace bitfields + +struct AutoRestoreBool { + bool* m_ptr; + bool m_value; + + AutoRestoreBool(bool*); + ~AutoRestoreBool(); +}; diff --git a/bindgen-integration/src/lib.rs b/bindgen-integration/src/lib.rs index 8d7eb753..be3c8451 100755 --- a/bindgen-integration/src/lib.rs +++ b/bindgen-integration/src/lib.rs @@ -101,3 +101,21 @@ fn test_bitfields_third() { bindings::bitfields::ItemKind::ITEM_KIND_TRES) }); } + +impl Drop for bindings::AutoRestoreBool { + fn drop(&mut self) { + unsafe { bindings::AutoRestoreBool::destruct(self) } + } +} + +#[test] +fn test_destructors() { + let mut v = true; + + { + let auto_restore = unsafe { bindings::AutoRestoreBool::new(&mut v) }; + v = false; + } + + assert!(v, "Should've been restored when going out of scope"); +} diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index fb6c839d..5586c146 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1597,6 +1597,24 @@ impl CodeGenerator for CompInfo { self); } } + + if ctx.options().codegen_config.destructors { + if let Some((is_virtual, destructor)) = self.destructor() { + let kind = if is_virtual { + MethodKind::VirtualDestructor + } else { + MethodKind::Destructor + }; + + Method::new(kind, destructor, false) + .codegen_method(ctx, + &mut methods, + &mut method_names, + result, + whitelisted_items, + self); + } + } } // NB: We can't use to_rust_ty here since for opaque types this tries to @@ -1693,6 +1711,7 @@ impl MethodCodegen for Method { if self.is_virtual() { return; // FIXME } + // First of all, output the actual function. let function_item = ctx.resolve_item(self.signature()); function_item.codegen(ctx, result, whitelisted_items, &()); @@ -1701,6 +1720,7 @@ impl MethodCodegen for Method { let signature_item = ctx.resolve_item(function.signature()); let mut name = match self.kind() { MethodKind::Constructor => "new".into(), + MethodKind::Destructor => "destruct".into(), _ => function.name().to_owned(), }; @@ -1801,11 +1821,7 @@ impl MethodCodegen for Method { exprs[0] = quote_expr!(ctx.ext_cx(), &mut __bindgen_tmp); } else if !self.is_static() { assert!(!exprs.is_empty()); - exprs[0] = if self.is_const() { - quote_expr!(ctx.ext_cx(), &*self) - } else { - quote_expr!(ctx.ext_cx(), &mut *self) - }; + exprs[0] = quote_expr!(ctx.ext_cx(), self); }; let call = aster::expr::ExprBuilder::new() diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 9a6548a7..2c7b6de2 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -26,6 +26,10 @@ pub enum MethodKind { /// A constructor. We represent it as method for convenience, to avoid code /// duplication. Constructor, + /// A destructor. + Destructor, + /// A virtual destructor. + VirtualDestructor, /// A static method. Static, /// A normal method. @@ -61,6 +65,12 @@ impl Method { self.kind } + /// Is this a destructor method? + pub fn is_destructor(&self) -> bool { + self.kind == MethodKind::Destructor || + self.kind == MethodKind::VirtualDestructor + } + /// Is this a constructor? pub fn is_constructor(&self) -> bool { self.kind == MethodKind::Constructor @@ -68,7 +78,8 @@ impl Method { /// Is this a virtual method? pub fn is_virtual(&self) -> bool { - self.kind == MethodKind::Virtual + self.kind == MethodKind::Virtual || + self.kind == MethodKind::VirtualDestructor } /// Is this a static method? @@ -250,6 +261,10 @@ pub struct CompInfo { /// The different constructors this struct or class contains. constructors: Vec<ItemId>, + /// The destructor of this type. The bool represents whether this destructor + /// is virtual. + destructor: Option<(bool, ItemId)>, + /// Vector of classes this one inherits from. base_members: Vec<Base>, @@ -321,6 +336,7 @@ impl CompInfo { template_params: vec![], methods: vec![], constructors: vec![], + destructor: None, base_members: vec![], inner_types: vec![], inner_vars: vec![], @@ -434,6 +450,11 @@ impl CompInfo { &self.constructors } + /// Get this type's destructor. + pub fn destructor(&self) -> Option<(bool, ItemId)> { + self.destructor + } + /// What kind of compound type is this? pub fn kind(&self) -> CompKind { self.kind @@ -657,8 +678,9 @@ impl CompInfo { CXCursor_Constructor => { ci.constructors.push(signature); } - // TODO(emilio): Bind the destructor? - CXCursor_Destructor => {} + CXCursor_Destructor => { + ci.destructor = Some((is_virtual, signature)); + } CXCursor_CXXMethod => { let is_const = cur.method_is_const(); let method_kind = if is_static { diff --git a/src/ir/function.rs b/src/ir/function.rs index fd88b657..0809b3c2 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -114,6 +114,7 @@ fn get_abi(cc: CXCallingConv) -> Option<abi::Abi> { pub fn cursor_mangling(ctx: &BindgenContext, cursor: &clang::Cursor) -> Option<String> { + use clang_sys; if !ctx.options().enable_mangling { return None; } @@ -131,10 +132,40 @@ pub fn cursor_mangling(ctx: &BindgenContext, } // Try to undo backend linkage munging (prepended _, generally) + // + // TODO(emilio): This is wrong when the target system is not the host + // system. See https://github.com/servo/rust-bindgen/issues/593 if cfg!(target_os = "macos") { mangling.remove(0); } + if cursor.kind() == clang_sys::CXCursor_Destructor { + // With old (3.8-) libclang versions, and the Itanium ABI, clang returns + // the "destructor group 0" symbol, which means that it'll try to free + // memory, which definitely isn't what we want. + // + // Explicitly force the destructor group 1 symbol. + // + // See http://refspecs.linuxbase.org/cxxabi-1.83.html#mangling-special + // for the reference, and http://stackoverflow.com/a/6614369/1091587 for + // a more friendly explanation. + // + // We don't need to do this for constructors since clang seems to always + // have returned the C1 constructor. + // + // FIXME(emilio): Can a legit symbol in other ABIs end with this string? + // I don't think so, but if it can this would become a linker error + // anyway, not an invalid free at runtime. + // + // TODO(emilio, #611): Use cpp_demangle if this becomes nastier with + // time. + if mangling.ends_with("D0Ev") { + let new_len = mangling.len() - 4; + mangling.truncate(new_len); + mangling.push_str("D1Ev"); + } + } + Some(mangling) } @@ -220,13 +251,14 @@ impl FunctionSig { let is_method = cursor.kind() == CXCursor_CXXMethod; let is_constructor = cursor.kind() == CXCursor_Constructor; - if (is_constructor || is_method) && + let is_destructor = cursor.kind() == CXCursor_Destructor; + if (is_constructor || is_destructor || is_method) && cursor.lexical_parent() != cursor.semantic_parent() { // Only parse constructors once. return Err(ParseError::Continue); } - if is_method || is_constructor { + if is_method || is_constructor || is_destructor { let is_const = is_method && cursor.method_is_const(); let is_virtual = is_method && cursor.method_is_virtual(); let is_static = is_method && cursor.method_is_static(); @@ -292,9 +324,9 @@ impl ClangSubItemParser for Function { -> Result<ParseResult<Self>, ParseError> { use clang_sys::*; match cursor.kind() { - // FIXME(emilio): Generate destructors properly. CXCursor_FunctionDecl | CXCursor_Constructor | + CXCursor_Destructor | CXCursor_CXXMethod => {} _ => return Err(ParseError::Continue), }; @@ -325,9 +357,23 @@ impl ClangSubItemParser for Function { let sig = try!(Item::from_ty(&cursor.cur_type(), cursor, None, context)); - let name = cursor.spelling(); + let mut name = cursor.spelling(); assert!(!name.is_empty(), "Empty function name?"); + if cursor.kind() == CXCursor_Destructor { + // Remove the leading `~`. The alternative to this is special-casing + // code-generation for destructor functions, which seems less than + // ideal. + if name.starts_with('~') { + name.remove(0); + } + + // Add a suffix to avoid colliding with constructors. This would be + // technically fine (since we handle duplicated functions/methods), + // but seems easy enough to handle it here. + name.push_str("_destructor"); + } + let mut mangled_name = cursor_mangling(context, &cursor); if mangled_name.as_ref() == Some(&name) { mangled_name = None; @@ -109,6 +109,8 @@ pub struct CodegenConfig { pub methods: bool, /// Whether to generate constructors. pub constructors: bool, + /// Whether to generate destructors. + pub destructors: bool, } impl CodegenConfig { @@ -120,6 +122,7 @@ impl CodegenConfig { vars: true, methods: true, constructors: true, + destructors: true, } } @@ -131,6 +134,7 @@ impl CodegenConfig { vars: false, methods: false, constructors: false, + destructors: false, } } } diff --git a/src/options.rs b/src/options.rs index f1c8479a..6d06ad3b 100644 --- a/src/options.rs +++ b/src/options.rs @@ -111,8 +111,8 @@ pub fn builder_from_flags<I> Arg::with_name("generate") .long("generate") .help("Generate a given kind of items, split by commas. \ - Valid values are \"functions\",\"types\", \"vars\" and \ - \"methods\".") + Valid values are \"functions\",\"types\", \"vars\", \ + \"methods\", \"constructors\" and \"destructors\".") .takes_value(true), Arg::with_name("ignore-methods") .long("ignore-methods") @@ -271,6 +271,8 @@ pub fn builder_from_flags<I> "types" => config.types = true, "vars" => config.vars = true, "methods" => config.methods = true, + "constructors" => config.constructors = true, + "destructors" => config.destructors = true, _ => { return Err(Error::new(ErrorKind::Other, "Unknown generate item")); diff --git a/tests/expectations/tests/bitfield-method-same-name.rs b/tests/expectations/tests/bitfield-method-same-name.rs index 9222f74d..e180153b 100644 --- a/tests/expectations/tests/bitfield-method-same-name.rs +++ b/tests/expectations/tests/bitfield-method-same-name.rs @@ -54,14 +54,14 @@ impl Foo { } #[inline] pub unsafe fn type_(&mut self) -> ::std::os::raw::c_schar { - Foo_type(&mut *self) + Foo_type(self) } #[inline] pub unsafe fn set_type_(&mut self, c: ::std::os::raw::c_schar) { - Foo_set_type_(&mut *self, c) + Foo_set_type_(self, c) } #[inline] pub unsafe fn set_type(&mut self, c: ::std::os::raw::c_schar) { - Foo_set_type(&mut *self, c) + Foo_set_type(self, c) } } diff --git a/tests/expectations/tests/class.rs b/tests/expectations/tests/class.rs index 3ed5edd2..edb2697b 100644 --- a/tests/expectations/tests/class.rs +++ b/tests/expectations/tests/class.rs @@ -283,14 +283,14 @@ impl Clone for RealAbstractionWithTonsOfMethods { } impl RealAbstractionWithTonsOfMethods { #[inline] - pub unsafe fn bar(&self) { RealAbstractionWithTonsOfMethods_bar(&*self) } + pub unsafe fn bar(&self) { RealAbstractionWithTonsOfMethods_bar(self) } #[inline] pub unsafe fn bar1(&mut self) { - RealAbstractionWithTonsOfMethods_bar1(&mut *self) + RealAbstractionWithTonsOfMethods_bar1(self) } #[inline] pub unsafe fn bar2(&mut self, foo: ::std::os::raw::c_int) { - RealAbstractionWithTonsOfMethods_bar2(&mut *self, foo) + RealAbstractionWithTonsOfMethods_bar2(self, foo) } #[inline] pub unsafe fn sta() { RealAbstractionWithTonsOfMethods_sta() } diff --git a/tests/expectations/tests/class_with_typedef.rs b/tests/expectations/tests/class_with_typedef.rs index 03822233..e962992d 100644 --- a/tests/expectations/tests/class_with_typedef.rs +++ b/tests/expectations/tests/class_with_typedef.rs @@ -70,18 +70,18 @@ impl Default for C { } impl C { #[inline] - pub unsafe fn method(&mut self, c: C_MyInt) { C_method(&mut *self, c) } + pub unsafe fn method(&mut self, c: C_MyInt) { C_method(self, c) } #[inline] pub unsafe fn methodRef(&mut self, c: *mut C_MyInt) { - C_methodRef(&mut *self, c) + C_methodRef(self, c) } #[inline] pub unsafe fn complexMethodRef(&mut self, c: *mut C_Lookup) { - C_complexMethodRef(&mut *self, c) + C_complexMethodRef(self, c) } #[inline] pub unsafe fn anotherMethod(&mut self, c: AnotherInt) { - C_anotherMethod(&mut *self, c) + C_anotherMethod(self, c) } } #[repr(C)] diff --git a/tests/expectations/tests/gen-constructors-neg.rs b/tests/expectations/tests/gen-constructors-neg.rs new file mode 100644 index 00000000..fbeb3d5e --- /dev/null +++ b/tests/expectations/tests/gen-constructors-neg.rs @@ -0,0 +1,21 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct Foo { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::<Foo>() , 1usize , concat ! ( + "Size of: " , stringify ! ( Foo ) )); + assert_eq! (::std::mem::align_of::<Foo>() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Foo ) )); +} +impl Clone for Foo { + fn clone(&self) -> Self { *self } +} diff --git a/tests/expectations/tests/gen-constructors.rs b/tests/expectations/tests/gen-constructors.rs new file mode 100644 index 00000000..72c2fc53 --- /dev/null +++ b/tests/expectations/tests/gen-constructors.rs @@ -0,0 +1,33 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Default, Copy)] +pub struct Foo { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::<Foo>() , 1usize , concat ! ( + "Size of: " , stringify ! ( Foo ) )); + assert_eq! (::std::mem::align_of::<Foo>() , 1usize , concat ! ( + "Alignment of " , stringify ! ( Foo ) )); +} +extern "C" { + #[link_name = "_ZN3FooC1Ei"] + pub fn Foo_Foo(this: *mut Foo, a: ::std::os::raw::c_int); +} +impl Clone for Foo { + fn clone(&self) -> Self { *self } +} +impl Foo { + #[inline] + pub unsafe fn new(a: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::uninitialized(); + Foo_Foo(&mut __bindgen_tmp, a); + __bindgen_tmp + } +} diff --git a/tests/expectations/tests/gen-destructors-neg.rs b/tests/expectations/tests/gen-destructors-neg.rs new file mode 100644 index 00000000..4aaec83a --- /dev/null +++ b/tests/expectations/tests/gen-destructors-neg.rs @@ -0,0 +1,23 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Default)] +pub struct Foo { + pub bar: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::<Foo>() , 4usize , concat ! ( + "Size of: " , stringify ! ( Foo ) )); + assert_eq! (::std::mem::align_of::<Foo>() , 4usize , concat ! ( + "Alignment of " , stringify ! ( Foo ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const Foo ) ) . bar as * const _ as usize } , + 0usize , concat ! ( + "Alignment of field: " , stringify ! ( Foo ) , "::" , + stringify ! ( bar ) )); +} diff --git a/tests/expectations/tests/gen-destructors.rs b/tests/expectations/tests/gen-destructors.rs new file mode 100644 index 00000000..7ec13b03 --- /dev/null +++ b/tests/expectations/tests/gen-destructors.rs @@ -0,0 +1,31 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Default)] +pub struct Foo { + pub bar: ::std::os::raw::c_int, +} +#[test] +fn bindgen_test_layout_Foo() { + assert_eq!(::std::mem::size_of::<Foo>() , 4usize , concat ! ( + "Size of: " , stringify ! ( Foo ) )); + assert_eq! (::std::mem::align_of::<Foo>() , 4usize , concat ! ( + "Alignment of " , stringify ! ( Foo ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const Foo ) ) . bar as * const _ as usize } , + 0usize , concat ! ( + "Alignment of field: " , stringify ! ( Foo ) , "::" , + stringify ! ( bar ) )); +} +extern "C" { + #[link_name = "_ZN3FooD1Ev"] + pub fn Foo_Foo_destructor(this: *mut Foo); +} +impl Foo { + #[inline] + pub unsafe fn destruct(&mut self) { Foo_Foo_destructor(self) } +} diff --git a/tests/expectations/tests/issue-410.rs b/tests/expectations/tests/issue-410.rs index 1f624fec..8833ef4e 100644 --- a/tests/expectations/tests/issue-410.rs +++ b/tests/expectations/tests/issue-410.rs @@ -34,7 +34,7 @@ pub mod root { impl Value { #[inline] pub unsafe fn a(&mut self, arg1: root::JSWhyMagic) { - Value_a(&mut *self, arg1) + Value_a(self, arg1) } } } diff --git a/tests/expectations/tests/method-mangling.rs b/tests/expectations/tests/method-mangling.rs index 23f12280..360f0ea7 100644 --- a/tests/expectations/tests/method-mangling.rs +++ b/tests/expectations/tests/method-mangling.rs @@ -25,7 +25,5 @@ impl Clone for Foo { } impl Foo { #[inline] - pub unsafe fn type_(&mut self) -> ::std::os::raw::c_int { - Foo_type(&mut *self) - } + pub unsafe fn type_(&mut self) -> ::std::os::raw::c_int { Foo_type(self) } } diff --git a/tests/expectations/tests/namespace.rs b/tests/expectations/tests/namespace.rs index 21b5a58b..8e64fa22 100644 --- a/tests/expectations/tests/namespace.rs +++ b/tests/expectations/tests/namespace.rs @@ -58,7 +58,7 @@ pub mod root { #[inline] pub unsafe fn lets_hope_this_works(&mut self) -> ::std::os::raw::c_int { - A_lets_hope_this_works(&mut *self) + A_lets_hope_this_works(self) } } } diff --git a/tests/expectations/tests/public-dtor.rs b/tests/expectations/tests/public-dtor.rs index 1accf49c..d24e863e 100644 --- a/tests/expectations/tests/public-dtor.rs +++ b/tests/expectations/tests/public-dtor.rs @@ -16,3 +16,11 @@ fn bindgen_test_layout_cv_String() { assert_eq! (::std::mem::align_of::<cv_String>() , 1usize , concat ! ( "Alignment of " , stringify ! ( cv_String ) )); } +extern "C" { + #[link_name = "_ZN2cv6StringD1Ev"] + pub fn cv_String_String_destructor(this: *mut cv_String); +} +impl cv_String { + #[inline] + pub unsafe fn destruct(&mut self) { cv_String_String_destructor(self) } +} diff --git a/tests/expectations/tests/union_dtor.rs b/tests/expectations/tests/union_dtor.rs index bfd573e0..61fb0380 100644 --- a/tests/expectations/tests/union_dtor.rs +++ b/tests/expectations/tests/union_dtor.rs @@ -52,3 +52,13 @@ fn bindgen_test_layout_UnionWithDtor() { "Alignment of field: " , stringify ! ( UnionWithDtor ) , "::" , stringify ! ( mBar ) )); } +extern "C" { + #[link_name = "_ZN13UnionWithDtorD1Ev"] + pub fn UnionWithDtor_UnionWithDtor_destructor(this: *mut UnionWithDtor); +} +impl UnionWithDtor { + #[inline] + pub unsafe fn destruct(&mut self) { + UnionWithDtor_UnionWithDtor_destructor(self) + } +} diff --git a/tests/headers/gen-constructors-neg.hpp b/tests/headers/gen-constructors-neg.hpp new file mode 100644 index 00000000..2dd491c4 --- /dev/null +++ b/tests/headers/gen-constructors-neg.hpp @@ -0,0 +1,6 @@ +// bindgen-flags: --generate types,functions + +class Foo { + public: + Foo(int a); +}; diff --git a/tests/headers/gen-constructors.hpp b/tests/headers/gen-constructors.hpp new file mode 100644 index 00000000..809d6ef9 --- /dev/null +++ b/tests/headers/gen-constructors.hpp @@ -0,0 +1,6 @@ +// bindgen-flags: --generate types,constructors,functions + +class Foo { + public: + Foo(int a); +}; diff --git a/tests/headers/gen-destructors-neg.hpp b/tests/headers/gen-destructors-neg.hpp new file mode 100644 index 00000000..5ede3ba3 --- /dev/null +++ b/tests/headers/gen-destructors-neg.hpp @@ -0,0 +1,9 @@ +// bindgen-flags: --generate types,functions +// +// NB: This is intended to _not_ generate destructors. + +class Foo { + int bar; + public: + ~Foo(); +}; diff --git a/tests/headers/gen-destructors.hpp b/tests/headers/gen-destructors.hpp new file mode 100644 index 00000000..719eb248 --- /dev/null +++ b/tests/headers/gen-destructors.hpp @@ -0,0 +1,7 @@ +// bindgen-flags: --generate types,destructors,functions + +class Foo { + int bar; + public: + ~Foo(); +}; |