Skip to content
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
15 changes: 2 additions & 13 deletions ctest/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,18 +576,15 @@ fn static_test_ident(ident: &str) -> BoxStr {
/// Wrap methods that depend on both ffi items and the generator.
pub(crate) struct TranslateHelper<'a> {
filtered_ffi_items: FfiItems,
ffi_items: &'a FfiItems,
generator: &'a TestGenerator,
translator: Translator<'a>,
}

impl<'a> TranslateHelper<'a> {
/// Create a new translation helper.
pub(crate) fn new(ffi_items: &'a FfiItems, generator: &'a TestGenerator) -> Self {
let filtered_ffi_items = ffi_items.clone();
let mut helper = Self {
filtered_ffi_items,
ffi_items,
filtered_ffi_items: ffi_items.clone(),
generator,
translator: Translator::new(ffi_items, generator),
};
Expand Down Expand Up @@ -697,15 +694,7 @@ impl<'a> TranslateHelper<'a> {
)
})?;

let item = if self.ffi_items.contains_struct(&ty) {
MapInput::StructType(&ty)
} else if self.ffi_items.contains_union(ident) {
MapInput::UnionType(&ty)
} else if self.generator.c_enums.iter().any(|f| f(&ty)) {
MapInput::CEnumType(&ty)
} else {
MapInput::Type(&ty)
};
let item = self.translator.map_rust_name_to_c(&ty);

Ok(self.generator.rty_to_cty(item))
}
Expand Down
20 changes: 13 additions & 7 deletions ctest/src/translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,7 @@ impl<'a> Translator<'a> {
}

let name = last.ident.to_string();
let item = if self.ffi_items.contains_struct(&name) {
MapInput::StructType(&name)
} else if self.ffi_items.contains_union(&name) {
MapInput::UnionType(&name)
} else {
MapInput::Type(&name)
};
let item = self.map_rust_name_to_c(&name);

Ok(cdecl::named(
&self.generator.rty_to_cty(item),
Expand Down Expand Up @@ -287,6 +281,18 @@ impl<'a> Translator<'a> {
_ => false,
}
}

pub(crate) fn map_rust_name_to_c<'name>(&self, name: &'name str) -> MapInput<'name> {
if self.ffi_items.contains_struct(name) {
MapInput::StructType(name)
} else if self.ffi_items.contains_union(name) {
MapInput::UnionType(name)
} else if self.generator.c_enums.iter().any(|f| f(name)) {
MapInput::CEnumType(name)
} else {
MapInput::Type(name)
}
}
}

/// Translate mutability from Rust to C.
Expand Down
15 changes: 8 additions & 7 deletions ctest/tests/input/simple.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ typedef unsigned long gregset_t[32];

Byte byte = 0x42;

enum Color
{
RED,
BLUE,
GREEN
};

struct Person
{
const char *name;
uint8_t age;
void (*job)(uint8_t, const char *);
enum Color favorite_color;
};

union Word
Expand All @@ -22,12 +30,5 @@ union Word
#define A "abc"
#define C_B "bac"

enum Color
{
RED,
BLUE,
GREEN
};

extern void *calloc(size_t num, size_t size);
extern Byte byte;
19 changes: 19 additions & 0 deletions ctest/tests/input/simple.out.with-renames.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ CTEST_EXTERN uint64_t ctest_size_of__Person__job(void) {
return sizeof(((struct Person){}).job);
}

// Return the offset of a struct/union field.
CTEST_EXTERN uint64_t ctest_offset_of__Person__favorite_color(void) {
return offsetof(struct Person, favorite_color);
}

// Return the size of a struct/union field.
CTEST_EXTERN uint64_t ctest_size_of__Person__favorite_color(void) {
return sizeof(((struct Person){}).favorite_color);
}

// Return the offset of a struct/union field.
CTEST_EXTERN uint64_t ctest_offset_of__Word__word(void) {
return offsetof(union Word, word);
Expand Down Expand Up @@ -170,6 +180,15 @@ ctest_field_ptr__Person__job(struct Person *b) {
return &b->job;
}

// Return a pointer to a struct/union field.
// This field can have a normal data type, or it could be a function pointer or an array, which
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
typedef enum Color *ctest_field_ty__Person__favorite_color;
CTEST_EXTERN ctest_field_ty__Person__favorite_color
ctest_field_ptr__Person__favorite_color(struct Person *b) {
return &b->favorite_color;
}

// Return a pointer to a struct/union field.
// This field can have a normal data type, or it could be a function pointer or an array, which
// have different syntax. A typedef is used for convenience, but the syntax must be precomputed.
Expand Down
59 changes: 56 additions & 3 deletions ctest/tests/input/simple.out.with-renames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,32 @@ mod generated_tests {
"field size job of Person");
}

