Skip to content

Extremely basic Vtable generation #2145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 68 additions & 25 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use crate::ir::analysis::{HasVtable, Sizedness};
use crate::ir::annotations::FieldAccessorKind;
use crate::ir::comment;
use crate::ir::comp::{
Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData,
FieldMethods, Method, MethodKind,
Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, FieldMethods,
Method, MethodKind,
};
use crate::ir::context::{BindgenContext, ItemId};
use crate::ir::derive::{
Expand Down Expand Up @@ -1017,23 +1017,14 @@ impl CodeGenerator for Type {

struct Vtable<'a> {
item_id: ItemId,
/// A reference to the originating compound object.
#[allow(dead_code)]
methods: &'a [Method],
#[allow(dead_code)]
base_classes: &'a [Base],
comp_info: &'a CompInfo,
}

impl<'a> Vtable<'a> {
fn new(
item_id: ItemId,
methods: &'a [Method],
base_classes: &'a [Base],
) -> Self {
Vtable {
item_id,
methods,
base_classes,
}
fn new(item_id: ItemId, comp_info: &'a CompInfo) -> Self {
Vtable { item_id, comp_info }
}
}

Expand All @@ -1049,15 +1040,68 @@ impl<'a> CodeGenerator for Vtable<'a> {
) {
assert_eq!(item.id(), self.item_id);
debug_assert!(item.is_enabled_for_codegen(ctx));

// 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);
result.push(quote! {
#[repr(C)]
pub struct #name ( #void );
});

// For now, we will only generate vtables for classes that:
// - do not inherit from others (compilers merge VTable from primary parent class).
// - do not contain a virtual destructor (requires ordering; platforms generate different vtables).
if self.comp_info.base_members().is_empty() &&
self.comp_info.destructor().is_none()
{
let class_ident = ctx.rust_ident(self.item_id.canonical_name(ctx));

let methods = self
.comp_info
.methods()
.iter()
.filter_map(|m| {
if !m.is_virtual() {
return None;
}

let function_item = ctx.resolve_item(m.signature());
let function = function_item.expect_function();
let signature_item = ctx.resolve_item(function.signature());
let signature = match signature_item.expect_type().kind() {
TypeKind::Function(ref sig) => sig,
_ => panic!("Function signature type mismatch"),
};

// FIXME: Is there a canonical name without the class prepended?
let function_name = function_item.canonical_name(ctx);

// FIXME: Need to account for overloading with times_seen (separately from regular function path).
let function_name = ctx.rust_ident(function_name);
let mut args = utils::fnsig_arguments(ctx, signature);
let ret = utils::fnsig_return_ty(ctx, signature);

args[0] = if m.is_const() {
quote! { this: & #class_ident }
} else {
quote! { this: &mut #class_ident }
};

Some(quote! {
pub #function_name : unsafe extern "C" fn( #( #args ),* ) #ret
})
})
.collect::<Vec<_>>();

result.push(quote! {
#[repr(C)]
pub struct #name {
#( #methods ),*
}
})
} else {
// For the cases we don't support, simply generate an empty struct.
let void = helpers::ast_ty::c_void(ctx);

result.push(quote! {
#[repr(C)]
pub struct #name ( #void );
});
}
}
}

Expand Down Expand Up @@ -1749,8 +1793,7 @@ impl CodeGenerator for CompInfo {

if !is_opaque {
if item.has_vtable_ptr(ctx) {
let vtable =
Vtable::new(item.id(), self.methods(), self.base_members());
let vtable = Vtable::new(item.id(), &self);
vtable.codegen(ctx, result, item);

let vtable_type = vtable
Expand Down
4 changes: 3 additions & 1 deletion tests/expectations/tests/enum_and_vtable_mangling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ 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 {
pub C_match: unsafe extern "C" fn(this: &mut C),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct C {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
)]

#[repr(C)]
pub struct BaseWithVtable__bindgen_vtable(::std::os::raw::c_void);
pub struct BaseWithVtable__bindgen_vtable {}
/// This should have an explicit vtable.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
)]

#[repr(C)]
pub struct VirtualMethods__bindgen_vtable(::std::os::raw::c_void);
pub struct VirtualMethods__bindgen_vtable {}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VirtualMethods {
Expand Down
5 changes: 4 additions & 1 deletion tests/expectations/tests/nested_vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
)]

#[repr(C)]
pub struct nsISupports__bindgen_vtable(::std::os::raw::c_void);
pub struct nsISupports__bindgen_vtable {
pub nsISupports_QueryInterface:
unsafe extern "C" fn(this: &mut nsISupports) -> *mut nsISupports,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct nsISupports {
Expand Down
7 changes: 6 additions & 1 deletion tests/expectations/tests/ref_argument_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@

pub const NSID_LENGTH: u32 = 10;
#[repr(C)]
pub struct nsID__bindgen_vtable(::std::os::raw::c_void);
pub struct nsID__bindgen_vtable {
pub nsID_ToProvidedString: unsafe extern "C" fn(
this: &mut nsID,
aDest: *mut [::std::os::raw::c_char; 10usize],
),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct nsID {
Expand Down
128 changes: 128 additions & 0 deletions tests/expectations/tests/virtual_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#![allow(
dead_code,
non_snake_case,
non_camel_case_types,
non_upper_case_globals
)]

#[repr(C)]
pub struct PureVirtualIFace__bindgen_vtable {
pub PureVirtualIFace_Foo: unsafe extern "C" fn(this: &mut PureVirtualIFace),
pub PureVirtualIFace_Bar: unsafe extern "C" fn(
this: &mut PureVirtualIFace,
arg1: ::std::os::raw::c_uint,
),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct PureVirtualIFace {
pub vtable_: *const PureVirtualIFace__bindgen_vtable,
}
#[test]
fn bindgen_test_layout_PureVirtualIFace() {
assert_eq!(
::std::mem::size_of::<PureVirtualIFace>(),
8usize,
concat!("Size of: ", stringify!(PureVirtualIFace))
);
assert_eq!(
::std::mem::align_of::<PureVirtualIFace>(),
8usize,
concat!("Alignment of ", stringify!(PureVirtualIFace))
);
}
impl Default for PureVirtualIFace {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
#[repr(C)]
pub struct AnotherInterface__bindgen_vtable {
pub AnotherInterface_Baz: unsafe extern "C" fn(this: &mut AnotherInterface),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AnotherInterface {
pub vtable_: *const AnotherInterface__bindgen_vtable,
}
#[test]
fn bindgen_test_layout_AnotherInterface() {
assert_eq!(
::std::mem::size_of::<AnotherInterface>(),
8usize,
concat!("Size of: ", stringify!(AnotherInterface))
);
assert_eq!(
::std::mem::align_of::<AnotherInterface>(),
8usize,
concat!("Alignment of ", stringify!(AnotherInterface))
);
}
impl Default for AnotherInterface {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Implementation {
pub _base: PureVirtualIFace,
}
#[test]
fn bindgen_test_layout_Implementation() {
assert_eq!(
::std::mem::size_of::<Implementation>(),
8usize,
concat!("Size of: ", stringify!(Implementation))
);
assert_eq!(
::std::mem::align_of::<Implementation>(),
8usize,
concat!("Alignment of ", stringify!(Implementation))
);
}
impl Default for Implementation {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DoubleImpl {
pub _base: PureVirtualIFace,
pub _base_1: AnotherInterface,
}
#[test]
fn bindgen_test_layout_DoubleImpl() {
assert_eq!(
::std::mem::size_of::<DoubleImpl>(),
16usize,
concat!("Size of: ", stringify!(DoubleImpl))
);
assert_eq!(
::std::mem::align_of::<DoubleImpl>(),
8usize,
concat!("Alignment of ", stringify!(DoubleImpl))
);
}
impl Default for DoubleImpl {
fn default() -> Self {
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
unsafe {
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
s.assume_init()
}
}
}
7 changes: 6 additions & 1 deletion tests/expectations/tests/virtual_overloaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
)]

#[repr(C)]
pub struct C__bindgen_vtable(::std::os::raw::c_void);
pub struct C__bindgen_vtable {
pub C_do_thing:
unsafe extern "C" fn(this: &mut C, arg1: ::std::os::raw::c_char),
pub C_do_thing1:
unsafe extern "C" fn(this: &mut C, arg1: ::std::os::raw::c_int),
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct C {
Expand Down
4 changes: 3 additions & 1 deletion tests/expectations/tests/vtable_recursive_sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
)]

#[repr(C)]
pub struct Base__bindgen_vtable(::std::os::raw::c_void);
pub struct Base__bindgen_vtable {
pub Base_AsDerived: unsafe extern "C" fn(this: &mut Base) -> *mut Derived,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Base {
Expand Down
24 changes: 24 additions & 0 deletions tests/headers/virtual_interface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class PureVirtualIFace {
public:
virtual void Foo() = 0;
virtual void Bar(unsigned int) = 0;
};

class AnotherInterface {
public:
virtual void Baz() = 0;
};

class Implementation : public PureVirtualIFace {
public:
void Foo() override {}
void Bar(unsigned int) override {}
};

class DoubleImpl : public PureVirtualIFace, public AnotherInterface {
public:
void Foo() override {}
void Bar(unsigned int) override {}

void Baz() override {}
};