diff --git a/.travis.yml b/.travis.yml index 8d7047aa98..df1a515691 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,9 @@ script: - cd ../../../bindgen - cargo test --features "$BINDGEN_FEATURES" - cargo test --release --features "$BINDGEN_FEATURES" + - cd ../bindgen-integration + - cargo test --features "$BINDGEN_FEATURES" + - cargo test --release --features "$BINDGEN_FEATURES" notifications: webhooks: http://build.servo.org:54856/travis diff --git a/Cargo.toml b/Cargo.toml index 0ca737c7f0..106e652be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "bindgen", + "bindgen-integration", "libbindgen", "libbindgen/tests/expectations", ] diff --git a/bindgen-integration/Cargo.toml b/bindgen-integration/Cargo.toml new file mode 100644 index 0000000000..e0687f9959 --- /dev/null +++ b/bindgen-integration/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bindgen-integration" +description = "A package to test various bindgen features" +version = "0.1.0" +authors = ["Emilio Cobos Álvarez "] +workspace = ".." +publish = false +build = "build.rs" + +[build-dependencies] +libbindgen = { path = "../libbindgen" } +gcc = "0.3" + +[features] +llvm_stable = ["libbindgen/llvm_stable"] diff --git a/bindgen-integration/build.rs b/bindgen-integration/build.rs new file mode 100644 index 0000000000..ff8ba172f6 --- /dev/null +++ b/bindgen-integration/build.rs @@ -0,0 +1,28 @@ +extern crate libbindgen; +extern crate gcc; + +use std::env; +use std::path::PathBuf; +use libbindgen::Builder; + +fn main() { + gcc::Config::new() + .cpp(true) + .file("cpp/Test.cc") + .compile("libtest.a"); + + let bindings = Builder::default() + .no_unstable_rust() + .header("cpp/Test.h") + .clang_arg("-x") + .clang_arg("c++") + .clang_arg("-std=c++11") + .generate() + .expect("Unable to generate bindings"); + + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("test.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/bindgen-integration/cpp/Test.cc b/bindgen-integration/cpp/Test.cc new file mode 100644 index 0000000000..d9c13a7671 --- /dev/null +++ b/bindgen-integration/cpp/Test.cc @@ -0,0 +1,15 @@ +#include "Test.h" + +const char* Test::name() { + return "Test"; +} + +Test::Test(int foo) + : m_int(foo) + , m_double(0.0) +{} + +Test::Test(double foo) + : m_int(0) + , m_double(foo) +{} diff --git a/bindgen-integration/cpp/Test.h b/bindgen-integration/cpp/Test.h new file mode 100644 index 0000000000..21f6d1a7bb --- /dev/null +++ b/bindgen-integration/cpp/Test.h @@ -0,0 +1,10 @@ +#pragma once + +class Test { + int m_int; + double m_double; +public: + static const char* name(); + Test(int foo); + Test(double foo); +}; diff --git a/bindgen-integration/src/lib.rs b/bindgen-integration/src/lib.rs new file mode 100644 index 0000000000..4f239510f4 --- /dev/null +++ b/bindgen-integration/src/lib.rs @@ -0,0 +1,28 @@ +mod bindings { + include!(concat!(env!("OUT_DIR"), "/test.rs")); +} + +use std::ffi::CStr; + +#[test] +fn test_static_method() { + let c_str = unsafe { bindings::Test::name() }; + let name = unsafe { + CStr::from_ptr(c_str).to_string_lossy().into_owned() + }; + assert_eq!(name, "Test", "Calling a static C++ method works!"); +} + +#[test] +fn test_constructor() { + let test = unsafe { bindings::Test::new(5) }; + assert_eq!(test.m_int, 5); + assert_eq!(test.m_double, 0.0); +} + +#[test] +fn test_overload() { + let test = unsafe { bindings::Test::new1(5.0) }; + assert_eq!(test.m_int, 0); + assert_eq!(test.m_double, 5.0); +} diff --git a/libbindgen/src/codegen/helpers.rs b/libbindgen/src/codegen/helpers.rs index f1a0f3143f..c09f007160 100644 --- a/libbindgen/src/codegen/helpers.rs +++ b/libbindgen/src/codegen/helpers.rs @@ -75,6 +75,7 @@ impl BlobTyBuilder { pub mod ast_ty { use aster; use ir::context::BindgenContext; + use ir::function::FunctionSig; use ir::ty::FloatKind; use syntax::ast; use syntax::ptr::P; @@ -164,4 +165,25 @@ pub mod ast_ty { let kind = ast::LitKind::FloatUnsuffixed(interned_str); aster::AstBuilder::new().expr().lit().build_lit(kind) } + + pub fn arguments_from_signature(signature: &FunctionSig, + ctx: &BindgenContext) + -> Vec> { + // TODO: We need to keep in sync the argument names, so we should unify + // this with the other loop that decides them. + let mut unnamed_arguments = 0; + signature.argument_types() + .iter() + .map(|&(ref name, _ty)| { + let arg_name = match *name { + Some(ref name) => ctx.rust_mangle(name).into_owned(), + None => { + unnamed_arguments += 1; + format!("arg{}", unnamed_arguments) + } + }; + aster::expr::ExprBuilder::new().id(arg_name) + }) + .collect::>() + } } diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs index 31946964fa..bf516cd25a 100644 --- a/libbindgen/src/codegen/mod.rs +++ b/libbindgen/src/codegen/mod.rs @@ -4,7 +4,7 @@ mod helpers; use aster; use ir::annotations::FieldAccessorKind; -use ir::comp::{CompInfo, CompKind, Field, Method}; +use ir::comp::{CompInfo, CompKind, Field, Method, MethodKind}; use ir::context::{BindgenContext, ItemId}; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Function, FunctionSig}; @@ -1181,15 +1181,28 @@ impl CodeGenerator for CompInfo { result.push(item); } + let mut method_names = Default::default(); if ctx.options().codegen_config.methods { - let mut method_names = Default::default(); for method in self.methods() { + assert!(method.kind() != MethodKind::Constructor); method.codegen_method(ctx, &mut methods, &mut method_names, result, whitelisted_items, - item); + self); + } + } + + if ctx.options().codegen_config.constructors { + for sig in self.constructors() { + Method::new(MethodKind::Constructor, *sig, /* const */ false) + .codegen_method(ctx, + &mut methods, + &mut method_names, + result, + whitelisted_items, + self); } } } @@ -1242,7 +1255,7 @@ trait MethodCodegen { method_names: &mut HashMap, result: &mut CodegenResult<'a>, whitelisted_items: &ItemSet, - parent: &Item); + parent: &CompInfo); } impl MethodCodegen for Method { @@ -1252,18 +1265,21 @@ impl MethodCodegen for Method { method_names: &mut HashMap, result: &mut CodegenResult<'a>, whitelisted_items: &ItemSet, - _parent: &Item) { + _parent: &CompInfo) { if self.is_virtual() { return; // FIXME } // First of all, output the actual function. - ctx.resolve_item(self.signature()) - .codegen(ctx, result, whitelisted_items, &()); - let function_item = ctx.resolve_item(self.signature()); + function_item.codegen(ctx, result, whitelisted_items, &()); + let function = function_item.expect_function(); - let mut name = function.name().to_owned(); let signature_item = ctx.resolve_item(function.signature()); + let mut name = match self.kind() { + MethodKind::Constructor => "new".into(), + _ => function.name().to_owned(), + }; + let signature = match *signature_item.expect_type().kind() { TypeKind::Function(ref sig) => sig, _ => panic!("How in the world?"), @@ -1283,7 +1299,7 @@ impl MethodCodegen for Method { let function_name = function_item.canonical_name(ctx); let mut fndecl = utils::rust_fndecl_from_signature(ctx, signature_item) .unwrap(); - if !self.is_static() { + if !self.is_static() && !self.is_constructor() { let mutability = if self.is_const() { ast::Mutability::Immutable } else { @@ -1319,32 +1335,39 @@ impl MethodCodegen for Method { }; } + // If it's a constructor, we always return `Self`, and we inject the + // "this" parameter, so there's no need to ask the user for it. + // + // Note that constructors in Clang are represented as functions with + // return-type = void. + if self.is_constructor() { + fndecl.inputs.remove(0); + fndecl.output = ast::FunctionRetTy::Ty(quote_ty!(ctx.ext_cx(), Self)); + } + let sig = ast::MethodSig { unsafety: ast::Unsafety::Unsafe, abi: Abi::Rust, - decl: P(fndecl.clone()), + decl: P(fndecl), generics: ast::Generics::default(), constness: respan(ctx.span(), ast::Constness::NotConst), }; - // TODO: We need to keep in sync the argument names, so we should unify - // this with the other loop that decides them. - let mut unnamed_arguments = 0; - let mut exprs = signature.argument_types() - .iter() - .map(|&(ref name, _ty)| { - let arg_name = match *name { - Some(ref name) => ctx.rust_mangle(name).into_owned(), - None => { - unnamed_arguments += 1; - format!("arg{}", unnamed_arguments) - } - }; - aster::expr::ExprBuilder::new().id(arg_name) - }) - .collect::>(); + let mut exprs = + helpers::ast_ty::arguments_from_signature(&signature, ctx); + + let mut stmts = vec![]; - if !self.is_static() { + // If it's a constructor, we need to insert an extra parameter with a + // variable called `__bindgen_tmp` we're going to create. + if self.is_constructor() { + let tmp_variable_decl = + quote_stmt!(ctx.ext_cx(), + let mut __bindgen_tmp = ::std::mem::uninitialized()) + .unwrap(); + stmts.push(tmp_variable_decl); + 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) @@ -1359,14 +1382,18 @@ impl MethodCodegen for Method { .with_args(exprs) .build(); + stmts.push(ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Expr(call), + span: ctx.span(), + }); + + if self.is_constructor() { + stmts.push(quote_stmt!(ctx.ext_cx(), __bindgen_tmp).unwrap()); + } + let block = ast::Block { - stmts: vec![ - ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Expr(call), - span: ctx.span(), - } - ], + stmts: stmts, id: ast::DUMMY_NODE_ID, rules: ast::BlockCheckMode::Default, span: ctx.span(), diff --git a/libbindgen/src/ir/comp.rs b/libbindgen/src/ir/comp.rs index d2ace023eb..cdba2bfbd2 100644 --- a/libbindgen/src/ir/comp.rs +++ b/libbindgen/src/ir/comp.rs @@ -23,6 +23,9 @@ pub enum CompKind { /// The kind of C++ method. #[derive(Debug, Copy, Clone, PartialEq)] pub enum MethodKind { + /// A constructor. We represent it as method for convenience, to avoid code + /// duplication. + Constructor, /// A static method. Static, /// A normal method. @@ -45,7 +48,7 @@ pub struct Method { impl Method { /// Construct a new `Method`. - fn new(kind: MethodKind, signature: ItemId, is_const: bool) -> Self { + pub fn new(kind: MethodKind, signature: ItemId, is_const: bool) -> Self { Method { kind: kind, signature: signature, @@ -58,6 +61,11 @@ impl Method { self.kind } + /// Is this a constructor? + pub fn is_constructor(&self) -> bool { + self.kind == MethodKind::Constructor + } + /// Is this a virtual method? pub fn is_virtual(&self) -> bool { self.kind == MethodKind::Virtual @@ -167,6 +175,9 @@ pub struct CompInfo { /// The method declarations inside this class, if in C++ mode. methods: Vec, + /// The different constructors this struct or class contains. + constructors: Vec, + /// Vector of classes this one inherits from. base_members: Vec, @@ -235,6 +246,7 @@ impl CompInfo { fields: vec![], template_args: vec![], methods: vec![], + constructors: vec![], base_members: vec![], ref_template: None, inner_types: vec![], @@ -454,6 +466,11 @@ impl CompInfo { &self.methods } + /// Get this type's set of constructors. + pub fn constructors(&self) -> &[ItemId] { + &self.constructors + } + /// What kind of compound type is this? pub fn kind(&self) -> CompKind { self.kind @@ -651,14 +668,15 @@ impl CompInfo { .expect("BaseSpecifier"); ci.base_members.push(type_id); } + CXCursor_Constructor | + CXCursor_Destructor | CXCursor_CXXMethod => { let is_virtual = cur.method_is_virtual(); let is_static = cur.method_is_static(); debug_assert!(!(is_static && is_virtual), "How?"); - if !ci.has_vtable { - ci.has_vtable = is_virtual; - } + ci.has_destructor |= cur.kind() == CXCursor_Destructor; + ci.has_vtable |= is_virtual; let linkage = cur.linkage(); if linkage != CXLinkage_External { @@ -699,30 +717,34 @@ impl CompInfo { // NB: This gets us an owned `Function`, not a // `FunctionSig`. - let method_signature = + let signature = Item::parse(cur, Some(potential_id), ctx) .expect("CXXMethod"); - let is_const = cur.method_is_const(); - let method_kind = if is_static { - MethodKind::Static - } else if is_virtual { - MethodKind::Virtual - } else { - MethodKind::Normal - }; - - let method = - Method::new(method_kind, method_signature, is_const); - - ci.methods.push(method); - } - CXCursor_Destructor => { - if cur.method_is_virtual() { - // FIXME: Push to the method list? - ci.has_vtable = true; + match cur.kind() { + CXCursor_Constructor => { + ci.constructors.push(signature); + } + // TODO(emilio): Bind the destructor? + CXCursor_Destructor => {}, + CXCursor_CXXMethod => { + let is_const = cur.method_is_const(); + let method_kind = if is_static { + MethodKind::Static + } else if is_virtual { + MethodKind::Virtual + } else { + MethodKind::Normal + }; + + let method = Method::new(method_kind, + signature, + is_const); + + ci.methods.push(method); + } + _ => unreachable!("How can we see this here?"), } - ci.has_destructor = true; } CXCursor_NonTypeTemplateParameter => { ci.has_non_type_template_params = true; @@ -746,7 +768,6 @@ impl CompInfo { // Intentionally not handled CXCursor_CXXAccessSpecifier | CXCursor_CXXFinalAttr | - CXCursor_Constructor | CXCursor_FunctionTemplate | CXCursor_ConversionFunction => {} _ => { diff --git a/libbindgen/src/ir/function.rs b/libbindgen/src/ir/function.rs index 5a864cfc7b..c4c26c620f 100644 --- a/libbindgen/src/ir/function.rs +++ b/libbindgen/src/ir/function.rs @@ -147,6 +147,7 @@ impl FunctionSig { }; let mut args: Vec<_> = match cursor.kind() { CXCursor_FunctionDecl | + CXCursor_Constructor | CXCursor_CXXMethod => { // For CXCursor_FunctionDecl, cursor.args() is the reliable way // to get parameter names and types. @@ -184,10 +185,12 @@ impl FunctionSig { } }; - if cursor.kind() == CXCursor_CXXMethod { - let is_const = cursor.method_is_const(); - let is_virtual = cursor.method_is_virtual(); - let is_static = cursor.method_is_static(); + let is_method = cursor.kind() == CXCursor_CXXMethod; + + if is_method || cursor.kind() == CXCursor_Constructor { + 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(); if !is_static && !is_virtual { let class = Item::parse(cursor.semantic_parent(), None, ctx) .expect("Expected to parse the class"); @@ -240,6 +243,8 @@ impl ClangSubItemParser for Function { use clangll::*; match cursor.kind() { CXCursor_FunctionDecl | + CXCursor_Constructor | + CXCursor_Destructor | CXCursor_CXXMethod => {} _ => return Err(ParseError::Continue), }; diff --git a/libbindgen/src/ir/item.rs b/libbindgen/src/ir/item.rs index 6601216eb9..11ec7d99b5 100644 --- a/libbindgen/src/ir/item.rs +++ b/libbindgen/src/ir/item.rs @@ -654,19 +654,26 @@ impl Item { /// Get the overload index for this method. If this is not a method, return /// `None`. - fn method_overload_index(&self, ctx: &BindgenContext) -> Option { + fn overload_index(&self, ctx: &BindgenContext) -> Option { self.func_name().and_then(|func_name| { let parent = ctx.resolve_item(self.parent_id()); if let ItemKind::Type(ref ty) = *parent.kind() { if let TypeKind::Comp(ref ci) = *ty.kind() { - return ci.methods() + // All the constructors have the same name, so no need to + // resolve and check. + return ci.constructors() .iter() - .filter(|method| { - let item = ctx.resolve_item(method.signature()); - let func = item.expect_function(); - func.name() == func_name + .position(|c| *c == self.id()) + .or_else(|| { + ci.methods() + .iter() + .filter(|m| { + let item = ctx.resolve_item(m.signature()); + let func = item.expect_function(); + func.name() == func_name + }) + .position(|m| m.signature() == self.id()) }) - .position(|method| method.signature() == self.id()); } } @@ -704,7 +711,7 @@ impl Item { ItemKind::Function(ref fun) => { let mut name = fun.name().to_owned(); - if let Some(idx) = self.method_overload_index(ctx) { + if let Some(idx) = self.overload_index(ctx) { if idx > 0 { write!(&mut name, "{}", idx).unwrap(); } diff --git a/libbindgen/src/lib.rs b/libbindgen/src/lib.rs index 29580f3682..2ff61f901b 100644 --- a/libbindgen/src/lib.rs +++ b/libbindgen/src/lib.rs @@ -108,6 +108,8 @@ pub struct CodegenConfig { pub vars: bool, /// Whether to generate methods. pub methods: bool, + /// Whether to generate constructors. + pub constructors: bool, } impl CodegenConfig { @@ -118,6 +120,7 @@ impl CodegenConfig { types: true, vars: true, methods: true, + constructors: true, } } @@ -128,6 +131,7 @@ impl CodegenConfig { types: false, vars: false, methods: false, + constructors: false, } } } diff --git a/libbindgen/tests/expectations/tests/constructors.rs b/libbindgen/tests/expectations/tests/constructors.rs new file mode 100644 index 0000000000..95afb82da8 --- /dev/null +++ b/libbindgen/tests/expectations/tests/constructors.rs @@ -0,0 +1,67 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy)] +pub struct TestOverload { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_TestOverload() { + assert_eq!(::std::mem::size_of::() , 1usize); + assert_eq!(::std::mem::align_of::() , 1usize); +} +extern "C" { + #[link_name = "_ZN12TestOverloadC1Ei"] + pub fn TestOverload_TestOverload(this: *mut TestOverload, + arg1: ::std::os::raw::c_int); +} +extern "C" { + #[link_name = "_ZN12TestOverloadC1Ed"] + pub fn TestOverload_TestOverload1(this: *mut TestOverload, arg1: f64); +} +impl Clone for TestOverload { + fn clone(&self) -> Self { *self } +} +impl TestOverload { + #[inline] + pub unsafe fn new(arg1: ::std::os::raw::c_int) -> Self { + let mut __bindgen_tmp = ::std::mem::uninitialized(); + TestOverload_TestOverload(&mut __bindgen_tmp, arg1); + __bindgen_tmp + } + #[inline] + pub unsafe fn new1(arg1: f64) -> Self { + let mut __bindgen_tmp = ::std::mem::uninitialized(); + TestOverload_TestOverload1(&mut __bindgen_tmp, arg1); + __bindgen_tmp + } +} +#[repr(C)] +#[derive(Debug, Copy)] +pub struct TestPublicNoArgs { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_TestPublicNoArgs() { + assert_eq!(::std::mem::size_of::() , 1usize); + assert_eq!(::std::mem::align_of::() , 1usize); +} +extern "C" { + #[link_name = "_ZN16TestPublicNoArgsC1Ev"] + pub fn TestPublicNoArgs_TestPublicNoArgs(this: *mut TestPublicNoArgs); +} +impl Clone for TestPublicNoArgs { + fn clone(&self) -> Self { *self } +} +impl TestPublicNoArgs { + #[inline] + pub unsafe fn new() -> Self { + let mut __bindgen_tmp = ::std::mem::uninitialized(); + TestPublicNoArgs_TestPublicNoArgs(&mut __bindgen_tmp); + __bindgen_tmp + } +} diff --git a/libbindgen/tests/headers/constructors.hpp b/libbindgen/tests/headers/constructors.hpp new file mode 100644 index 0000000000..d417488917 --- /dev/null +++ b/libbindgen/tests/headers/constructors.hpp @@ -0,0 +1,13 @@ + +class TestOverload { + // This one shouldnt' be generated. + TestOverload(); +public: + TestOverload(int); + TestOverload(double); +}; + +class TestPublicNoArgs { +public: + TestPublicNoArgs(); +};