diff --git a/build.rs b/build.rs index f00c9c26de..b511381089 100644 --- a/build.rs +++ b/build.rs @@ -137,6 +137,7 @@ mod clang_ast { } } + println!("cargo:rerun-if-changed=src/clang/CMakeLists.txt"); println!("cargo:rerun-if-changed=src/clang/clang_interface.hpp"); println!("cargo:rerun-if-changed=src/clang/clang_interface_impl.hpp"); println!("cargo:rerun-if-changed=src/clang/clang_interface.cpp"); diff --git a/src/clang.rs b/src/clang.rs index 292ed7835c..4e7d36f4ff 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -7,6 +7,7 @@ use cexpr; use ir::context::BindgenContext; use regex; +use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::fmt; use std::hash::Hash; @@ -717,6 +718,25 @@ impl Cursor { found } + /// Visit all direct and indirect virtual bases of this class. + pub fn visit_virtual_bases(&self, mut visitor: Visitor) + where + Visitor: FnMut(Cursor) -> CXChildVisitResult, + { + match self.node { + ASTNode::Decl(d) => unsafe { + clang_interface::CXXRecordDecl_visitVBases( + d, + self.kind, + Some(visit_children::), + self.unit, + mem::transmute(&mut visitor), + ) + }, + _ => {} + } + } + /// Is the referent an inlined function? pub fn is_inlined_function(&self) -> bool { match self.node { @@ -992,6 +1012,81 @@ impl Cursor { } } + pub fn get_primary_base(&self) -> Option { + match self.node { + ASTNode::Decl(d) => unsafe { + let base = clang_interface::CXXRecordDecl_getPrimaryBase(d, self.context()); + if !base.is_null() { + return Some(self.with_node(ASTNode::Decl(base))); + } + }, + _ => {} + } + None + } + + pub fn get_vtable_components(&self) -> Option { + match self.node { + ASTNode::Decl(d) => unsafe { + let layout = clang_interface::CXXRecordDecl_getVTableLayout(d, self.context()); + if !layout.is_null() { + let length = clang_interface::VTableLayout_componentCount(layout); + return Some(VTableComponentIterator { + layout, + unit: self.unit, + length, + index: 0, + }); + } + }, + _ => {} + } + None + } + + pub fn get_secondary_vtables(&self) -> Vec<(usize, Cursor)> { + match self.node { + ASTNode::Decl(d) => unsafe { + let layout = clang_interface::CXXRecordDecl_getVTableLayout(d, self.context()); + if !layout.is_null() { + let mut secondary_vtables = vec![]; + let count = clang_interface::VTableLayout_getNumVTables(layout); + for i in 1..count { + let index = clang_interface::VTableLayout_getVTableOffset(layout, i); + let base = self.with_node(ASTNode::Decl( + clang_interface::VTableLayout_getVTableBase(layout, i) + )); + secondary_vtables.push((index as usize, base)); + } + return secondary_vtables; + } + }, + _ => {} + } + vec![] + } + + /// Is this cursor's referent a dynamic class (i.e. has a virtual pointer)? + pub fn is_dynamic_class(&self) -> bool { + match self.node { + ASTNode::Decl(d) => unsafe { + clang_interface::Decl_isDynamicClass(d) + }, + _ => false, + } + } + + pub fn base_offset(&self, base: &Cursor) -> Option { + match (self.node, base.node) { + (ASTNode::Decl(d), ASTNode::CXXBaseSpecifier(b)) => unsafe { + clang_interface::CXXRecordDecl_baseClassOffset(d, b, self.context()) + .try_into() + .ok() + }, + _ => None, + } + } + /// Try to evaluate this cursor. pub fn evaluate(&self) -> Option { // Work around https://bugs.llvm.org/show_bug.cgi?id=42532, see: @@ -1724,6 +1819,86 @@ impl ExactSizeIterator for TypeTemplateArgIterator { } } +pub enum VTableComponent { + VCallOffset(i64), + VBaseOffset(i64), + OffsetToTop(i64), + RTTI(Cursor), + FunctionPointer(Cursor), + CompleteDtorPointer(Cursor), + DeletingDtorPointer(Cursor), + UnusedFunctionPointer(Cursor), +} + +impl VTableComponent { + fn new( + component: *const clang_interface::clang_VTableComponent, + unit: *mut clang_interface::clang_ASTUnit, + ) -> Self { + assert!(!component.is_null()); + use self::clang_interface::VTableComponentKind; + let kind = unsafe { clang_interface::VTableComponent_getKind(component) }; + match kind { + VTableComponentKind::CK_VCallOffset => { + let offset = unsafe { clang_interface::VTableComponent_getOffset(component) }; + VTableComponent::VCallOffset(offset) + } + VTableComponentKind::CK_VBaseOffset => { + let offset = unsafe { clang_interface::VTableComponent_getOffset(component) }; + VTableComponent::VBaseOffset(offset) + } + VTableComponentKind::CK_OffsetToTop => { + let offset = unsafe { clang_interface::VTableComponent_getOffset(component) }; + VTableComponent::OffsetToTop(offset) + } + VTableComponentKind::CK_RTTI => { + let decl = unsafe { clang_interface::VTableComponent_getDecl(component) }; + VTableComponent::RTTI(Cursor::new(ASTNode::Decl(decl), unit)) + } + VTableComponentKind::CK_FunctionPointer => { + let decl = unsafe { clang_interface::VTableComponent_getDecl(component) }; + VTableComponent::FunctionPointer(Cursor::new(ASTNode::Decl(decl), unit)) + } + VTableComponentKind::CK_CompleteDtorPointer => { + let decl = unsafe { clang_interface::VTableComponent_getDecl(component) }; + VTableComponent::CompleteDtorPointer(Cursor::new(ASTNode::Decl(decl), unit)) + } + VTableComponentKind::CK_DeletingDtorPointer => { + let decl = unsafe { clang_interface::VTableComponent_getDecl(component) }; + VTableComponent::DeletingDtorPointer(Cursor::new(ASTNode::Decl(decl), unit)) + } + VTableComponentKind::CK_UnusedFunctionPointer => { + let decl = unsafe { clang_interface::VTableComponent_getDecl(component) }; + VTableComponent::UnusedFunctionPointer(Cursor::new(ASTNode::Decl(decl), unit)) + } + _ => unreachable!("Invalid VTableComponent Kind: {}", kind), + } + } +} + +pub struct VTableComponentIterator { + layout: *const clang_interface::clang_VTableLayout, + unit: *mut clang_interface::clang_ASTUnit, + length: u32, + index: u32, +} + +impl Iterator for VTableComponentIterator { + type Item = VTableComponent; + fn next(&mut self) -> Option { + if self.index < self.length { + let component = unsafe { + clang_interface::VTableLayout_getComponent(self.layout, self.index) + }; + self.index += 1; + Some(VTableComponent::new(component, self.unit)) + } else { + None + } + } +} + + /// A `SourceLocation` is a file, line, column, and byte offset location for /// some source text. pub struct SourceLocation { diff --git a/src/clang/CMakeLists.txt b/src/clang/CMakeLists.txt index 391a270414..9ba74073d7 100644 --- a/src/clang/CMakeLists.txt +++ b/src/clang/CMakeLists.txt @@ -99,6 +99,7 @@ function(target_link_libs_recursive out_libs target) list(FIND visited_targets ${dep} visited) if (${visited} EQUAL -1) list(APPEND visited_targets ${dep}) + set(visited_targets ${visited_targets} PARENT_SCOPE) if (TARGET ${dep}) set(dependencies "") target_link_libs_recursive(dependencies ${dep}) @@ -107,7 +108,6 @@ function(target_link_libs_recursive out_libs target) list(APPEND libs ${dep}) endif() endforeach(dep) - set(visited_targets ${visited_targets} PARENT_SCOPE) set(${out_libs} ${libs} PARENT_SCOPE) endif() endfunction() @@ -115,6 +115,7 @@ endfunction() if (Clang_FOUND) target_link_libs_recursive(link_libs BindgenClangInterface) + list(REVERSE link_libs) file(WRITE "${CMAKE_INSTALL_PREFIX}/BindgenClangInterface.deps" "${link_libs}") else() # we didn't find clang cmake files file(REMOVE "${CMAKE_INSTALL_PREFIX}/BindgenClangInterface.deps") diff --git a/src/clang/clang_interface.cpp b/src/clang/clang_interface.cpp index baebf15a83..ffd1cc468e 100644 --- a/src/clang/clang_interface.cpp +++ b/src/clang/clang_interface.cpp @@ -1,3 +1,4 @@ +#include #include #include "clang/AST/Comment.h" @@ -9,6 +10,8 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/VTableBuilder.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" @@ -529,6 +532,16 @@ BindgenQualType Decl_getResultType(const Decl *D, ASTContext *Ctx) { return Type_getResultType(Decl_getType(D, Ctx)); } +bool Decl_isDynamicClass(const Decl *D) { + if (auto *ClassTemplate = dyn_cast_or_null(D)) + D = ClassTemplate->getTemplatedDecl(); + if (auto *C = dyn_cast_or_null(D)) { + auto *Def = C->getDefinition(); + return Def && Def->isDynamicClass(); + } + return false; +} + BindgenStringRef Expr_getSpelling(const Expr *E) { if (auto *SL = dyn_cast_or_null(&*E)) { SmallString<256> Buf; @@ -1483,6 +1496,21 @@ void CXXBaseSpecifier_visitChildren(const CXXBaseSpecifier *Parent, visitor.TraverseTypeLoc(Parent->getTypeSourceInfo()->getTypeLoc()); #endif } +void CXXRecordDecl_visitVBases(const Decl *D, CXCursorKind kind, Visitor VisitFn, + ASTUnit *Unit, CXClientData data) { + if (auto *ClassTemplate = dyn_cast_or_null(D)) + D = ClassTemplate->getTemplatedDecl(); + auto *RD = dyn_cast_or_null(D); + if (RD) + RD = RD->getDefinition(); + if (!RD) + return; + + for (auto &Base : RD->vbases()) { + if (VisitFn(Node(&Base), Node(D, kind), Unit, data) == CXChildVisit_Break) + return; + } +} void disposeTokens(const ASTUnit *TU, CXToken *Tokens, unsigned NumTokens) { delete[] Tokens; @@ -1679,6 +1707,151 @@ SourceLocation *CXXBaseSpecifier_getLocation(const CXXBaseSpecifier *B) { #endif } +static const CXXRecordDecl *GetCXXRecordDeclForLayout(const Decl *D, ASTContext *Context) { + if (auto *ClassTemplate = dyn_cast_or_null(D)) + D = ClassTemplate->getTemplatedDecl(); + auto *RD = dyn_cast_or_null(D); + if (RD) + RD = RD->getDefinition(); + if (!RD || RD->isInvalidDecl() || !RD->isCompleteDefinition() || RD->hasAnyDependentBases()) + return nullptr; + + // If this is a templated class but not an instantiation, then we can't lay it + // out. + auto RT = Context->getRecordType(RD); + if (RT->isDependentType() && !RT->getAs()) + return nullptr; + + return RD; +} + +int64_t CXXRecordDecl_baseClassOffset(const Decl *D, const CXXBaseSpecifier *B, ASTContext *Context) { + auto *RD = GetCXXRecordDeclForLayout(D, Context); + if (!RD) + return -1; + const ASTRecordLayout &Layout = Context->getASTRecordLayout(RD); + + if (B->isVirtual()) + return Layout.getVBaseClassOffset(B->getType()->getAsCXXRecordDecl()).getQuantity(); + else + return Layout.getBaseClassOffset(B->getType()->getAsCXXRecordDecl()).getQuantity(); +} + +const Decl *CXXRecordDecl_getPrimaryBase(const Decl *D, ASTContext *Context) { + auto *RD = GetCXXRecordDeclForLayout(D, Context); + if (!RD) + return nullptr; + const ASTRecordLayout &Layout = Context->getASTRecordLayout(RD); + return Layout.getPrimaryBase(); +} + +static void VTableLayout_dump(const VTableLayout *Layout) { + for (auto &AP : Layout->getAddressPoints()) { + llvm::errs() << AP.first.getBase()->getName() << ": "; + llvm::errs() << AP.second.VTableIndex << ", " << AP.second.AddressPointIndex << "\n"; + } + for (size_t i = 0, e = Layout->getNumVTables(); i < e; ++i) { + llvm::errs() << Layout->getVTableOffset(i) << ", "; + } + llvm::errs() << "\n\n"; +} + +const VTableLayout *CXXRecordDecl_getVTableLayout(const Decl *D, ASTContext *Context) { + auto *RD = GetCXXRecordDeclForLayout(D, Context); + if (!RD) + return nullptr; + auto *VTContext = dyn_cast(Context->getVTableContext()); + + // We don't know how to handle microsoft vtables yet. + if (!VTContext) + return nullptr; + + return &VTContext->getVTableLayout(RD); +} + +unsigned VTableLayout_componentCount(const VTableLayout *Layout) { + if (!Layout) + return 0; + + return Layout->vtable_components().size(); +} + +size_t VTableLayout_getNumVTables(const VTableLayout *Layout) { + if (!Layout) + return 0; + return Layout->getNumVTables(); +} + +size_t VTableLayout_getVTableOffset(const VTableLayout *Layout, size_t i) { + if (!Layout) + return 0; + return Layout->getVTableOffset(i); +} + +const Decl *VTableLayout_getVTableBase(const VTableLayout *Layout, size_t index) { + if (!Layout) + return nullptr; + + for (auto &AP : Layout->getAddressPoints()) { + if (AP.second.VTableIndex == index) { + return AP.first.getBase(); + } + } + + return nullptr; +} + +const VTableComponent *VTableLayout_getComponent(const VTableLayout *Layout, unsigned Index) { + if (!Layout || Index >= Layout->vtable_components().size()) + return nullptr; + + return &Layout->vtable_components()[Index]; +} + +enum VTableComponentKind VTableComponent_getKind(const VTableComponent *C) { + assert(C && "Null VTableComponent"); + switch (C->getKind()) { + case VTableComponent::CK_VCallOffset: return CK_VCallOffset; + case VTableComponent::CK_VBaseOffset: return CK_VBaseOffset; + case VTableComponent::CK_OffsetToTop: return CK_OffsetToTop; + case VTableComponent::CK_RTTI: return CK_RTTI; + case VTableComponent::CK_FunctionPointer: return CK_FunctionPointer; + case VTableComponent::CK_CompleteDtorPointer: return CK_CompleteDtorPointer; + case VTableComponent::CK_DeletingDtorPointer: return CK_DeletingDtorPointer; + case VTableComponent::CK_UnusedFunctionPointer: return CK_UnusedFunctionPointer; + } + return CK_Invalid; +} + +int64_t VTableComponent_getOffset(const VTableComponent *C) { + assert(C && "Null VTableComponent"); + switch (C->getKind()) { + case VTableComponent::CK_VCallOffset: return C->getVCallOffset().getQuantity(); + case VTableComponent::CK_VBaseOffset: return C->getVBaseOffset().getQuantity(); + case VTableComponent::CK_OffsetToTop: return C->getOffsetToTop().getQuantity(); + default: return std::numeric_limits::min(); + } +} + +const Decl *VTableComponent_getDecl(const VTableComponent *C) { + if (!C) + return nullptr; + + switch (C->getKind()) { + case VTableComponent::CK_FunctionPointer: + case VTableComponent::CK_CompleteDtorPointer: + case VTableComponent::CK_DeletingDtorPointer: + case VTableComponent::CK_UnusedFunctionPointer: + return C->getGlobalDecl().getDecl(); + + case VTableComponent::CK_RTTI: + return C->getRTTIDecl(); + + default: + return nullptr; + } +} + SourceLocation *Attr_getLocation(const Attr *A) { return new SourceLocation(A->getLocation()); } diff --git a/src/clang/clang_interface.hpp b/src/clang/clang_interface.hpp index 688c58f683..c3e529c104 100644 --- a/src/clang/clang_interface.hpp +++ b/src/clang/clang_interface.hpp @@ -22,6 +22,8 @@ struct ASTContext; struct SourceRange; struct Attr; struct PreprocessedEntity; +struct VTableLayout; +struct VTableComponent; namespace comments { struct Comment; @@ -34,6 +36,30 @@ using namespace clang; extern "C" { +enum VTableComponentKind { + CK_VCallOffset, + CK_VBaseOffset, + CK_OffsetToTop, + CK_RTTI, + CK_FunctionPointer, + + /// A pointer to the complete destructor. + CK_CompleteDtorPointer, + + /// A pointer to the deleting destructor. + CK_DeletingDtorPointer, + + /// An entry that is never used. + /// + /// In some cases, a vtable function pointer will end up never being + /// called. Such vtable function pointers are represented as a + /// CK_UnusedFunctionPointer. + CK_UnusedFunctionPointer, + + /// Error value indicating that this component's kind could not be retreived. + CK_Invalid, +}; + struct EvalResult; struct BindgenStringRef { @@ -144,6 +170,7 @@ bool CXXMethod_isConst(const Decl *D); bool CXXMethod_isVirtual(const Decl *D); bool CXXMethod_isPureVirtual(const Decl *D); BindgenQualType Decl_getResultType(const Decl *D, ASTContext *); +bool Decl_isDynamicClass(const Decl *); const Expr *Expr_getArgument(const Expr *E, unsigned i); @@ -208,6 +235,8 @@ void Expr_visitChildren(const Expr *Parent, CXCursorKind kind, Visitor V, void CXXBaseSpecifier_visitChildren(const CXXBaseSpecifier *Parent, CXCursorKind kind, Visitor V, ASTUnit *Unit, CXClientData data); +void CXXRecordDecl_visitVBases(const Decl *Parent, CXCursorKind kind, Visitor V, + ASTUnit *Unit, CXClientData data); void tokenize(ASTUnit *TU, BindgenSourceRange Range, CXToken **Tokens, unsigned *NumTokens); @@ -256,6 +285,18 @@ bool CXXBaseSpecifier_isVirtualBase(const CXXBaseSpecifier *); BindgenQualType CXXBaseSpecifier_getType(const CXXBaseSpecifier *); BindgenStringRef CXXBaseSpecifier_getSpelling(const CXXBaseSpecifier *); SourceLocation *CXXBaseSpecifier_getLocation(const CXXBaseSpecifier *); +int64_t CXXRecordDecl_baseClassOffset(const Decl *, const CXXBaseSpecifier *, ASTContext *); +const Decl *CXXRecordDecl_getPrimaryBase(const Decl *, ASTContext *); +const VTableLayout *CXXRecordDecl_getVTableLayout(const Decl *, ASTContext *); +unsigned VTableLayout_componentCount(const VTableLayout *); +size_t VTableLayout_getNumVTables(const VTableLayout *); +size_t VTableLayout_getVTableOffset(const VTableLayout *, size_t index); +const Decl *VTableLayout_getVTableBase(const VTableLayout *, size_t index); +const VTableComponent *VTableLayout_getComponent(const VTableLayout *, unsigned); +enum VTableComponentKind VTableComponent_getKind(const VTableComponent *); +int64_t VTableComponent_getOffset(const VTableComponent *); +const Decl *VTableComponent_getDecl(const VTableComponent *); + SourceLocation *Attr_getLocation(const Attr *); SourceLocation *PreprocessedEntity_getLocation(const PreprocessedEntity *); const FileEntry *PreprocessedEntity_getIncludedFile(const PreprocessedEntity *); diff --git a/src/clang/clang_interface.rs b/src/clang/clang_interface.rs index 7b3f330fb5..b58355e290 100644 --- a/src/clang/clang_interface.rs +++ b/src/clang/clang_interface.rs @@ -90,7 +90,7 @@ where pub const _GLIBCXX_CSTDINT: u32 = 1; pub const _GLIBCXX_CXX_CONFIG_H: u32 = 1; pub const _GLIBCXX_RELEASE: u32 = 9; -pub const __GLIBCXX__: u32 = 20200130; +pub const __GLIBCXX__: u32 = 20200312; pub const _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY: u32 = 1; pub const _GLIBCXX_USE_DEPRECATED: u32 = 1; pub const _GLIBCXX_EXTERN_TEMPLATE: u32 = 1; @@ -6643,6 +6643,16 @@ pub struct clang_PreprocessedEntity { } #[repr(C)] #[derive(Debug, Copy, Clone)] +pub struct clang_VTableLayout { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct clang_VTableComponent { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] pub struct clang_comments_Comment { _unused: [u8; 0], } @@ -6651,6 +6661,26 @@ pub struct clang_comments_Comment { pub struct clang_comments_FullComment { _unused: [u8; 0], } +pub mod VTableComponentKind { + pub type Type = u32; + pub const CK_VCallOffset: Type = 0; + pub const CK_VBaseOffset: Type = 1; + pub const CK_OffsetToTop: Type = 2; + pub const CK_RTTI: Type = 3; + pub const CK_FunctionPointer: Type = 4; + #[doc = " A pointer to the complete destructor."] + pub const CK_CompleteDtorPointer: Type = 5; + #[doc = " A pointer to the deleting destructor."] + pub const CK_DeletingDtorPointer: Type = 6; + #[doc = " An entry that is never used."] + #[doc = ""] + #[doc = " In some cases, a vtable function pointer will end up never being"] + #[doc = " called. Such vtable function pointers are represented as a"] + #[doc = " CK_UnusedFunctionPointer."] + pub const CK_UnusedFunctionPointer: Type = 7; + #[doc = " Error value indicating that this component's kind could not be retreived."] + pub const CK_Invalid: Type = 8; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct EvalResult { @@ -7058,6 +7088,9 @@ extern "C" { arg1: *mut clang_ASTContext, ) -> BindgenQualType; } +extern "C" { + pub fn Decl_isDynamicClass(arg1: *const clang_Decl) -> bool; +} extern "C" { pub fn Expr_getArgument( E: *const clang_Expr, @@ -7265,6 +7298,15 @@ extern "C" { data: CXClientData, ); } +extern "C" { + pub fn CXXRecordDecl_visitVBases( + Parent: *const clang_Decl, + kind: CXCursorKind::Type, + V: Visitor, + Unit: *mut clang_ASTUnit, + data: CXClientData, + ); +} extern "C" { pub fn tokenize( TU: *mut clang_ASTUnit, @@ -7451,6 +7493,67 @@ extern "C" { arg1: *const clang_CXXBaseSpecifier, ) -> *mut clang_SourceLocation; } +extern "C" { + pub fn CXXRecordDecl_baseClassOffset( + arg1: *const clang_Decl, + arg2: *const clang_CXXBaseSpecifier, + arg3: *mut clang_ASTContext, + ) -> i64; +} +extern "C" { + pub fn CXXRecordDecl_getPrimaryBase( + arg1: *const clang_Decl, + arg2: *mut clang_ASTContext, + ) -> *const clang_Decl; +} +extern "C" { + pub fn CXXRecordDecl_getVTableLayout( + arg1: *const clang_Decl, + arg2: *mut clang_ASTContext, + ) -> *const clang_VTableLayout; +} +extern "C" { + pub fn VTableLayout_componentCount( + arg1: *const clang_VTableLayout, + ) -> ::std::os::raw::c_uint; +} +extern "C" { + pub fn VTableLayout_getNumVTables( + arg1: *const clang_VTableLayout, + ) -> size_t; +} +extern "C" { + pub fn VTableLayout_getVTableOffset( + arg1: *const clang_VTableLayout, + index: size_t, + ) -> size_t; +} +extern "C" { + pub fn VTableLayout_getVTableBase( + arg1: *const clang_VTableLayout, + index: size_t, + ) -> *const clang_Decl; +} +extern "C" { + pub fn VTableLayout_getComponent( + arg1: *const clang_VTableLayout, + arg2: ::std::os::raw::c_uint, + ) -> *const clang_VTableComponent; +} +extern "C" { + pub fn VTableComponent_getKind( + arg1: *const clang_VTableComponent, + ) -> VTableComponentKind::Type; +} +extern "C" { + pub fn VTableComponent_getOffset(arg1: *const clang_VTableComponent) + -> i64; +} +extern "C" { + pub fn VTableComponent_getDecl( + arg1: *const clang_VTableComponent, + ) -> *const clang_Decl; +} extern "C" { pub fn Attr_getLocation( arg1: *const clang_Attr, diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 9a3b10d70b..652bb90039 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -19,10 +19,10 @@ use ir::analysis::{HasVtable, Sizedness}; use ir::annotations::FieldAccessorKind; use ir::comment; use ir::comp::{ - Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, - FieldMethods, Method, MethodKind, + BaseKind, Bitfield, BitfieldUnit, CompInfo, CompKind, Field, + FieldData, FieldMethods, Method, MethodKind, VTableEntry, }; -use ir::context::{BindgenContext, ItemId}; +use ir::context::{BindgenContext, ItemId, TypeId}; use ir::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, @@ -137,6 +137,10 @@ struct CodegenResult<'a> { functions_seen: HashSet, vars_seen: HashSet, + /// VTables that we have generated, indexed by ID of their corresponding + /// class item. + vtables_seen: HashSet, + /// Used for making bindings to overloaded functions. Maps from a canonical /// function name to the number of overloads we have already codegen'd for /// that name. This lets us give each overload a unique suffix. @@ -156,6 +160,7 @@ impl<'a> CodegenResult<'a> { items_seen: Default::default(), functions_seen: Default::default(), vars_seen: Default::default(), + vtables_seen: Default::default(), overload_counters: Default::default(), } } @@ -214,6 +219,14 @@ impl<'a> CodegenResult<'a> { self.vars_seen.insert(name.into()); } + fn seen_vtable(&self, name: &str) -> bool { + self.vtables_seen.contains(name) + } + + fn saw_vtable(&mut self, name: &str) { + self.vtables_seen.insert(name.into()); + } + fn inner(&mut self, cb: F) -> Vec where F: FnOnce(&mut Self), @@ -877,23 +890,18 @@ impl CodeGenerator for Type { } struct Vtable<'a> { + comp_info: &'a CompInfo, item_id: ItemId, - #[allow(dead_code)] - methods: &'a [Method], - #[allow(dead_code)] - base_classes: &'a [Base], } impl<'a> Vtable<'a> { fn new( + comp_info: &'a CompInfo, item_id: ItemId, - methods: &'a [Method], - base_classes: &'a [Base], ) -> Self { Vtable { - item_id: item_id, - methods: methods, - base_classes: base_classes, + comp_info, + item_id, } } } @@ -907,16 +915,122 @@ impl<'a> CodeGenerator for Vtable<'a> { result: &mut CodegenResult<'b>, item: &Item, ) { - assert_eq!(item.id(), self.item_id); debug_assert!(item.is_enabled_for_codegen(ctx)); + let canonical_name = self.canonical_name(ctx); + + if result.seen_vtable(&canonical_name) { + return; + } + result.saw_vtable(&canonical_name); + + let vtable = match self.comp_info.vtable() { + Some(vtable) => vtable, + None => return, + }; + // For now, generate an empty struct, later we should generate function // pointers and whatnot. - let name = ctx.rust_ident(&self.canonical_name(ctx)); - let void = helpers::ast_ty::c_void(ctx); + let vfn_ty = self + .try_to_rust_ty(ctx, &()) + .expect("vtable to Rust type conversion is infallible"); + let name = ctx.rust_ident(&canonical_name); + let mut vcall_offset_count = 0; + let mut vbase_offset_count = 0; + let mut offset_to_top_count = 0; + let primary_vtable = vtable.primary_entries().iter().map(|entry| { + match entry { + VTableEntry::VCallOffset(_) => { + let field_name = format!("_vcall_offset_{}", vcall_offset_count); + vcall_offset_count += 1; + let field_name = ctx.rust_ident_raw(field_name); + quote! { #field_name: isize, } + } + VTableEntry::VBaseOffset(_) => { + let field_name = format!("_vbase_offset_{}", vbase_offset_count); + vbase_offset_count += 1; + let field_name = ctx.rust_ident_raw(field_name); + quote! { #field_name: isize, } + } + VTableEntry::OffsetToTop(_) => { + let field_name = format!("_offset_to_top_{}", offset_to_top_count); + offset_to_top_count += 1; + let field_name = ctx.rust_ident_raw(field_name); + quote! { #field_name: isize, } + } + VTableEntry::RTTI => { + let ty = helpers::ast_ty::c_void(ctx).to_ptr(true); + // Note: The address point (beginning of virtual function + // pointers) always follows the typeinfo pointer (RTTI), + // which itself must always be present. Therefore we can + // just emit the vfn field immediately after _rtti. + quote! { + _rtti: #ty, + vfns: #vfn_ty, + } + } + _ => quote! {}, + } + }).collect::(); + + let secondary_vtables = vtable + .secondary_vtables() + .iter() + .map(|(_, ty)| { + let class = ty.into_resolver().through_type_refs().resolve(ctx); + let name = ctx.rust_ident( + format!("_base_{}", class.canonical_name(ctx)) + ); + let ty = ctx.rust_ident( + format!("{}__bindgen_vtable", class.canonical_name(ctx)) + ); + quote! { #name: #ty, } + }).collect::(); result.push(quote! { #[repr(C)] - pub struct #name ( #void ); + pub struct #name { + #primary_vtable + #secondary_vtables + } + }); + + let vfns = vtable.primary_entries().iter().map(|entry| { + match entry { + VTableEntry::FunctionPointer(id) => { + let function = ctx.resolve_item(id).expect_function(); + let field_name = ctx.rust_ident_raw(ctx.rust_mangle(function.name())); + let field_ty = function.signature().to_rust_ty_or_opaque(ctx, &()); + // ty.append_implicit_template_params(ctx, field_item); + quote! { #field_name: #field_ty, } + } + VTableEntry::CompleteDtorPointer(id) => { + let function = ctx.resolve_item(id).expect_function(); + let field_ty = function.signature().to_rust_ty_or_opaque(ctx, &()); + // ty.append_implicit_template_params(ctx, field_item); + quote! { _complete_destructor: #field_ty, } + } + VTableEntry::DeletingDtorPointer(id) => { + let function = ctx.resolve_item(id).expect_function(); + let field_ty = function.signature().to_rust_ty_or_opaque(ctx, &()); + // ty.append_implicit_template_params(ctx, field_item); + quote! { _deleting_destructor: #field_ty, } + } + VTableEntry::UnusedFunctionPointer(id) => { + let function = ctx.resolve_item(id).expect_function(); + let field_name = format!("_{}", ctx.rust_mangle(function.name())); + let field_name = ctx.rust_ident_raw(field_name); + let field_ty = function.signature().to_rust_ty_or_opaque(ctx, &()); + // ty.append_implicit_template_params(ctx, field_item); + quote! { #field_name: #field_ty, } + } + _ => quote! {} + } + }).collect::(); + result.push(quote! { + #[repr(C)] + pub struct #vfn_ty { + #vfns + } }); } } @@ -935,7 +1049,8 @@ impl<'a> TryToRustTy for Vtable<'a> { ctx: &BindgenContext, _: &(), ) -> error::Result { - let name = ctx.rust_ident(self.canonical_name(ctx)); + let name = format!("{}__bindgen_vfns", self.item_id.canonical_name(ctx)); + let name = ctx.rust_ident(name); Ok(quote! { #name }) @@ -970,6 +1085,9 @@ impl CodeGenerator for TemplateInstantiation { return; } + // TODO: We generated a unique test for each instantiation location even + // if the template instantiation is identical. These need collapsing. + let layout = item.kind().expect_type().layout(ctx); if let Some(layout) = layout { @@ -1023,6 +1141,7 @@ trait FieldCodegen<'a> { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, + template_args: &HashMap, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1043,6 +1162,7 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, + template_args: &HashMap, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1060,6 +1180,7 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth, accessor_kind, parent, + template_args, result, struct_layout, fields, @@ -1074,6 +1195,7 @@ impl<'a> FieldCodegen<'a> for Field { codegen_depth, accessor_kind, parent, + template_args, result, struct_layout, fields, @@ -1095,6 +1217,7 @@ impl<'a> FieldCodegen<'a> for FieldData { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, + template_args: &HashMap, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1110,8 +1233,10 @@ impl<'a> FieldCodegen<'a> for FieldData { let field_item = self.ty().into_resolver().through_type_refs().resolve(ctx); - let field_ty = field_item.expect_type(); - let mut ty = self.ty().to_rust_ty_or_opaque(ctx, &()); + let ty_id = field_item.id().expect_type_id(ctx); + let ty_id = template_args.get(&ty_id).copied().unwrap_or_else(|| ty_id); + let field_ty = ctx.resolve_type(ty_id); + let mut ty = ty_id.to_rust_ty_or_opaque(ctx, &()); ty.append_implicit_template_params(ctx, field_item); // NB: If supported, we use proper `union` types. @@ -1301,6 +1426,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { codegen_depth: usize, accessor_kind: FieldAccessorKind, parent: &CompInfo, + template_args: &HashMap, result: &mut CodegenResult, struct_layout: &mut StructLayoutTracker, fields: &mut F, @@ -1367,6 +1493,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { codegen_depth, accessor_kind, parent, + template_args, result, struct_layout, fields, @@ -1435,6 +1562,7 @@ impl<'a> FieldCodegen<'a> for Bitfield { _codegen_depth: usize, _accessor_kind: FieldAccessorKind, parent: &CompInfo, + template_args: &HashMap, _result: &mut CodegenResult, _struct_layout: &mut StructLayoutTracker, _fields: &mut F, @@ -1449,7 +1577,8 @@ impl<'a> FieldCodegen<'a> for Bitfield { let setter_name = bitfield_setter_name(ctx, self); let unit_field_ident = Ident::new(unit_field_name, Span::call_site()); - let bitfield_ty_item = ctx.resolve_item(self.ty()); + let ty_id = template_args.get(&self.ty()).copied().unwrap_or_else(|| self.ty()); + let bitfield_ty_item = ctx.resolve_item(ty_id); let bitfield_ty = bitfield_ty_item.expect_type(); let bitfield_ty_layout = bitfield_ty @@ -1526,6 +1655,183 @@ impl<'a> FieldCodegen<'a> for Bitfield { } } +impl CompInfo { + fn lay_out_fields<'a, F, M>( + &self, + ctx: &BindgenContext, + result: &mut CodegenResult<'a>, + item: &Item, + vtable_ty: Option, + struct_layout: &mut StructLayoutTracker, + fields: &mut F, + methods: &mut M, + include_virtual_bases: bool, + ) where + F: Extend, + M: Extend, + { + if self.is_dynamic_class() { + let vtable = Vtable::new(self, item.id()); + vtable.codegen(ctx, result, item); + + if self.primary_base().is_none() { + // We don't have a primary base class but still need a vtable + // pointer. + let vtable_ty = vtable_ty.unwrap_or_else(|| { + vtable.try_to_rust_ty(ctx, &()).unwrap() + }); + let vtable_type = vtable_ty.to_ptr(true); + + fields.extend(Some(quote! { + pub vtable_: #vtable_type , + })); + + struct_layout.saw_vtable(); + } + } + + let mut nonvirtual_bases = self.base_members() + .into_iter() + .filter(|base| base.kind == BaseKind::Normal) + .collect::>(); + + nonvirtual_bases.sort_by(|base1, base2| { + return base1.offset.partial_cmp(&base2.offset).unwrap() + }); + + let primary_base_id = self + .primary_base() + .map(|id| id.into_resolver().through_type_refs().resolve(ctx).id()); + + for base in nonvirtual_bases { + if self.is_dynamic_class() { + let base_item = base.ty + .into_resolver() + .through_type_refs() + .resolve(ctx); + let base_info = base_item + .expect_type() + .canonical_type(ctx) + .as_comp() + .expect("Base was not a composite type?"); + let vtable_ty = if Some(base_item.id()) == primary_base_id { + // We share a vtable pointer with our primary base, so we + // should be sure to point to the correct vfn type when + // generating the primary base fields. + Some( + Vtable::new(self, item.id()) + .try_to_rust_ty(ctx, &()) + .expect("vtable to Rust type conversion is infallible") + ) + } else { + None + }; + base_info.lay_out_fields( + ctx, + result, + base_item, + vtable_ty, + struct_layout, + fields, + methods, + false, + ); + + struct_layout.saw_base(base_item.expect_type()); + } else { + if !base.requires_storage(ctx) { + continue; + } + + let inner_item = ctx.resolve_item(base.ty); + let mut inner = inner_item.to_rust_ty_or_opaque(ctx, &()); + inner.append_implicit_template_params(ctx, &inner_item); + let field_name = ctx.rust_ident(&base.field_name); + + fields.extend(Some(quote! { + pub #field_name: #inner, + })); + + struct_layout.saw_base(inner_item.expect_type()); + } + } + + let template_args = match item.expect_type().kind() { + TypeKind::TemplateInstantiation(instantiation) => { + let templated_class_id = instantiation.template_definition(); + item.expect_type() + .canonical_type(ctx) + .self_template_params(ctx) + .into_iter() + .zip(instantiation.template_arguments().into_iter().copied()) + // Only pass type arguments for the type parameters that + // the def uses. + .filter(|&(param, _)| { + ctx.uses_template_parameter(templated_class_id.into(), param) + }) + .collect::>() + }, + _ => HashMap::default(), + }; + + let codegen_depth = item.codegen_depth(ctx); + let fields_should_be_private = + item.annotations().private_fields().unwrap_or(false); + let struct_accessor_kind = item + .annotations() + .accessor_kind() + .unwrap_or(FieldAccessorKind::None); + for field in self.fields() { + field.codegen( + ctx, + fields_should_be_private, + codegen_depth, + struct_accessor_kind, + self, + &template_args, + result, + struct_layout, + fields, + methods, + (), + ); + } + + // Lay out virtual bases + if include_virtual_bases { + let mut virtual_bases = self.virtual_bases().to_vec(); + + virtual_bases.sort_by_key(|base| base.offset); + + virtual_bases.dedup_by_key(|base| base.offset); + + for base in virtual_bases { + let base_item = base.ty + .into_resolver() + .through_type_refs() + .resolve(ctx); + let base_info = base_item + .expect_type() + .canonical_type(ctx) + .as_comp() + .expect("Base was not a composite type?"); + base_info.lay_out_fields( + ctx, + result, + base_item, + None, + struct_layout, + fields, + methods, + false, + ); + + struct_layout.saw_base(base_item.expect_type()); + } + } + } +} + impl CodeGenerator for CompInfo { type Extra = Item; @@ -1568,65 +1874,24 @@ impl CodeGenerator for CompInfo { let mut struct_layout = StructLayoutTracker::new(ctx, self, ty, &canonical_name); - if !is_opaque { - if item.has_vtable_ptr(ctx) { - let vtable = - Vtable::new(item.id(), self.methods(), self.base_members()); - vtable.codegen(ctx, result, item); + // This layout computation is derived from the logic in + // RecordLayoutBuilder.cpp - let vtable_type = vtable - .try_to_rust_ty(ctx, &()) - .expect("vtable to Rust type conversion is infallible") - .to_ptr(true); - - fields.push(quote! { - pub vtable_: #vtable_type , - }); - - struct_layout.saw_vtable(); - } - - for base in self.base_members() { - if !base.requires_storage(ctx) { - continue; - } - - let inner_item = ctx.resolve_item(base.ty); - let mut inner = inner_item.to_rust_ty_or_opaque(ctx, &()); - inner.append_implicit_template_params(ctx, &inner_item); - let field_name = ctx.rust_ident(&base.field_name); - - struct_layout.saw_base(inner_item.expect_type()); - - fields.push(quote! { - pub #field_name: #inner, - }); - } - } + // TODO: Handle Microsft C++ ABI, or at least check that we aren't + // trying to codegen for windows. let mut methods = vec![]; if !is_opaque { - let codegen_depth = item.codegen_depth(ctx); - let fields_should_be_private = - item.annotations().private_fields().unwrap_or(false); - let struct_accessor_kind = item - .annotations() - .accessor_kind() - .unwrap_or(FieldAccessorKind::None); - for field in self.fields() { - field.codegen( - ctx, - fields_should_be_private, - codegen_depth, - struct_accessor_kind, - self, - result, - &mut struct_layout, - &mut fields, - &mut methods, - (), - ); - } + self.lay_out_fields( + ctx, + result, + item, + None, + &mut struct_layout, + &mut fields, + &mut methods, + true, + ); } let is_union = self.kind() == CompKind::Union; @@ -3627,12 +3892,20 @@ impl CodeGenerator for Function { quote! { #[link(wasm_import_module = #name)] } }); + let vis = if self.is_private() { + quote! {} + } else { + quote! { + pub + } + }; + let ident = ctx.rust_ident(canonical_name); let tokens = quote! { #wasm_link_attribute extern #abi { #(#attributes)* - pub fn #ident ( #( #args ),* ) #ret; + #vis fn #ident ( #( #args ),* ) #ret; } }; result.push(tokens); diff --git a/src/ir/comp.rs b/src/ir/comp.rs index b4417e68ec..773751ed26 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -15,6 +15,7 @@ use ir::derive::CanDeriveCopy; use parse::{ClangItemParser, ParseError}; use peeking_take_while::PeekableExt; use std::cmp; +use std::convert::TryInto; use std::io; use std::mem; use HashMap; @@ -955,6 +956,8 @@ pub struct Base { pub kind: BaseKind, /// Name of the field in which this base should be stored. pub field_name: String, + /// Offset of the start of this base in its child record + pub offset: Option, } impl Base { @@ -984,6 +987,48 @@ impl Base { } } +#[derive(Clone, Debug)] +pub struct VTable { + entries: Vec, + + // Class types of secondary vtables inside this vtable + secondary_vtables: Vec<(usize, TypeId)>, +} + +impl VTable { + pub fn primary_size(&self) -> usize { + if self.secondary_vtables.is_empty() { + self.entries.len() + } else { + self.secondary_vtables[0].0 + } + } + + pub fn entries(&self) -> &[VTableEntry] { + &self.entries + } + + pub fn primary_entries(&self) -> &[VTableEntry] { + &self.entries[..self.primary_size()] + } + + pub fn secondary_vtables(&self) -> &[(usize, TypeId)] { + &self.secondary_vtables + } +} + +#[derive(Clone, Debug)] +pub enum VTableEntry { + VCallOffset(isize), + VBaseOffset(isize), + OffsetToTop(isize), + RTTI, + FunctionPointer(FunctionId), + CompleteDtorPointer(FunctionId), + DeletingDtorPointer(FunctionId), + UnusedFunctionPointer(FunctionId), +} + /// A compound type. /// /// Either a struct or union, a compound type is built up from the combination @@ -1013,8 +1058,15 @@ pub struct CompInfo { /// is virtual. destructor: Option<(MethodKind, FunctionId)>, - /// Vector of classes this one inherits from. - base_members: Vec, + /// Vector of direct base classes this one inherits from. + direct_bases: Vec, + + /// Primary base of this class, if any. + primary_base: Option, + + /// Virtual base classes this class inherits from, either directly or + /// indirectly. + virtual_bases: Vec, /// The inner types that were declared inside this class, in something like: /// @@ -1031,6 +1083,8 @@ pub struct CompInfo { /// Set of static constants declared inside this class. inner_vars: Vec, + vtable: Option, + /// Whether this type should generate an vtable (TODO: Should be able to /// look at the virtual methods and ditch this field). has_own_virtual_method: bool, @@ -1061,6 +1115,10 @@ pub struct CompInfo { /// Used to indicate when a struct has been forward declared. Usually used /// in headers so that APIs can't modify them directly. is_forward_declaration: bool, + + /// Indicates if this is a dynamic class, i.e. a class that requires a + /// virtual pointer. + is_dynamic_class: bool, } impl CompInfo { @@ -1073,9 +1131,12 @@ impl CompInfo { methods: vec![], constructors: vec![], destructor: None, - base_members: vec![], + direct_bases: vec![], + primary_base: None, + virtual_bases: vec![], inner_types: vec![], inner_vars: vec![], + vtable: None, has_own_virtual_method: false, has_destructor: false, has_nonempty_base: false, @@ -1083,6 +1144,7 @@ impl CompInfo { packed_attr: false, found_unknown_attr: false, is_forward_declaration: false, + is_dynamic_class: false, } } @@ -1209,9 +1271,28 @@ impl CompInfo { self.kind() == CompKind::Union } - /// The set of types that this one inherits from. + /// The set of direct bases that this class inherits from. pub fn base_members(&self) -> &[Base] { - &self.base_members + &self.direct_bases + } + + /// Primary base of this class, if any. + pub fn primary_base(&self) -> Option { + self.primary_base + } + + pub fn is_dynamic_class(&self) -> bool { + self.is_dynamic_class + } + + /// The set of virtual bases this class inherits from, directly or + /// indirectly. + pub fn virtual_bases(&self) -> &[Base] { + &self.virtual_bases + } + + pub fn vtable(&self) -> Option<&VTable> { + self.vtable.as_ref() } /// Construct a new compound type from a Clang type. @@ -1247,6 +1328,65 @@ impl CompInfo { _ => false, }); + ci.is_dynamic_class = cursor.is_dynamic_class(); + if let Some(primary_base) = cursor.get_primary_base() { + let base_type = Item::from_ty_or_ref( + primary_base.cur_type(), + primary_base, + None, + ctx, + ); + ci.primary_base = Some(base_type); + } + + if let Some(components) = cursor.get_vtable_components() { + let entries = components.map(|component| { + match component { + VTableComponent::VCallOffset(offset) => + VTableEntry::VCallOffset(offset.try_into().unwrap()), + VTableComponent::VBaseOffset(offset) => + VTableEntry::VBaseOffset(offset.try_into().unwrap()), + VTableComponent::OffsetToTop(offset) => + VTableEntry::OffsetToTop(offset.try_into().unwrap()), + VTableComponent::RTTI(_) => VTableEntry::RTTI, + VTableComponent::FunctionPointer(cur) | + VTableComponent::CompleteDtorPointer(cur) | + VTableComponent::DeletingDtorPointer(cur) | + VTableComponent::UnusedFunctionPointer(cur) => { + let item = Item::parse(cur, Some(potential_id), ctx) + .expect("Could not resolve vtable function pointer"); + let id = item.expect_function_id(ctx); + match component { + VTableComponent::FunctionPointer(_) => + VTableEntry::FunctionPointer(id), + VTableComponent::CompleteDtorPointer(_) => + VTableEntry::CompleteDtorPointer(id), + VTableComponent::DeletingDtorPointer(_) => + VTableEntry::DeletingDtorPointer(id), + VTableComponent::UnusedFunctionPointer(_) => + VTableEntry::UnusedFunctionPointer(id), + _ => unreachable!("Other arms handled in outer match"), + } + } + } + }).collect(); + + let mut secondary_vtables = cursor + .get_secondary_vtables(); + secondary_vtables.sort_by_key(|(index, _)| *index); + let secondary_vtables = secondary_vtables + .into_iter() + .map(|(index, cur)| { + (index, Item::from_ty_or_ref(cur.cur_type(), cur, None, ctx)) + }) + .collect(); + + ci.vtable = Some(VTable { + entries, + secondary_vtables, + }); + } + let mut maybe_anonymous_struct_field = None; cursor.visit(|cur| { if cur.kind() != CXCursor_FieldDecl { @@ -1401,16 +1541,18 @@ impl CompInfo { BaseKind::Normal }; - let field_name = match ci.base_members.len() { + let field_name = match ci.direct_bases.len() { 0 => "_base".into(), n => format!("_base_{}", n), }; let type_id = Item::from_ty_or_ref(cur.cur_type(), cur, None, ctx); - ci.base_members.push(Base { + let offset = cursor.base_offset(&cur); + ci.direct_bases.push(Base { ty: type_id, kind: kind, field_name: field_name, + offset: offset, }); } CXCursor_Constructor | CXCursor_Destructor | @@ -1504,8 +1646,8 @@ impl CompInfo { } if let Ok(item) = Item::parse(cur, Some(potential_id), ctx) - { - ci.inner_vars.push(item.as_var_id_unchecked()); + { + ci.inner_vars.push(item.as_var_id_unchecked()); } } // Intentionally not handled @@ -1526,6 +1668,26 @@ impl CompInfo { CXChildVisit_Continue }); + cursor.visit_virtual_bases(|vbase| { + assert!(vbase.is_virtual_base()); + + let field_name = match ci.virtual_bases.len() { + 0 => "_vbase".into(), + n => format!("_vbase_{}", n), + }; + let type_id = + Item::from_ty_or_ref(vbase.cur_type(), vbase, None, ctx); + let offset = cursor.base_offset(&vbase); + ci.virtual_bases.push(Base { + ty: type_id, + kind: BaseKind::Virtual, + field_name: field_name, + offset: offset, + }); + + CXChildVisit_Continue + }); + if let Some((ty, _, offset)) = maybe_anonymous_struct_field { let field = RawField::new(None, ty, None, None, None, false, offset); diff --git a/src/ir/function.rs b/src/ir/function.rs index bba9d26df8..b7f5b5a24e 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -89,6 +89,9 @@ pub struct Function { /// The linkage of the function. linkage: Linkage, + + /// Is this a private method? + private: bool, } impl Function { @@ -100,6 +103,7 @@ impl Function { comment: Option, kind: FunctionKind, linkage: Linkage, + private: bool, ) -> Self { Function { name, @@ -108,6 +112,7 @@ impl Function { comment, kind, linkage, + private, } } @@ -135,6 +140,11 @@ impl Function { pub fn linkage(&self) -> Linkage { self.linkage } + + /// Is this a private method? + pub fn is_private(&self) -> bool { + self.private + } } impl DotAttributes for Function { @@ -566,9 +576,7 @@ impl ClangSubItemParser for Function { return Err(ParseError::Continue); } - if cursor.access_specifier() == CX_CXXPrivate { - return Err(ParseError::Continue); - } + let private = cursor.access_specifier() == CX_CXXPrivate; if !context.options().generate_inline_functions && cursor.is_inlined_function() @@ -607,7 +615,7 @@ impl ClangSubItemParser for Function { let comment = cursor.raw_comment(); let function = - Self::new(name, mangled_name, sig, comment, kind, linkage); + Self::new(name, mangled_name, sig, comment, kind, linkage, private); Ok(ParseResult::New(function, Some(cursor))) } } diff --git a/tests/expectations/tests/bug-1529681.rs b/tests/expectations/tests/bug-1529681.rs index 81a3209306..59afdcb218 100644 --- a/tests/expectations/tests/bug-1529681.rs +++ b/tests/expectations/tests/bug-1529681.rs @@ -25,3 +25,19 @@ fn bindgen_test_layout_BrowsingContext() { concat!("Alignment of ", stringify!(BrowsingContext)) ); } +extern "C" { + #[link_name = "\u{1}_ZNK15BrowsingContext3TieEPv"] + fn BrowsingContext_Tie( + this: *const BrowsingContext, + aUnused: *mut ::std::os::raw::c_void, + ) -> [u8; 0usize]; +} +impl BrowsingContext { + #[inline] + pub unsafe fn Tie( + &self, + aUnused: *mut ::std::os::raw::c_void, + ) -> [u8; 0usize] { + BrowsingContext_Tie(self, aUnused) + } +} diff --git a/tests/expectations/tests/constructors.rs b/tests/expectations/tests/constructors.rs index f334b91d40..4613cb88fd 100644 --- a/tests/expectations/tests/constructors.rs +++ b/tests/expectations/tests/constructors.rs @@ -25,30 +25,40 @@ fn bindgen_test_layout_TestOverload() { concat!("Alignment of ", stringify!(TestOverload)) ); } +extern "C" { + #[link_name = "\u{1}_ZN12TestOverloadC1Ev"] + fn TestOverload_TestOverload(this: *mut TestOverload); +} extern "C" { #[link_name = "\u{1}_ZN12TestOverloadC1Ei"] - pub fn TestOverload_TestOverload( + pub fn TestOverload_TestOverload1( this: *mut TestOverload, arg1: ::std::os::raw::c_int, ); } extern "C" { #[link_name = "\u{1}_ZN12TestOverloadC1Ed"] - pub fn TestOverload_TestOverload1(this: *mut TestOverload, arg1: f64); + pub fn TestOverload_TestOverload2(this: *mut TestOverload, arg1: f64); } impl TestOverload { #[inline] - pub unsafe fn new(arg1: ::std::os::raw::c_int) -> Self { + pub unsafe fn new() -> Self { let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); - TestOverload_TestOverload(__bindgen_tmp.as_mut_ptr(), arg1); + TestOverload_TestOverload(__bindgen_tmp.as_mut_ptr()); __bindgen_tmp.assume_init() } #[inline] - pub unsafe fn new1(arg1: f64) -> Self { + pub unsafe fn new1(arg1: ::std::os::raw::c_int) -> Self { let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); TestOverload_TestOverload1(__bindgen_tmp.as_mut_ptr(), arg1); __bindgen_tmp.assume_init() } + #[inline] + pub unsafe fn new2(arg1: f64) -> Self { + let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit(); + TestOverload_TestOverload2(__bindgen_tmp.as_mut_ptr(), arg1); + __bindgen_tmp.assume_init() + } } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] diff --git a/tests/expectations/tests/constructors_1_33.rs b/tests/expectations/tests/constructors_1_33.rs index 7de6c27bd2..6df4009fee 100644 --- a/tests/expectations/tests/constructors_1_33.rs +++ b/tests/expectations/tests/constructors_1_33.rs @@ -25,10 +25,14 @@ fn bindgen_test_layout_TestOverload() { concat!("Alignment of ", stringify!(TestOverload)) ); } +extern "C" { + #[link_name = "\u{1}_ZN12TestOverloadC1Ev"] + fn TestOverload_TestOverload(this: *mut TestOverload); +} extern "C" { /// Calling this should use `mem::unintialized()` and not `MaybeUninit()` as only rust 1.36 includes that. #[link_name = "\u{1}_ZN12TestOverloadC1Ei"] - pub fn TestOverload_TestOverload( + pub fn TestOverload_TestOverload1( this: *mut TestOverload, arg1: ::std::os::raw::c_int, ); @@ -36,21 +40,27 @@ extern "C" { extern "C" { /// Calling this should use `mem::unintialized()` and not `MaybeUninit()` as only rust 1.36 includes that. #[link_name = "\u{1}_ZN12TestOverloadC1Ed"] - pub fn TestOverload_TestOverload1(this: *mut TestOverload, arg1: f64); + pub fn TestOverload_TestOverload2(this: *mut TestOverload, arg1: f64); } impl TestOverload { #[inline] - pub unsafe fn new(arg1: ::std::os::raw::c_int) -> Self { + pub unsafe fn new() -> Self { let mut __bindgen_tmp = ::std::mem::uninitialized(); - TestOverload_TestOverload(&mut __bindgen_tmp, arg1); + TestOverload_TestOverload(&mut __bindgen_tmp); __bindgen_tmp } #[inline] - pub unsafe fn new1(arg1: f64) -> Self { + pub unsafe fn new1(arg1: ::std::os::raw::c_int) -> Self { let mut __bindgen_tmp = ::std::mem::uninitialized(); TestOverload_TestOverload1(&mut __bindgen_tmp, arg1); __bindgen_tmp } + #[inline] + pub unsafe fn new2(arg1: f64) -> Self { + let mut __bindgen_tmp = ::std::mem::uninitialized(); + TestOverload_TestOverload2(&mut __bindgen_tmp, arg1); + __bindgen_tmp + } } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] diff --git a/tests/expectations/tests/enum_and_vtable_mangling.rs b/tests/expectations/tests/enum_and_vtable_mangling.rs index 8d2cf204e8..06efdd9116 100644 --- a/tests/expectations/tests/enum_and_vtable_mangling.rs +++ b/tests/expectations/tests/enum_and_vtable_mangling.rs @@ -16,11 +16,21 @@ pub enum _bindgen_ty_1 { whatever_else = 1, } #[repr(C)] -pub struct C__bindgen_vtable(::std::os::raw::c_void); +pub struct C__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: C__bindgen_vfns, +} +#[repr(C)] +pub struct C__bindgen_vfns { + match_: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct C { - pub vtable_: *const C__bindgen_vtable, + pub vtable_: *const C__bindgen_vfns, pub i: ::std::os::raw::c_int, } #[test] diff --git a/tests/expectations/tests/inherit-from-template-instantiation-with-vtable.rs b/tests/expectations/tests/inherit-from-template-instantiation-with-vtable.rs index 6e118d685f..4e355a952d 100644 --- a/tests/expectations/tests/inherit-from-template-instantiation-with-vtable.rs +++ b/tests/expectations/tests/inherit-from-template-instantiation-with-vtable.rs @@ -7,13 +7,11 @@ non_upper_case_globals )] -#[repr(C)] -pub struct BaseWithVtable__bindgen_vtable(::std::os::raw::c_void); /// This should have an explicit vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct BaseWithVtable { - pub vtable_: *const BaseWithVtable__bindgen_vtable, + pub vtable_: *const BaseWithVtable__bindgen_vfns, pub t: T, pub _phantom_0: ::std::marker::PhantomData<::std::cell::UnsafeCell>, } @@ -22,11 +20,24 @@ impl Default for BaseWithVtable { unsafe { ::std::mem::zeroed() } } } +#[repr(C)] +pub struct DerivedWithNoVirtualMethods__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: DerivedWithNoVirtualMethods__bindgen_vfns, +} +#[repr(C)] +pub struct DerivedWithNoVirtualMethods__bindgen_vfns { + hello: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, +} /// This should not have an explicit vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct DerivedWithNoVirtualMethods { - pub _base: BaseWithVtable<*mut ::std::os::raw::c_char>, + pub vtable_: *const BaseWithVtable__bindgen_vfns, + pub t: *mut ::std::os::raw::c_char, } #[test] fn bindgen_test_layout_DerivedWithNoVirtualMethods() { @@ -46,11 +57,31 @@ impl Default for DerivedWithNoVirtualMethods { unsafe { ::std::mem::zeroed() } } } +extern "C" { + #[link_name = "\u{1}_ZN14BaseWithVtableIPcE5helloEv"] + fn DerivedWithNoVirtualMethods_hello(this: *mut ::std::os::raw::c_void); +} +#[repr(C)] +pub struct DerivedWithVirtualMethods__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: DerivedWithVirtualMethods__bindgen_vfns, +} +#[repr(C)] +pub struct DerivedWithVirtualMethods__bindgen_vfns { + hello: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, + zoidberg: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, +} /// This should not have an explicit vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct DerivedWithVirtualMethods { - pub _base: BaseWithVtable<*mut ::std::os::raw::c_char>, + pub vtable_: *const BaseWithVtable__bindgen_vfns, + pub t: *mut ::std::os::raw::c_char, } #[test] fn bindgen_test_layout_DerivedWithVirtualMethods() { @@ -70,6 +101,10 @@ impl Default for DerivedWithVirtualMethods { unsafe { ::std::mem::zeroed() } } } +extern "C" { + #[link_name = "\u{1}_ZN25DerivedWithVirtualMethods8zoidbergEv"] + fn DerivedWithVirtualMethods_zoidberg(this: *mut ::std::os::raw::c_void); +} /// This should not have any vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -83,13 +118,23 @@ impl Default for BaseWithoutVtable { } } #[repr(C)] -pub struct DerivedWithVtable__bindgen_vtable(::std::os::raw::c_void); +pub struct DerivedWithVtable__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: DerivedWithVtable__bindgen_vfns, +} +#[repr(C)] +pub struct DerivedWithVtable__bindgen_vfns { + leela: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, +} /// This should have an explicit vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct DerivedWithVtable { - pub vtable_: *const DerivedWithVtable__bindgen_vtable, - pub _base: BaseWithoutVtable<*mut ::std::os::raw::c_char>, + pub vtable_: *const DerivedWithVtable__bindgen_vfns, + pub u: *mut ::std::os::raw::c_char, } #[test] fn bindgen_test_layout_DerivedWithVtable() { @@ -109,6 +154,10 @@ impl Default for DerivedWithVtable { unsafe { ::std::mem::zeroed() } } } +extern "C" { + #[link_name = "\u{1}_ZN17DerivedWithVtable5leelaEv"] + fn DerivedWithVtable_leela(this: *mut ::std::os::raw::c_void); +} /// This should not have any vtable. #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -173,6 +222,46 @@ fn __bindgen_test_layout_BaseWithVtable_open0_ptr_char_close0_instantiation_1() ); } #[test] +fn __bindgen_test_layout_BaseWithVtable_open0_ptr_char_close0_instantiation_2() +{ + assert_eq!( + ::std::mem::size_of::>(), + 16usize, + concat!( + "Size of template specialization: ", + stringify!(BaseWithVtable<*mut ::std::os::raw::c_char>) + ) + ); + assert_eq!( + ::std::mem::align_of::>(), + 8usize, + concat!( + "Alignment of template specialization: ", + stringify!(BaseWithVtable<*mut ::std::os::raw::c_char>) + ) + ); +} +#[test] +fn __bindgen_test_layout_BaseWithVtable_open0_ptr_char_close0_instantiation_3() +{ + assert_eq!( + ::std::mem::size_of::>(), + 16usize, + concat!( + "Size of template specialization: ", + stringify!(BaseWithVtable<*mut ::std::os::raw::c_char>) + ) + ); + assert_eq!( + ::std::mem::align_of::>(), + 8usize, + concat!( + "Alignment of template specialization: ", + stringify!(BaseWithVtable<*mut ::std::os::raw::c_char>) + ) + ); +} +#[test] fn __bindgen_test_layout_BaseWithoutVtable_open0_ptr_char_close0_instantiation() { assert_eq!( diff --git a/tests/expectations/tests/issue-1197-pure-virtual-stuff.rs b/tests/expectations/tests/issue-1197-pure-virtual-stuff.rs index 1c1baa5919..7243af35cd 100644 --- a/tests/expectations/tests/issue-1197-pure-virtual-stuff.rs +++ b/tests/expectations/tests/issue-1197-pure-virtual-stuff.rs @@ -8,11 +8,25 @@ )] #[repr(C)] -pub struct Foo__bindgen_vtable(::std::os::raw::c_void); +pub struct Foo__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: Foo__bindgen_vfns, +} +#[repr(C)] +pub struct Foo__bindgen_vfns { + Bar: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, + _complete_destructor: + ::std::option::Option, + _deleting_destructor: + ::std::option::Option, +} #[repr(C)] #[derive(Debug)] pub struct Foo { - pub vtable_: *const Foo__bindgen_vtable, + pub vtable_: *const Foo__bindgen_vfns, } #[test] fn bindgen_test_layout_Foo() { diff --git a/tests/expectations/tests/issue-691-template-parameter-virtual.rs b/tests/expectations/tests/issue-691-template-parameter-virtual.rs index afe5b75d29..0b7477c5b6 100644 --- a/tests/expectations/tests/issue-691-template-parameter-virtual.rs +++ b/tests/expectations/tests/issue-691-template-parameter-virtual.rs @@ -8,11 +8,21 @@ )] #[repr(C)] -pub struct VirtualMethods__bindgen_vtable(::std::os::raw::c_void); +pub struct VirtualMethods__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: VirtualMethods__bindgen_vfns, +} +#[repr(C)] +pub struct VirtualMethods__bindgen_vfns { + foo: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void), + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct VirtualMethods { - pub vtable_: *const VirtualMethods__bindgen_vtable, + pub vtable_: *const VirtualMethods__bindgen_vfns, } #[test] fn bindgen_test_layout_VirtualMethods() { @@ -32,6 +42,10 @@ impl Default for VirtualMethods { unsafe { ::std::mem::zeroed() } } } +extern "C" { + #[link_name = "\u{1}_ZN14VirtualMethods3fooEv"] + fn VirtualMethods_foo(this: *mut ::std::os::raw::c_void); +} #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct Set { diff --git a/tests/expectations/tests/libclang-9/class.rs b/tests/expectations/tests/libclang-9/class.rs index 49e080d0aa..5c4b232802 100644 --- a/tests/expectations/tests/libclang-9/class.rs +++ b/tests/expectations/tests/libclang-9/class.rs @@ -624,6 +624,12 @@ fn bindgen_test_layout_RealAbstractionWithTonsOfMethods() { ) ); } +extern "C" { + #[link_name = "\u{1}_ZN32RealAbstractionWithTonsOfMethods3fooEv"] + fn RealAbstractionWithTonsOfMethods_foo( + this: *mut RealAbstractionWithTonsOfMethods, + ); +} extern "C" { #[link_name = "\u{1}_ZNK32RealAbstractionWithTonsOfMethods3barEv"] pub fn RealAbstractionWithTonsOfMethods_bar( @@ -648,6 +654,10 @@ extern "C" { pub fn RealAbstractionWithTonsOfMethods_sta(); } impl RealAbstractionWithTonsOfMethods { + #[inline] + pub unsafe fn foo(&mut self) { + RealAbstractionWithTonsOfMethods_foo(self) + } #[inline] pub unsafe fn bar(&self) { RealAbstractionWithTonsOfMethods_bar(self) diff --git a/tests/expectations/tests/libclang-9/class_1_0.rs b/tests/expectations/tests/libclang-9/class_1_0.rs index b86534a7db..53f0acb7da 100644 --- a/tests/expectations/tests/libclang-9/class_1_0.rs +++ b/tests/expectations/tests/libclang-9/class_1_0.rs @@ -677,6 +677,12 @@ fn bindgen_test_layout_RealAbstractionWithTonsOfMethods() { ) ); } +extern "C" { + #[link_name = "\u{1}_ZN32RealAbstractionWithTonsOfMethods3fooEv"] + fn RealAbstractionWithTonsOfMethods_foo( + this: *mut RealAbstractionWithTonsOfMethods, + ); +} extern "C" { #[link_name = "\u{1}_ZNK32RealAbstractionWithTonsOfMethods3barEv"] pub fn RealAbstractionWithTonsOfMethods_bar( @@ -706,6 +712,10 @@ impl Clone for RealAbstractionWithTonsOfMethods { } } impl RealAbstractionWithTonsOfMethods { + #[inline] + pub unsafe fn foo(&mut self) { + RealAbstractionWithTonsOfMethods_foo(self) + } #[inline] pub unsafe fn bar(&self) { RealAbstractionWithTonsOfMethods_bar(self) diff --git a/tests/expectations/tests/nested_vtable.rs b/tests/expectations/tests/nested_vtable.rs index e11496e2db..66007eb4e7 100644 --- a/tests/expectations/tests/nested_vtable.rs +++ b/tests/expectations/tests/nested_vtable.rs @@ -8,11 +8,23 @@ )] #[repr(C)] -pub struct nsISupports__bindgen_vtable(::std::os::raw::c_void); +pub struct nsISupports__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: nsISupports__bindgen_vfns, +} +#[repr(C)] +pub struct nsISupports__bindgen_vfns { + QueryInterface: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + ) -> *mut nsISupports, + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct nsISupports { - pub vtable_: *const nsISupports__bindgen_vtable, + pub vtable_: *const nsISupports__bindgen_vfns, } #[test] fn bindgen_test_layout_nsISupports() { @@ -39,9 +51,23 @@ extern "C" { ) -> *mut nsISupports; } #[repr(C)] +pub struct nsIRunnable__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: nsIRunnable__bindgen_vfns, +} +#[repr(C)] +pub struct nsIRunnable__bindgen_vfns { + QueryInterface: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + ) -> *mut nsISupports, + >, +} +#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct nsIRunnable { - pub _base: nsISupports, + pub vtable_: *const nsIRunnable__bindgen_vfns, } #[test] fn bindgen_test_layout_nsIRunnable() { @@ -62,9 +88,23 @@ impl Default for nsIRunnable { } } #[repr(C)] +pub struct Runnable__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: Runnable__bindgen_vfns, +} +#[repr(C)] +pub struct Runnable__bindgen_vfns { + QueryInterface: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + ) -> *mut nsISupports, + >, +} +#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Runnable { - pub _base: nsIRunnable, + pub vtable_: *const nsIRunnable__bindgen_vfns, } #[test] fn bindgen_test_layout_Runnable() { diff --git a/tests/expectations/tests/packed-vtable.rs b/tests/expectations/tests/packed-vtable.rs index 4bb206c68a..78c3005b9b 100644 --- a/tests/expectations/tests/packed-vtable.rs +++ b/tests/expectations/tests/packed-vtable.rs @@ -9,11 +9,22 @@ #![cfg(feature = "nightly")] #[repr(C)] -pub struct PackedVtable__bindgen_vtable(::std::os::raw::c_void); +pub struct PackedVtable__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: PackedVtable__bindgen_vfns, +} +#[repr(C)] +pub struct PackedVtable__bindgen_vfns { + _complete_destructor: + ::std::option::Option, + _deleting_destructor: + ::std::option::Option, +} #[repr(C, packed)] #[derive(Debug)] pub struct PackedVtable { - pub vtable_: *const PackedVtable__bindgen_vtable, + pub vtable_: *const PackedVtable__bindgen_vfns, } #[test] fn bindgen_test_layout_PackedVtable() { diff --git a/tests/expectations/tests/ref_argument_array.rs b/tests/expectations/tests/ref_argument_array.rs index d31c774332..3532a6b248 100644 --- a/tests/expectations/tests/ref_argument_array.rs +++ b/tests/expectations/tests/ref_argument_array.rs @@ -9,11 +9,24 @@ pub const NSID_LENGTH: u32 = 10; #[repr(C)] -pub struct nsID__bindgen_vtable(::std::os::raw::c_void); +pub struct nsID__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: nsID__bindgen_vfns, +} +#[repr(C)] +pub struct nsID__bindgen_vfns { + ToProvidedString: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + aDest: *mut [::std::os::raw::c_char; 10usize], + ), + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct nsID { - pub vtable_: *const nsID__bindgen_vtable, + pub vtable_: *const nsID__bindgen_vfns, } #[test] fn bindgen_test_layout_nsID() { diff --git a/tests/expectations/tests/virtual_dtor.rs b/tests/expectations/tests/virtual_dtor.rs index 486de8c691..2f41eedf7f 100644 --- a/tests/expectations/tests/virtual_dtor.rs +++ b/tests/expectations/tests/virtual_dtor.rs @@ -8,11 +8,22 @@ )] #[repr(C)] -pub struct nsSlots__bindgen_vtable(::std::os::raw::c_void); +pub struct nsSlots__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: nsSlots__bindgen_vfns, +} +#[repr(C)] +pub struct nsSlots__bindgen_vfns { + _complete_destructor: + ::std::option::Option, + _deleting_destructor: + ::std::option::Option, +} #[repr(C)] #[derive(Debug)] pub struct nsSlots { - pub vtable_: *const nsSlots__bindgen_vtable, + pub vtable_: *const nsSlots__bindgen_vfns, } #[test] fn bindgen_test_layout_nsSlots() { diff --git a/tests/expectations/tests/virtual_inheritance.rs b/tests/expectations/tests/virtual_inheritance.rs index 4da4170413..1bad9860b6 100644 --- a/tests/expectations/tests/virtual_inheritance.rs +++ b/tests/expectations/tests/virtual_inheritance.rs @@ -31,12 +31,20 @@ fn bindgen_test_layout_A() { ); } #[repr(C)] -pub struct B__bindgen_vtable(::std::os::raw::c_void); +pub struct B__bindgen_vtable { + _vbase_offset_0: isize, + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: B__bindgen_vfns, +} +#[repr(C)] +pub struct B__bindgen_vfns {} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct B { - pub vtable_: *const B__bindgen_vtable, + pub vtable_: *const B__bindgen_vfns, pub bar: ::std::os::raw::c_int, + pub foo: ::std::os::raw::c_int, } #[test] fn bindgen_test_layout_B() { @@ -62,12 +70,20 @@ impl Default for B { } } #[repr(C)] -pub struct C__bindgen_vtable(::std::os::raw::c_void); +pub struct C__bindgen_vtable { + _vbase_offset_0: isize, + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: C__bindgen_vfns, +} +#[repr(C)] +pub struct C__bindgen_vfns {} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct C { - pub vtable_: *const C__bindgen_vtable, + pub vtable_: *const C__bindgen_vfns, pub baz: ::std::os::raw::c_int, + pub foo: ::std::os::raw::c_int, } #[test] fn bindgen_test_layout_C() { @@ -93,11 +109,24 @@ impl Default for C { } } #[repr(C)] +pub struct D__bindgen_vtable { + _vbase_offset_0: isize, + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: D__bindgen_vfns, + _base_B: B__bindgen_vtable, +} +#[repr(C)] +pub struct D__bindgen_vfns {} +#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct D { - pub _base: C, - pub _base_1: B, + pub vtable_: *const D__bindgen_vfns, + pub baz: ::std::os::raw::c_int, + pub vtable_: *const B__bindgen_vfns, + pub bar: ::std::os::raw::c_int, pub bazz: ::std::os::raw::c_int, + pub foo: ::std::os::raw::c_int, } #[test] fn bindgen_test_layout_D() { diff --git a/tests/expectations/tests/virtual_overloaded.rs b/tests/expectations/tests/virtual_overloaded.rs index f55b5153b9..8af4eb5056 100644 --- a/tests/expectations/tests/virtual_overloaded.rs +++ b/tests/expectations/tests/virtual_overloaded.rs @@ -8,11 +8,30 @@ )] #[repr(C)] -pub struct C__bindgen_vtable(::std::os::raw::c_void); +pub struct C__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: C__bindgen_vfns, +} +#[repr(C)] +pub struct C__bindgen_vfns { + do_thing: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + arg1: ::std::os::raw::c_char, + ), + >, + do_thing: ::std::option::Option< + unsafe extern "C" fn( + this: *mut ::std::os::raw::c_void, + arg1: ::std::os::raw::c_int, + ), + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct C { - pub vtable_: *const C__bindgen_vtable, + pub vtable_: *const C__bindgen_vfns, } #[test] fn bindgen_test_layout_C() { diff --git a/tests/expectations/tests/vtable_recursive_sig.rs b/tests/expectations/tests/vtable_recursive_sig.rs index 90d8b0e394..0daa2e77e4 100644 --- a/tests/expectations/tests/vtable_recursive_sig.rs +++ b/tests/expectations/tests/vtable_recursive_sig.rs @@ -8,11 +8,21 @@ )] #[repr(C)] -pub struct Base__bindgen_vtable(::std::os::raw::c_void); +pub struct Base__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: Base__bindgen_vfns, +} +#[repr(C)] +pub struct Base__bindgen_vfns { + AsDerived: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void) -> *mut Derived, + >, +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Base { - pub vtable_: *const Base__bindgen_vtable, + pub vtable_: *const Base__bindgen_vfns, } #[test] fn bindgen_test_layout_Base() { @@ -37,9 +47,21 @@ extern "C" { pub fn Base_AsDerived(this: *mut ::std::os::raw::c_void) -> *mut Derived; } #[repr(C)] +pub struct Derived__bindgen_vtable { + _offset_to_top_0: isize, + _rtti: *const ::std::os::raw::c_void, + vfns: Derived__bindgen_vfns, +} +#[repr(C)] +pub struct Derived__bindgen_vfns { + AsDerived: ::std::option::Option< + unsafe extern "C" fn(this: *mut ::std::os::raw::c_void) -> *mut Derived, + >, +} +#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Derived { - pub _base: Base, + pub vtable_: *const Derived__bindgen_vfns, } #[test] fn bindgen_test_layout_Derived() { @@ -59,3 +81,7 @@ impl Default for Derived { unsafe { ::std::mem::zeroed() } } } +extern "C" { + #[link_name = "\u{1}_ZN7Derived9AsDerivedEv"] + fn Derived_AsDerived(this: *mut ::std::os::raw::c_void) -> *mut Derived; +}