Skip to content

Commit e01216c

Browse files
authored
feat(bolero-generator): add support for calling generators from anywhere (#258)
1 parent 665f1c7 commit e01216c

17 files changed

+793
-246
lines changed

lib/bolero-engine/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ pub use bolero_generator::{
44
TypeGenerator, ValueGenerator,
55
};
66

7+
#[cfg(kani)]
8+
pub use bolero_generator::kani;
9+
710
pub type Seed = u128;
811

912
#[cfg(not(kani))]

lib/bolero-generator/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,21 @@ readme = "README.md"
1212
rust-version = "1.66.0"
1313

1414
[features]
15-
default = ["either", "std"]
15+
default = ["any", "either", "std"]
16+
any = ["getrandom", "rand_xoshiro", "std"]
1617
std = ["alloc", "either/use_std"]
1718
alloc = ["rand_core/alloc"]
1819

1920
[dependencies]
2021
arbitrary = { version = "1.0", optional = true }
2122
bolero-generator-derive = { version = "0.11", path = "../bolero-generator-derive" }
2223
either = { version = "1.5", default-features = false, optional = true }
24+
getrandom = { version = "0.2", optional = true }
2325
rand_core = { version = "0.6", default-features = false }
26+
rand_xoshiro = { version = "0.6", optional = true }
2427

2528
[dev-dependencies]
29+
insta = "1"
2630
rand = "0.8"
2731

2832
[lints.rust.unexpected_cfgs]

lib/bolero-generator/src/any.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use crate::{gen, TypeGenerator, ValueGenerator};
2+
3+
#[cfg(not(kani))]
4+
mod default;
5+
6+
#[cfg(any(kani, test))]
7+
#[cfg_attr(not(kani), allow(dead_code, unused_imports))]
8+
mod kani;
9+
10+
#[cfg(test)]
11+
mod tests;
12+
13+
pub mod scope {
14+
#[cfg(not(kani))]
15+
pub use super::default::*;
16+
#[cfg(kani)]
17+
pub use super::kani::*;
18+
}
19+
20+
pub use scope::{assume, fill_bytes, Error};
21+
22+
pub trait Any: ValueGenerator {
23+
fn any(&self) -> Self::Output;
24+
}
25+
26+
impl<G: 'static + ValueGenerator> Any for G {
27+
#[track_caller]
28+
fn any(&self) -> Self::Output {
29+
scope::any(self)
30+
}
31+
}
32+
33+
#[inline]
34+
pub fn any<T: TypeGenerator>() -> T {
35+
gen().any()
36+
}
37+
38+
pub trait AnySliceExt<T> {
39+
fn pick(&self) -> &T;
40+
}
41+
42+
impl<T> AnySliceExt<T> for [T] {
43+
#[inline]
44+
fn pick(&self) -> &T {
45+
let index = (0..self.len()).any();
46+
&self[index]
47+
}
48+
}
49+
50+
pub trait AnySliceMutExt<T> {
51+
fn shuffle(&mut self);
52+
fn fill_any(&mut self)
53+
where
54+
T: TypeGenerator;
55+
}
56+
57+
impl<T> AnySliceMutExt<T> for [T] {
58+
#[inline]
59+
fn shuffle(&mut self) {
60+
let max_dst = self.len().saturating_sub(1);
61+
for src in 0..max_dst {
62+
let dst = (src..=max_dst).any();
63+
self.swap(src, dst);
64+
}
65+
}
66+
67+
#[inline]
68+
fn fill_any(&mut self)
69+
where
70+
T: TypeGenerator,
71+
{
72+
for value in self {
73+
*value = any();
74+
}
75+
}
76+
}
77+
78+
#[cfg(feature = "alloc")]
79+
impl<T> AnySliceMutExt<T> for alloc::collections::VecDeque<T> {
80+
#[inline]
81+
fn shuffle(&mut self) {
82+
let max_dst = self.len().saturating_sub(1);
83+
for src in 0..max_dst {
84+
let dst = (src..=max_dst).any();
85+
self.swap(src, dst);
86+
}
87+
}
88+
89+
#[inline]
90+
fn fill_any(&mut self)
91+
where
92+
T: TypeGenerator,
93+
{
94+
for value in self {
95+
*value = any();
96+
}
97+
}
98+
}
99+
100+
#[inline]
101+
pub fn fill<T>(values: &mut [T])
102+
where
103+
T: TypeGenerator,
104+
{
105+
values.fill_any()
106+
}
107+
108+
#[inline]
109+
pub fn shuffle<T>(items: &mut [T]) {
110+
items.shuffle()
111+
}
112+
113+
#[inline]
114+
pub fn pick<T>(items: &[T]) -> &T {
115+
items.pick()
116+
}
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use crate::driver::object::{self, DynDriver, Object};
2+
use core::fmt;
3+
use std::cell::RefCell;
4+
5+
pub trait Scope: 'static + DynDriver + core::any::Any {
6+
fn borrowed(&mut self) -> object::Borrowed;
7+
}
8+
9+
impl<T> Scope for T
10+
where
11+
T: 'static + DynDriver + core::any::Any,
12+
{
13+
fn borrowed(&mut self) -> object::Borrowed {
14+
object::Borrowed(self)
15+
}
16+
}
17+
18+
type Type = Box<dyn Scope>;
19+
20+
thread_local! {
21+
static SCOPE: RefCell<Type> = RefCell::new(Box::new(Object(default())));
22+
}
23+
24+
fn default() -> impl crate::Driver {
25+
use rand_core::SeedableRng;
26+
use rand_xoshiro::Xoshiro128PlusPlus;
27+
28+
let mut seed = [42; 16];
29+
// make a best effort to get random seeds
30+
let _ = getrandom::getrandom(&mut seed);
31+
let rng = Xoshiro128PlusPlus::from_seed(seed);
32+
crate::driver::Rng::new(rng, &Default::default())
33+
}
34+
35+
fn set(value: Type) -> Type {
36+
SCOPE.with(|r| core::mem::replace(&mut *r.borrow_mut(), value))
37+
}
38+
39+
// protect against panics in the `with` function
40+
struct Prev(Option<Type>);
41+
42+
impl Prev {
43+
fn reset(mut self) -> Type {
44+
set(self.0.take().unwrap())
45+
}
46+
}
47+
48+
impl Drop for Prev {
49+
fn drop(&mut self) {
50+
if let Some(prev) = self.0.take() {
51+
let _ = set(prev);
52+
}
53+
}
54+
}
55+
56+
pub fn with<D, F, R>(driver: Box<D>, f: F) -> (Box<D>, R)
57+
where
58+
D: Scope,
59+
F: FnOnce() -> R,
60+
{
61+
let prev = Prev(Some(set(driver)));
62+
let res = f();
63+
let driver = prev.reset();
64+
let driver = if driver.type_id() == core::any::TypeId::of::<D>() {
65+
unsafe {
66+
let raw = Box::into_raw(driver);
67+
Box::from_raw(raw as *mut D)
68+
}
69+
} else {
70+
panic!(
71+
"invalid scope state; expected {}",
72+
core::any::type_name::<D>()
73+
)
74+
};
75+
(driver, res)
76+
}
77+
78+
fn borrow_with<F: FnOnce(&mut object::Borrowed) -> R, R>(f: F) -> R {
79+
SCOPE.with(|r| {
80+
let mut driver = r.borrow_mut();
81+
let mut driver = driver.borrowed();
82+
f(&mut driver)
83+
})
84+
}
85+
86+
#[track_caller]
87+
pub fn any<G: crate::ValueGenerator>(g: &G) -> G::Output {
88+
borrow_with(|driver| {
89+
g.generate(driver).unwrap_or_else(|| {
90+
std::panic::panic_any(Error {
91+
location: core::panic::Location::caller(),
92+
generator: core::any::type_name::<G>(),
93+
output: core::any::type_name::<G::Output>(),
94+
})
95+
})
96+
})
97+
}
98+
99+
#[track_caller]
100+
pub fn assume(condition: bool, message: &'static str) {
101+
if !condition {
102+
std::panic::panic_any(Error {
103+
location: core::panic::Location::caller(),
104+
generator: "<assume>",
105+
output: message,
106+
});
107+
}
108+
}
109+
110+
#[track_caller]
111+
pub fn fill_bytes(bytes: &mut [u8]) {
112+
borrow_with(|driver| {
113+
let len = bytes.len();
114+
let mut hint = || (len, Some(len));
115+
driver
116+
.0
117+
.gen_from_bytes(&mut hint, &mut |src: &[u8]| {
118+
if src.len() == len {
119+
bytes.copy_from_slice(src);
120+
Some(len)
121+
} else {
122+
None
123+
}
124+
})
125+
.unwrap_or_else(|| {
126+
std::panic::panic_any(Error {
127+
location: core::panic::Location::caller(),
128+
generator: "<fill_bytes>",
129+
output: "could not generate enough bytes",
130+
});
131+
})
132+
})
133+
}
134+
135+
#[derive(Clone)]
136+
pub struct Error {
137+
location: &'static core::panic::Location<'static>,
138+
generator: &'static str,
139+
output: &'static str,
140+
}
141+
142+
impl fmt::Debug for Error {
143+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144+
f.debug_struct("Error")
145+
.field("location", &self.location)
146+
.field("generator", &self.generator)
147+
.field("output", &self.output)
148+
.finish()
149+
}
150+
}
151+
152+
impl fmt::Display for Error {
153+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154+
write!(
155+
f,
156+
"Could not generate value of type {} at {}",
157+
self.output, self.location,
158+
)
159+
}
160+
}
161+
162+
impl std::error::Error for Error {}

