Skip to content

Commit

Permalink
Add support for verifying volatile pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
gnzlbg committed Feb 22, 2019
1 parent c908271 commit a349575
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ctest"
version = "0.2.10"
version = "0.2.11"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
Expand Down
80 changes: 72 additions & 8 deletions src/lib.rs
Expand Up @@ -54,13 +54,29 @@ macro_rules! t {
}

/// Programming language
#[derive(Debug)]
pub enum Lang {
/// The C programming language.
C,
/// The C++ programming language.
CXX,
}

/// A kind of item to which the C volatile qualifier could apply.
#[derive(Debug)]
pub enum VolatileItemKind {
/// A struct field (struct_name, field_name)
StructField(String, String),
/// An extern static
Static(String),
/// N-th function argument
FunctionArg(String, usize),
/// Function return type
FunctionRet(String),
#[doc(hidden)]
__Other,
}

/// A builder used to generate a test suite.
///
/// This builder has a number of configuration options which modify how the
Expand All @@ -76,6 +92,7 @@ pub struct TestGenerator {
defines: Vec<(String, Option<String>)>,
cfg: Vec<(String, Option<String>)>,
verbose_skip: bool,
is_volatile: Box<Fn(VolatileItemKind) -> bool>,
skip_fn: Box<Fn(&str) -> bool>,
skip_fn_ptrcheck: Box<Fn(&str) -> bool>,
skip_static: Box<Fn(&str) -> bool>,
Expand Down Expand Up @@ -129,6 +146,7 @@ impl TestGenerator {
defines: Vec::new(),
cfg: Vec::new(),
verbose_skip: false,
is_volatile: Box::new(|_| false),
skip_fn: Box::new(|_| false),
skip_fn_ptrcheck: Box::new(|_| false),
skip_static: Box::new(|_| false),
Expand Down Expand Up @@ -404,6 +422,34 @@ impl TestGenerator {
self
}

/// Is volatile?
///
/// The closure given takes a `VolatileKind` denoting a particular item that
/// could be volatile, and returns whether this is the case.
///
/// # Examples
///
/// ```no_run
/// use ctest::{TestGenerator, VolatileItemKind::StructField};
///
/// let mut cfg = TestGenerator::new();
/// cfg.is_volatile(|i| {
/// match i {
/// StructField(ref s, ref f)
/// if s == "foo_struct" && f == "foo_field"
/// => true,
/// _ => false,
/// }});
/// ```
pub fn is_volatile<F>(&mut self, f: F) -> &mut Self
where
F: Fn(VolatileItemKind) -> bool + 'static,
{
self.is_volatile = Box::new(f);
self
}


/// Configures how Rust `const`s names are translated to C.
///
/// The closure is given a Rust `const` name. The name of the corresponding
Expand Down Expand Up @@ -1192,7 +1238,10 @@ impl<'a> Generator<'a> {
}

let sig = format!("__test_field_type_{}_{}({}* b)", ty, name, cty);
let sig = self.csig_returning_ptr(&field.ty, &sig);
let mut sig = self.csig_returning_ptr(&field.ty, &sig);
if (self.opts.is_volatile)(VolatileItemKind::StructField(ty.to_string(), name.to_string())) {
sig = format!("volatile {}", sig);
}
t!(writeln!(
self.c,
r#"
Expand Down Expand Up @@ -1448,13 +1497,22 @@ impl<'a> Generator<'a> {
let args = if args.is_empty() && !variadic {
"void".to_string()
} else {
args.iter()
.map(|a| self.rust_ty_to_c_ty(a))
args.iter().enumerate()
.map(|(idx, a)| {
let mut arg = self.rust_ty_to_c_ty(a);
if (self.opts.is_volatile)(VolatileItemKind::FunctionArg(name.to_string(), idx)) {
arg = format!("volatile {}", arg);
}
arg
})
.collect::<Vec<_>>()
.join(", ")
+ if variadic { ", ..." } else { "" }
};
let c_ret = self.rust_ty_to_c_ty(ret);
let mut c_ret = self.rust_ty_to_c_ty(ret);
if (self.opts.is_volatile)(VolatileItemKind::FunctionRet(name.to_string())) {
c_ret = format!("volatile {}", c_ret);
}
let abi = self.abi2str(abi);
t!(writeln!(
self.c,
Expand Down Expand Up @@ -1516,15 +1574,15 @@ impl<'a> Generator<'a> {
let c_name = c_name.unwrap_or_else(|| name.to_string());

if rust_ty.contains("extern fn") {
let c_ty = c_ty.replacen("(*)", &format!("(* __test_static_{}(void))", name), 1);
t!(writeln!(
let sig = c_ty.replacen("(*)", &format!("(* __test_static_{}(void))", name), 1);
t!(writeln!(
self.c,
r#"
{ty} {{
{sig} {{
return {c_name};
}}
"#,
ty = c_ty,
sig = sig,
c_name = c_name
));
t!(writeln!(
Expand Down Expand Up @@ -1593,6 +1651,12 @@ impl<'a> Generator<'a> {
ty = rust_ty
));
} else {
let c_ty = if (self.opts.is_volatile)(VolatileItemKind::Static(name.to_owned())) {
format!("volatile {}", c_ty)
} else {
c_ty.to_owned()
};

t!(writeln!(
self.c,
r#"
Expand Down
15 changes: 15 additions & 0 deletions testcrate/build.rs
Expand Up @@ -25,6 +25,7 @@ fn main() {
t if is_union => format!("union {}", t),
t => t.to_string(),
})
.is_volatile(t1_volatile)
.generate("src/t1.rs", "t1gen.rs");
ctest::TestGenerator::new()
.header("t2.h")
Expand All @@ -48,6 +49,7 @@ fn main() {
t if is_union => format!("union {}", t),
t => t.to_string(),
})
.is_volatile(t1_volatile)
.generate("src/t1.rs", "t1gen_cxx.rs");
ctest::TestGenerator::new()
.header("t2.h")
Expand All @@ -61,3 +63,16 @@ fn main() {
})
.generate("src/t2.rs", "t2gen_cxx.rs");
}

fn t1_volatile(i: ctest::VolatileItemKind) -> bool {
use ctest::VolatileItemKind::*;
match i {
StructField(ref n, ref f) if n == "V" && f == "v" => true,
Static(ref n) if n == "vol_ptr" => true,
FunctionArg(ref n, 0) if n == "T1_vol0" => true,
FunctionArg(ref n, 1) if n == "T1_vol2" => true,
FunctionRet(ref n) if n == "T1_vol1" || n == "T1_vol2" => true,
Static(ref n) if n == "T1_fn_ptr_vol" => true,
_ => false,
}
}
7 changes: 7 additions & 0 deletions testcrate/src/t1.c
Expand Up @@ -58,3 +58,10 @@ const int32_t* T1_const_opt_const_ref = NULL;
void (*const T1_opt_fn1)(void) = baz;
uint32_t (*(*T1_opt_fn2)(uint8_t))(uint16_t) = nested;
uint32_t (*(*T1_opt_fn3)(uint8_t(*arg0)(uint8_t), uint16_t(*arg1)(uint16_t)))(uint16_t) = nested2;

volatile uint8_t* vol_ptr = NULL;
void* T1_vol0(volatile void* x, void* a) { return a? a: (void*)x; }
volatile void* T1_vol1(void* x, void* b) { return b? (volatile void*)x : (volatile void*)x; }
volatile void* T1_vol2(void* c, volatile void* x) { return c? x : x; }

uint8_t (* volatile T1_fn_ptr_vol)(uint8_t, uint8_t) = foo;
16 changes: 16 additions & 0 deletions testcrate/src/t1.h
Expand Up @@ -132,3 +132,19 @@ struct Pack {
#ifdef _MSC_VER
#pragma pack(pop)
#endif

// volatile pointers in struct fields:
struct V {
volatile uint8_t* v;
};

// volatile pointers in externs:
extern volatile uint8_t* vol_ptr;

// volatile pointers in function arguments:
void* T1_vol0(volatile void*, void*);
volatile void* T1_vol1(void*, void*);
volatile void* T1_vol2(void*, volatile void*);

// volatile function pointers:
uint8_t (*volatile T1_fn_ptr_vol)(uint8_t, uint8_t);
13 changes: 13 additions & 0 deletions testcrate/src/t1.rs
Expand Up @@ -146,3 +146,16 @@ pub struct Pack {
pub a: u8,
pub b: u16,
}

#[repr(C)]
pub struct V {
pub v: *mut u8,
}

extern "C" {
pub static mut vol_ptr: *mut u8;
pub fn T1_vol0(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
pub fn T1_vol1(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
pub fn T1_vol2(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
pub static T1_fn_ptr_vol : Option<unsafe extern "C" fn(u8, u8) -> u8>;
}

0 comments on commit a349575

Please sign in to comment.