/// Make sure that the offset and size of a field in a struct/union is the same.
pub fn ctest_field_size_offset_Person_favorite_color() {
extern "C" {
fn ctest_offset_of__Person__favorite_color() -> u64;
fn ctest_size_of__Person__favorite_color() -> u64;
}

let uninit_ty = MaybeUninit::<Person>::zeroed();
let uninit_ty = uninit_ty.as_ptr();

// SAFETY: we assume the field access doesn't wrap
let ty_ptr = unsafe { &raw const (*uninit_ty).favorite_color };
// SAFETY: we assume that all zeros is a valid bitpattern for `ty_ptr`, otherwise the
// test should be skipped.
let val = unsafe { ty_ptr.read_unaligned() };

// SAFETY: FFI call with no preconditions
let ctest_field_offset = unsafe { ctest_offset_of__Person__favorite_color() };
check_same(offset_of!(Person, favorite_color) as u64, ctest_field_offset,
"field offset favorite_color of Person");
// SAFETY: FFI call with no preconditions
let ctest_field_size = unsafe { ctest_size_of__Person__favorite_color() };
check_same(size_of_val(&val) as u64, ctest_field_size,
"field size favorite_color of Person");
}

/// Make sure that the offset and size of a field in a struct/union is the same.
pub fn ctest_field_size_offset_Word_word() {
extern "C" {
Expand Down Expand Up @@ -454,6 +480,24 @@ mod generated_tests {
"field type job of Person");
}

/// Tests if the pointer to the field is the same in Rust and C.
pub fn ctest_field_ptr_Person_favorite_color() {
extern "C" {
fn ctest_field_ptr__Person__favorite_color(a: *const Person) -> *mut u8;
}

let uninit_ty = MaybeUninit::<Person>::zeroed();
let ty_ptr = uninit_ty.as_ptr();
// SAFETY: We don't read `field_ptr`, only compare the pointer itself.
// The assumption is made that this does not wrap the address space.
let field_ptr = unsafe { &raw const ((*ty_ptr).favorite_color) };

// SAFETY: FFI call with no preconditions
let ctest_field_ptr = unsafe { ctest_field_ptr__Person__favorite_color(ty_ptr) };
check_same(field_ptr.cast(), ctest_field_ptr,
"field type favorite_color of Person");
}

/// Tests if the pointer to the field is the same in Rust and C.
pub fn ctest_field_ptr_Word_word() {
extern "C" {
Expand Down Expand Up @@ -720,7 +764,7 @@ mod generated_tests {
/// if there are no fields, then everything is padding, if there are fields, then we have to
/// go through each field and figure out the padding.
fn roundtrip_padding__Person() -> Vec<bool> {
if 3 == 0 {
if 4 == 0 {
// FIXME(ctest): What if it's an alias to a struct/union?
return vec![!false; size_of::<Person>()]
}
Expand Down Expand Up @@ -753,6 +797,13 @@ mod generated_tests {
let size = size_of_val(&val);
let off = offset_of!(Person, job);
v.push((off, size));

let ty_ptr = unsafe { &raw const ((*bar).favorite_color) };
let val = unsafe { ty_ptr.read_unaligned() };

let size = size_of_val(&val);
let off = offset_of!(Person, favorite_color);
v.push((off, size));
// This vector contains `true` if the byte is padding and `false` if the byte is not
// padding. Initialize all bytes as:
// - padding if we have fields, this means that only the fields will be checked
Expand Down Expand Up @@ -1007,11 +1058,11 @@ fn main() {
// FIXME(ctest): Maybe consider running the tests in parallel, since everything is independent
// and we already use atomics.
fn run_all() {
ctest_const_cstr_A();
ctest_const_cstr_B();
ctest_const_RED();
ctest_const_BLUE();
ctest_const_GREEN();
ctest_const_cstr_A();
ctest_const_cstr_B();
ctest_size_align_Byte();
ctest_size_align_gregset_t();
ctest_size_align_Color();
Expand All @@ -1021,11 +1072,13 @@ fn run_all() {
ctest_field_size_offset_Person_name();
ctest_field_size_offset_Person_age();
ctest_field_size_offset_Person_job();
ctest_field_size_offset_Person_favorite_color();
ctest_field_size_offset_Word_word();
ctest_field_size_offset_Word_byte();
ctest_field_ptr_Person_name();
ctest_field_ptr_Person_age();
ctest_field_ptr_Person_job();
ctest_field_ptr_Person_favorite_color();
ctest_field_ptr_Word_word();
ctest_field_ptr_Word_byte();
ctest_roundtrip_Byte();
Expand Down
11 changes: 6 additions & 5 deletions ctest/tests/input/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ pub type Byte = u8;
// This should be automatically skipped for roundtripping.
pub type gregset_t = [c_ulong; 32];

pub type Color = c_int;
pub const RED: Color = 0;
pub const BLUE: Color = RED + 1;
pub const GREEN: Color = BLUE + 1;

#[repr(C)]
pub struct Person {
pub name: *const c_char,
pub age: u8,
pub job: extern "C" fn(u8, *const c_char),
pub favorite_color: Color,
}

#[repr(C)]
Expand All @@ -21,11 +27,6 @@ pub union Word {
const A: *const c_char = c"abc".as_ptr();
const B: *const c_char = c"bac".as_ptr();

pub type Color = c_int;
pub const RED: Color = 0;
pub const BLUE: Color = RED + 1;
pub const GREEN: Color = BLUE + 1;

unsafe extern "C" {
pub fn calloc(num: usize, size: usize) -> *mut c_void;

Expand Down
Loading