diff options
author | Emilio Cobos Álvarez <emilio@crisal.io> | 2016-12-12 16:11:43 +0100 |
---|---|---|
committer | Emilio Cobos Álvarez <emilio@crisal.io> | 2016-12-13 14:33:01 +0100 |
commit | 883ff74e5f20b68b41f322daa582d9208eece65a (patch) | |
tree | 10f0c8aa40dad4a9b48d8a705761074fff9036d3 /libbindgen/src | |
parent | 5d9c48e59b49135db1ecfd4ff8c3dbab8ed05086 (diff) |
Add support for constructors, and integration tests.
Diffstat (limited to 'libbindgen/src')
-rw-r--r-- | libbindgen/src/codegen/helpers.rs | 22 | ||||
-rw-r--r-- | libbindgen/src/codegen/mod.rs | 96 | ||||
-rw-r--r-- | libbindgen/src/ir/comp.rs | 70 | ||||
-rw-r--r-- | libbindgen/src/ir/function.rs | 13 | ||||
-rw-r--r-- | libbindgen/src/ir/item.rs | 23 | ||||
-rw-r--r-- | libbindgen/src/lib.rs | 4 |
6 files changed, 156 insertions, 72 deletions
diff --git a/libbindgen/src/codegen/helpers.rs b/libbindgen/src/codegen/helpers.rs index f1a0f314..c09f0071 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<P<ast::Expr>> { + // 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::<Vec<_>>() + } } diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs index 31946964..f3181de7 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<String, usize>, 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<String, usize>, 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,38 @@ 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::<Vec<_>>(); + 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 `tmp` we're going to create. + if self.is_constructor() { + let tmp_variable_decl = + quote_stmt!(ctx.ext_cx(), let mut tmp = ::std::mem::uninitialized()) + .unwrap(); + stmts.push(tmp_variable_decl); + exprs[0] = quote_expr!(ctx.ext_cx(), &mut tmp); + } else if !self.is_static() { assert!(!exprs.is_empty()); exprs[0] = if self.is_const() { quote_expr!(ctx.ext_cx(), &*self) @@ -1359,14 +1381,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(), 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 d2ace023..c351a152 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<Method>, + /// The different constructors this struct or class contains. + constructors: Vec<ItemId>, + /// Vector of classes this one inherits from. base_members: Vec<ItemId>, @@ -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,10 @@ impl CompInfo { &self.methods } + pub fn constructors(&self) -> &[ItemId] { + &self.constructors + } + /// What kind of compound type is this? pub fn kind(&self) -> CompKind { self.kind @@ -651,14 +667,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 +716,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 +767,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 5a864cfc..c4c26c62 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 6601216e..11ec7d99 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<usize> { + fn overload_index(&self, ctx: &BindgenContext) -> Option<usize> { 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 29580f36..2ff61f90 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, } } } |