lib/bolero-generator/src/any/kani.rs

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::cell::RefCell;
2+
3+
type Type = crate::kani::Driver;
4+
5+
pub use core::convert::Infallible as Error;
6+
7+
/// Kani doesn't support thread_locals so use a static global instead
8+
static mut CURRENT: Type = Type {
9+
depth: 0,
10+
max_depth: crate::driver::Options::DEFAULT_MAX_DEPTH,
11+
};
12+
13+
fn get() -> Type {
14+
unsafe {
15+
let depth = CURRENT.depth;
16+
let max_depth = CURRENT.max_depth;
17+
18+
Type { depth, max_depth }
19+
}
20+
}
21+
22+
fn set(value: Type) -> Type {
23+
let prev = get();
24+
25+
unsafe {
26+
CURRENT.depth = value.depth;
27+
CURRENT.max_depth = value.max_depth;
28+
}
29+
30+
prev
31+
}
32+
33+
pub fn with<F, R>(driver: Type, f: F) -> (Type, R)
34+
where
35+
F: FnOnce() -> R,
36+
{
37+
let prev = set(driver);
38+
let res = f();
39+
let driver = set(prev);
40+
(driver, res)
41+
}
42+
43+
fn borrow_with<F: FnOnce(&mut Type) -> R, R>(f: F) -> R {
44+
let mut driver = unsafe {
45+
let depth = CURRENT.depth;
46+
let max_depth = CURRENT.max_depth;
47+
48+
Type { depth, max_depth }
49+
};
50+
let result = f(&mut driver);
51+
set(driver);
52+
result
53+
}
54+
55+
pub fn any<G: crate::ValueGenerator>(g: &G) -> G::Output {
56+
borrow_with(|driver| {
57+
let v = g.generate(driver);
58+
assume(v.is_some(), "generator should return at least one value");
59+
v.unwrap()
60+
})
61+
}
62+
63+
pub fn fill_bytes(bytes: &mut [u8]) {
64+
for dst in bytes {
65+
#[cfg(kani)]
66+
let src = ::kani::any();
67+
68+
#[cfg(not(kani))]
69+
let src = 0;
70+
71+
*dst = src;
72+
}
73+
}
74+
75+
#[inline]
76+
pub fn assume(condition: bool, message: &'static str) {
77+
#[cfg(kani)]
78+
::kani::assume(condition);
79+
80+
let _ = condition;
81+
let _ = message;
82+
}

0 commit comments

Comments
 (0)