Dominik Maier 663a33168e
Associated types for Corpus, State (#767)
* Associated types for Corpus, State

* cleanup

* fix no_std

* drop unused clauses

* Corpus

* cleanup

* adding things

* fixed fuzzer

* remove phantom data

* python

* progress?

* more more

* oof

* wow it builds?

* python fixes, tests

* fix python fun

* black fmt for python

* clippy, added Nop things

* fixes

* fix merge

* make it compile (#836)

* doc-test fixes, prelude-b-gone for cargo-hack compat

* fixes for windows, concolic

* really fix windows, maybe

* imagine using windows

* ...

* elide I generic when used with S: State

* Elide many, many generics, but at what cost?

* progress on push

* Constraint HasCorpus, HasSolutions at trait definition

* remove unused feature

* remove unstable usage since we constrained HasCorpus at definition

* compiled, but still no type inference for MaxMapFeedback

* cleanup inprocess

* resolve some std conflicts

* simplify map

* undo unnecessary cfg specification

* fix breaking test case for CI on no-std

* fix concolic build failures

* fix macos build

* fixes for windows build

* timeout fixes for windows build

* fix pybindings issues

* fixup qemu

* fix outstanding local build issues

* maybe fix windows inprocess

* doc fixes

* unbridled fury

* de-associate State from Feedback, replace with generic as AT inference is not sufficient to derive specialisation for MapFeedback

* merge update

* refactor + speed up fuzzer builds by sharing build work

* cleanup lingering compiler errors

* lol missed one

* revert QEMU-Nyx change, not sure how I did that

* move HasInput to inputs

* HasInput => KnowsInput

* update bounds to enforce via associated types

* disentangle observers with fuzzer

* revert --target; update some fuzzers to match new API

* resolve outstanding fuzzer build blockers (that I can run on my system)

* fixes for non-linux unixes

* fix for windows

* Knows => Uses, final fixes for windows

* <guttural screaming>

* fixes for concolic

* loosen bound for frida executor so windows builds correctly

* cleanup generics for eventmanager/eventprocessor to drop observers requirement

* improve inference over fuzz_one and friends

* update migration notes

* fixes for python bindings

* fixes for generic counts in event managers

* finish migration notes

* post-merge fix

Co-authored-by: Addison Crump <addison.crump@cispa.de>
2022-10-24 03:22:26 +02:00

1205 lines
40 KiB
Rust

//! Observers give insights about runs of a target, such as coverage, timing, stack depth, and more.
pub mod map;
pub use map::*;
pub mod cmp;
pub use cmp::*;
#[cfg(feature = "std")]
pub mod stdio;
#[cfg(feature = "std")]
pub use stdio::{StdErrObserver, StdOutObserver};
#[cfg(feature = "std")]
pub mod stacktrace;
#[cfg(feature = "std")]
pub use stacktrace::*;
pub mod concolic;
// Rust is breaking this with 'error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `type_id`' and so we disable this component for the moment
//#[cfg(unstable_feature)]
//pub mod owned;
//#[cfg(unstable_feature)]
//pub use owned::*;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{fmt::Debug, time::Duration};
use serde::{Deserialize, Serialize};
use crate::{
bolts::{
current_time,
ownedref::OwnedRefMut,
tuples::{MatchName, Named},
},
executors::ExitKind,
inputs::UsesInput,
state::UsesState,
Error,
};
/// Observers observe different information about the target.
/// They can then be used by various sorts of feedback.
pub trait Observer<S>: Named + Debug
where
S: UsesInput,
{
/// The testcase finished execution, calculate any changes.
/// Reserved for future use.
#[inline]
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
/// Called right before execution starts.
#[inline]
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
/// Called right after execution finishes.
#[inline]
fn post_exec(
&mut self,
_state: &mut S,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
}
/// Called right before execution starts in the child process, if any.
#[inline]
fn pre_exec_child(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
/// Called right after execution finishes in the child process, if any.
#[inline]
fn post_exec_child(
&mut self,
_state: &mut S,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
}
/// If this observer observes `stdout`
#[inline]
fn observes_stdout(&mut self) -> bool {
false
}
/// If this observer observes `stderr`
#[inline]
fn observes_stderr(&mut self) -> bool {
false
}
/// React to new `stdout`
/// To use this, always return `true` from `observes_stdout`
#[inline]
#[allow(unused_variables)]
fn observe_stdout(&mut self, stdout: &str) {}
/// React to new `stderr`
/// To use this, always return `true` from `observes_stderr`
#[inline]
#[allow(unused_variables)]
fn observe_stderr(&mut self, stderr: &str) {}
}
/// Defines the observer type shared across traits of the type.
/// Needed for consistency across HasCorpus/HasSolutions and friends.
pub trait UsesObservers: UsesState {
/// The observers type
type Observers: ObserversTuple<Self::State>;
}
/// A haskell-style tuple of observers
pub trait ObserversTuple<S>: MatchName + Debug
where
S: UsesInput,
{
/// This is called right before the next execution.
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error>;
/// This is called right after the last execution
fn post_exec_all(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error>;
/// This is called right before the next execution in the child process, if any.
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error>;
/// This is called right after the last execution in the child process, if any.
fn post_exec_child_all(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error>;
/// Returns true if a `stdout` observer was added to the list
fn observes_stdout(&mut self) -> bool;
/// Returns true if a `stderr` observer was added to the list
fn observes_stderr(&mut self) -> bool;
/// Runs `observe_stdout` for all stdout observers in the list
fn observe_stdout(&mut self, stdout: &str);
/// Runs `observe_stderr` for all stderr observers in the list
fn observe_stderr(&mut self, stderr: &str);
}
impl<S> ObserversTuple<S> for ()
where
S: UsesInput,
{
fn pre_exec_all(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
fn post_exec_all(
&mut self,
_state: &mut S,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
}
fn pre_exec_child_all(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
fn post_exec_child_all(
&mut self,
_state: &mut S,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
Ok(())
}
/// Returns true if a `stdout` observer was added to the list
#[inline]
fn observes_stdout(&mut self) -> bool {
false
}
/// Returns true if a `stderr` observer was added to the list
#[inline]
fn observes_stderr(&mut self) -> bool {
false
}
/// Runs `observe_stdout` for all stdout observers in the list
#[inline]
#[allow(unused_variables)]
fn observe_stdout(&mut self, stdout: &str) {}
/// Runs `observe_stderr` for all stderr observers in the list
#[inline]
#[allow(unused_variables)]
fn observe_stderr(&mut self, stderr: &str) {}
}
impl<Head, Tail, S> ObserversTuple<S> for (Head, Tail)
where
Head: Observer<S>,
Tail: ObserversTuple<S>,
S: UsesInput,
{
fn pre_exec_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.0.pre_exec(state, input)?;
self.1.pre_exec_all(state, input)
}
fn post_exec_all(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.0.post_exec(state, input, exit_kind)?;
self.1.post_exec_all(state, input, exit_kind)
}
fn pre_exec_child_all(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.0.pre_exec_child(state, input)?;
self.1.pre_exec_child_all(state, input)
}
fn post_exec_child_all(
&mut self,
state: &mut S,
input: &S::Input,
exit_kind: &ExitKind,
) -> Result<(), Error> {
self.0.post_exec_child(state, input, exit_kind)?;
self.1.post_exec_child_all(state, input, exit_kind)
}
/// Returns true if a `stdout` observer was added to the list
#[inline]
fn observes_stdout(&mut self) -> bool {
self.0.observes_stdout() || self.1.observes_stdout()
}
/// Returns true if a `stderr` observer was added to the list
#[inline]
fn observes_stderr(&mut self) -> bool {
self.0.observes_stderr() || self.1.observes_stderr()
}
/// Runs `observe_stdout` for all stdout observers in the list
#[inline]
fn observe_stdout(&mut self, stdout: &str) {
self.0.observe_stdout(stdout);
}
/// Runs `observe_stderr` for all stderr observers in the list
#[inline]
fn observe_stderr(&mut self, stderr: &str) {
self.1.observe_stderr(stderr);
}
}
/// A trait for [`Observer`]`s` with a hash field
pub trait ObserverWithHashField {
/// get the value of the hash field
fn hash(&self) -> &Option<u64>;
/// update the hash field with the given value
fn update_hash(&mut self, hash: u64);
/// clears the current value of the hash and sets it to None
fn clear_hash(&mut self);
}
/// A simple observer, just overlooking the runtime of the target.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimeObserver {
name: String,
start_time: Duration,
last_runtime: Option<Duration>,
}
impl TimeObserver {
/// Creates a new [`TimeObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {
name: name.to_string(),
start_time: Duration::from_secs(0),
last_runtime: None,
}
}
/// Gets the runtime for the last execution of this target.
#[must_use]
pub fn last_runtime(&self) -> &Option<Duration> {
&self.last_runtime
}
}
impl<S> Observer<S> for TimeObserver
where
S: UsesInput,
{
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.last_runtime = None;
self.start_time = current_time();
Ok(())
}
fn post_exec(
&mut self,
_state: &mut S,
_input: &S::Input,
_exit_kind: &ExitKind,
) -> Result<(), Error> {
self.last_runtime = current_time().checked_sub(self.start_time);
Ok(())
}
}
impl Named for TimeObserver {
fn name(&self) -> &str {
&self.name
}
}
/// A simple observer with a list of things.
#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "T: serde::de::DeserializeOwned")]
pub struct ListObserver<'a, T>
where
T: Debug + Serialize,
{
name: String,
/// The list
list: OwnedRefMut<'a, Vec<T>>,
}
impl<'a, T> ListObserver<'a, T>
where
T: Debug + Serialize + serde::de::DeserializeOwned,
{
/// Creates a new [`ListObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str, list: &'a mut Vec<T>) -> Self {
Self {
name: name.to_string(),
list: OwnedRefMut::Ref(list),
}
}
/// Get a list ref
#[must_use]
pub fn list(&self) -> &Vec<T> {
self.list.as_ref()
}
/// Get a list mut
#[must_use]
pub fn list_mut(&mut self) -> &mut Vec<T> {
self.list.as_mut()
}
}
impl<'a, S, T> Observer<S> for ListObserver<'a, T>
where
S: UsesInput,
T: Debug + Serialize + serde::de::DeserializeOwned,
{
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.list.as_mut().clear();
Ok(())
}
}
impl<'a, T> Named for ListObserver<'a, T>
where
T: Debug + Serialize + serde::de::DeserializeOwned,
{
fn name(&self) -> &str {
&self.name
}
}
/// `Observer` Python bindings
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
use std::cell::UnsafeCell;
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
use super::{Debug, Observer, ObserversTuple, String, Vec};
use crate::{
bolts::tuples::{type_eq, MatchName, Named},
executors::{pybind::PythonExitKind, ExitKind},
inputs::{BytesInput, HasBytesVec},
observers::map::pybind::{
PythonMapObserverI16, PythonMapObserverI32, PythonMapObserverI64, PythonMapObserverI8,
PythonMapObserverU16, PythonMapObserverU32, PythonMapObserverU64, PythonMapObserverU8,
PythonMapObserverWrapperI16, PythonMapObserverWrapperI32, PythonMapObserverWrapperI64,
PythonMapObserverWrapperI8, PythonMapObserverWrapperU16, PythonMapObserverWrapperU32,
PythonMapObserverWrapperU64, PythonMapObserverWrapperU8,
},
state::pybind::{PythonStdState, PythonStdStateWrapper},
Error,
};
#[derive(Debug)]
pub struct PyObjectObserver {
inner: PyObject,
name: UnsafeCell<String>,
}
impl Clone for PyObjectObserver {
fn clone(&self) -> PyObjectObserver {
PyObjectObserver {
inner: self.inner.clone(),
name: UnsafeCell::new(String::new()),
}
}
}
impl PyObjectObserver {
#[must_use]
pub fn new(obj: PyObject) -> Self {
PyObjectObserver {
inner: obj,
name: UnsafeCell::new(String::new()),
}
}
}
crate::impl_serde_pyobjectwrapper!(PyObjectObserver, inner);
impl Named for PyObjectObserver {
fn name(&self) -> &str {
let s = Python::with_gil(|py| -> PyResult<String> {
let s: String = self.inner.call_method0(py, "name")?.extract(py)?;
Ok(s)
})
.unwrap();
unsafe {
*self.name.get() = s;
&*self.name.get()
}
}
}
impl Observer<PythonStdState> for PyObjectObserver {
fn flush(&mut self) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method0(py, "flush")?;
Ok(())
})
.unwrap();
Ok(())
}
fn pre_exec(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method1(
py,
"pre_exec",
(PythonStdStateWrapper::wrap(state), input.bytes()),
)?;
Ok(())
})?;
Ok(())
}
fn post_exec(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method1(
py,
"post_exec",
(
PythonStdStateWrapper::wrap(state),
input.bytes(),
PythonExitKind::from(*exit_kind),
),
)?;
Ok(())
})?;
Ok(())
}
fn pre_exec_child(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method1(
py,
"pre_exec_child",
(PythonStdStateWrapper::wrap(state), input.bytes()),
)?;
Ok(())
})?;
Ok(())
}
fn post_exec_child(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
Python::with_gil(|py| -> PyResult<()> {
self.inner.call_method1(
py,
"post_exec_child",
(
PythonStdStateWrapper::wrap(state),
input.bytes(),
PythonExitKind::from(*exit_kind),
),
)?;
Ok(())
})?;
Ok(())
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum PythonObserverWrapper {
MapI8(Py<PythonMapObserverI8>),
MapI16(Py<PythonMapObserverI16>),
MapI32(Py<PythonMapObserverI32>),
MapI64(Py<PythonMapObserverI64>),
MapU8(Py<PythonMapObserverU8>),
MapU16(Py<PythonMapObserverU16>),
MapU32(Py<PythonMapObserverU32>),
MapU64(Py<PythonMapObserverU64>),
Python(PyObjectObserver),
}
#[pyclass(unsendable, name = "Observer")]
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Serialize, Deserialize, Clone, Debug)]
/// Observer Trait binding
pub struct PythonObserver {
pub wrapper: PythonObserverWrapper,
}
macro_rules! unwrap_me {
($wrapper:expr, $name:ident, $body:block) => {
match &$wrapper {
PythonObserverWrapper::MapI8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperI8,
borrowed.wrapper,
$name,
$body
))
})
.unwrap(),
PythonObserverWrapper::MapI16(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperI16,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapI32(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperI32,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapI64(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperI64,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperU8,
borrowed.wrapper,
$name,
$body
))
})
.unwrap(),
PythonObserverWrapper::MapU16(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperU16,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU32(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperU32,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU64(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let borrowed = py_wrapper.borrow(py);
Ok(crate::mapob_unwrap_me!(
PythonMapObserverWrapperU64,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::Python(py_wrapper) => {
let $name = py_wrapper;
$body
}
}
};
}
macro_rules! unwrap_me_mut {
($wrapper:expr, $name:ident, $body:block) => {
match &mut $wrapper {
PythonObserverWrapper::MapI8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperI8,
borrowed.wrapper,
$name,
$body
))
})
.unwrap(),
PythonObserverWrapper::MapI16(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperI16,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapI32(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperI32,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapI64(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperI64,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU8(py_wrapper) => Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperU8,
borrowed.wrapper,
$name,
$body
))
})
.unwrap(),
PythonObserverWrapper::MapU16(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperU16,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU32(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperU32,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::MapU64(py_wrapper) => {
Python::with_gil(|py| -> PyResult<_> {
let mut borrowed = py_wrapper.borrow_mut(py);
Ok(crate::mapob_unwrap_me_mut!(
PythonMapObserverWrapperU64,
borrowed.wrapper,
$name,
$body
))
})
.unwrap()
}
PythonObserverWrapper::Python(py_wrapper) => {
let $name = py_wrapper;
$body
}
}
};
}
#[pymethods]
impl PythonObserver {
#[staticmethod]
#[must_use]
pub fn new_map_i8(map_observer: Py<PythonMapObserverI8>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapI8(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_i16(map_observer: Py<PythonMapObserverI16>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapI16(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_i32(map_observer: Py<PythonMapObserverI32>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapI32(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_i64(map_observer: Py<PythonMapObserverI64>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapI64(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_u8(map_observer: Py<PythonMapObserverU8>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapU8(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_u16(map_observer: Py<PythonMapObserverU16>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapU16(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_u32(map_observer: Py<PythonMapObserverU32>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapU32(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_map_u64(map_observer: Py<PythonMapObserverU64>) -> Self {
Self {
wrapper: PythonObserverWrapper::MapU64(map_observer),
}
}
#[staticmethod]
#[must_use]
pub fn new_py(py_observer: PyObject) -> Self {
Self {
wrapper: PythonObserverWrapper::Python(PyObjectObserver::new(py_observer)),
}
}
pub fn unwrap_py(&self) -> Option<PyObject> {
match &self.wrapper {
PythonObserverWrapper::Python(pyo) => Some(pyo.inner.clone()),
_ => None,
}
}
}
impl Named for PythonObserver {
fn name(&self) -> &str {
let ptr = unwrap_me!(self.wrapper, o, { o.name() as *const str });
unsafe { ptr.as_ref().unwrap() }
}
}
impl Observer<PythonStdState> for PythonObserver {
fn flush(&mut self) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, { Observer::<PythonStdState>::flush(o) })
}
fn pre_exec(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, { o.pre_exec(state, input) })
}
fn post_exec(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, { o.post_exec(state, input, exit_kind) })
}
fn pre_exec_child(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, { o.pre_exec_child(state, input) })
}
fn post_exec_child(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
unwrap_me_mut!(self.wrapper, o, {
o.post_exec_child(state, input, exit_kind)
})
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[allow(clippy::unsafe_derive_deserialize)]
#[pyclass(unsendable, name = "ObserversTuple")]
pub struct PythonObserversTuple {
list: Vec<PythonObserver>,
}
#[pymethods]
impl PythonObserversTuple {
#[new]
fn new(list: Vec<PythonObserver>) -> Self {
Self { list }
}
fn len(&self) -> usize {
self.list.len()
}
fn __getitem__(&self, idx: usize) -> PythonObserver {
self.list[idx].clone()
}
#[pyo3(name = "match_name")]
fn pymatch_name(&self, name: &str) -> Option<PythonObserver> {
for ob in &self.list {
if *ob.name() == *name {
return Some(ob.clone());
}
}
None
}
}
impl ObserversTuple<PythonStdState> for PythonObserversTuple {
fn pre_exec_all(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
for ob in &mut self.list {
ob.pre_exec(state, input)?;
}
Ok(())
}
fn post_exec_all(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
for ob in &mut self.list {
ob.post_exec(state, input, exit_kind)?;
}
Ok(())
}
fn pre_exec_child_all(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
) -> Result<(), Error> {
for ob in &mut self.list {
ob.pre_exec_child(state, input)?;
}
Ok(())
}
fn post_exec_child_all(
&mut self,
state: &mut PythonStdState,
input: &BytesInput,
exit_kind: &ExitKind,
) -> Result<(), Error> {
for ob in &mut self.list {
ob.post_exec_child(state, input, exit_kind)?;
}
Ok(())
}
// TODO: expose stdout/stderr to python
#[inline]
fn observes_stdout(&mut self) -> bool {
false
}
#[inline]
fn observes_stderr(&mut self) -> bool {
false
}
#[inline]
fn observe_stderr(&mut self, _: &str) {}
#[inline]
fn observe_stdout(&mut self, _: &str) {}
}
impl MatchName for PythonObserversTuple {
fn match_name<T>(&self, name: &str) -> Option<&T> {
unsafe {
let mut r = None;
for ob in &self.list {
Python::with_gil(|py| -> PyResult<_> {
match &ob.wrapper {
PythonObserverWrapper::MapI8(py_wrapper) => {
if type_eq::<PythonMapObserverI8, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapI16(py_wrapper) => {
if type_eq::<PythonMapObserverI16, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapI32(py_wrapper) => {
if type_eq::<PythonMapObserverI32, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapI64(py_wrapper) => {
if type_eq::<PythonMapObserverI64, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapU8(py_wrapper) => {
if type_eq::<PythonMapObserverU8, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapU16(py_wrapper) => {
if type_eq::<PythonMapObserverU16, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapU32(py_wrapper) => {
if type_eq::<PythonMapObserverU32, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::MapU64(py_wrapper) => {
if type_eq::<PythonMapObserverU64, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow(py)) as *const T)
.as_ref();
}
}
PythonObserverWrapper::Python(py_wrapper) => {
if type_eq::<PyObjectObserver, T>() && py_wrapper.name() == name {
r = (py_wrapper as *const _ as *const T).as_ref();
}
}
}
Ok(())
})
.unwrap();
}
r
}
}
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
unsafe {
let mut r = None;
for ob in &mut self.list {
Python::with_gil(|py| -> PyResult<_> {
match &mut ob.wrapper {
PythonObserverWrapper::MapI8(py_wrapper) => {
if type_eq::<PythonMapObserverI8, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapI16(py_wrapper) => {
if type_eq::<PythonMapObserverI16, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapI32(py_wrapper) => {
if type_eq::<PythonMapObserverI32, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapI64(py_wrapper) => {
if type_eq::<PythonMapObserverI64, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapU8(py_wrapper) => {
if type_eq::<PythonMapObserverU8, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapU16(py_wrapper) => {
if type_eq::<PythonMapObserverU16, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapU32(py_wrapper) => {
if type_eq::<PythonMapObserverU32, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::MapU64(py_wrapper) => {
if type_eq::<PythonMapObserverU64, T>()
&& py_wrapper.borrow(py).name() == name
{
r = (std::ptr::addr_of!(*(*py_wrapper).borrow_mut(py))
as *mut T)
.as_mut();
}
}
PythonObserverWrapper::Python(py_wrapper) => {
if type_eq::<PyObjectObserver, T>() && py_wrapper.name() == name {
r = (py_wrapper as *mut _ as *mut T).as_mut();
}
}
}
Ok(())
})
.unwrap();
}
r
}
}
}
/// Register the classes to the python module
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PythonObserver>()?;
m.add_class::<PythonObserversTuple>()?;
Ok(())
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use crate::{
bolts::tuples::{tuple_list, tuple_list_type, Named},
observers::{StdMapObserver, TimeObserver},
};
static mut MAP: [u32; 4] = [0; 4];
#[test]
fn test_observer_serde() {
let obv = tuple_list!(
TimeObserver::new("time"),
StdMapObserver::new("map", unsafe { &mut MAP })
);
let vec = postcard::to_allocvec(&obv).unwrap();
println!("{vec:?}");
let obv2: tuple_list_type!(TimeObserver, StdMapObserver<u32>) =
postcard::from_bytes(&vec).unwrap();
assert_eq!(obv.0.name(), obv2.0.name());
}
}