-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Enable frozen_abi on banking trace file #33501
Changes from all commits
5e3a8a8
c875a1a
4a34a58
368d5a5
f5bdb30
56111b7
2230884
bd5e2cc
b564f05
fd6c28d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,24 @@ use { | |
std::any::type_name, | ||
}; | ||
|
||
// The most important trait for the abi digesting. This trait is used to create any complexities of | ||
// object graph to generate the abi digest. The frozen abi test harness calls T::example() to | ||
// instantiate the tested root type and traverses its fields recursively, abusing the | ||
// serde::serialize(). | ||
// | ||
// This trait applicability is similar to the Default trait. That means all referenced types must | ||
// implement this trait. AbiExample is implemented for almost all common types in this file. | ||
// | ||
// When implementing AbiExample manually, you need to return a _minimally-populated_ value | ||
// from it to actually generate a meaningful digest. This impl semantics is unlike Default, which | ||
// usually returns something empty. See actual impls for inspiration. | ||
// | ||
// The requirement of AbiExample impls even applies to those types of `#[serde(skip)]`-ed fields. | ||
// That's because the abi digesting needs a properly initialized object to enter into the | ||
// serde::serialize() to begin with, even knowning they aren't used for serialization and thus abi | ||
// digest. Luckily, `#[serde(skip)]`-ed fields' AbiExample impls can just delegate to T::default(), | ||
// exploiting the nature of this artificial impl requirement as an exception from the usual | ||
// AbiExample semantics. | ||
pub trait AbiExample: Sized { | ||
fn example() -> Self; | ||
} | ||
|
@@ -137,25 +155,12 @@ tuple_example_impls! { | |
} | ||
} | ||
|
||
// Source: https://github.com/rust-lang/rust/blob/ba18875557aabffe386a2534a1aa6118efb6ab88/src/libcore/array/mod.rs#L417 | ||
macro_rules! array_example_impls { | ||
{$n:expr, $t:ident $($ts:ident)*} => { | ||
impl<T> AbiExample for [T; $n] where T: AbiExample { | ||
fn example() -> Self { | ||
[$t::example(), $($ts::example()),*] | ||
} | ||
} | ||
array_example_impls!{($n - 1), $($ts)*} | ||
}; | ||
{$n:expr,} => { | ||
impl<T> AbiExample for [T; $n] { | ||
fn example() -> Self { [] } | ||
} | ||
}; | ||
impl<const N: usize, T: AbiExample> AbiExample for [T; N] { | ||
fn example() -> Self { | ||
std::array::from_fn(|_| T::example()) | ||
} | ||
} | ||
Comment on lines
+158
to
162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. elegance of const generics... off-topic: really wish Default is reimplementd like this as soon as possible... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please, I get so annoyed at the lack of Default whenever I use a fixed byte-buffer size |
||
|
||
array_example_impls! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T} | ||
|
||
// Source: https://github.com/rust-lang/rust/blob/ba18875557aabffe386a2534a1aa6118efb6ab88/src/libcore/default.rs#L137 | ||
macro_rules! example_impls { | ||
($t:ty, $v:expr) => { | ||
|
@@ -232,7 +237,14 @@ impl<T: BlockType> AbiExample for BitVec<T> { | |
} | ||
|
||
impl<T: BlockType> IgnoreAsHelper for BitVec<T> {} | ||
impl<T: BlockType> EvenAsOpaque for BitVec<T> {} | ||
// This (EvenAsOpaque) marker trait is needed for BitVec because we can't impl AbiExample for its | ||
// private type: | ||
// thread '...TestBitVec_frozen_abi...' panicked at ...: | ||
// derive or implement AbiExample/AbiEnumVisitor for | ||
// bv::bit_vec::inner::Inner<u64> | ||
impl<T: BlockType> EvenAsOpaque for BitVec<T> { | ||
const TYPE_NAME_MATCHER: &'static str = "bv::bit_vec::inner::"; | ||
} | ||
|
||
pub(crate) fn normalize_type_name(type_name: &str) -> String { | ||
type_name.chars().filter(|c| *c != '&').collect() | ||
|
@@ -329,13 +341,38 @@ impl<T: AbiExample> AbiExample for std::sync::Arc<T> { | |
} | ||
} | ||
|
||
// When T is weakly owned by the likes of `std::{sync, rc}::Weak`s, we need to uphold the ownership | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Appreciate the explanation here. However, I'm not super clear on why we need to have the implementations for #[serde(skip)]
recycler: Weak<RecyclerX<PinnedVec<T>>>, so shouldn't this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
another good question. yeah, some unspoken nuances are lying here... ;) i did my best to answer that in patch: fd6c28d
yep, i noticed this bug in as you correctly pointed out, |
||
// of T in some way at least during abi digesting... However, there's no easy way. Stashing them | ||
// into static is confronted with Send/Sync issue. Stashing them into thread_local is confronted | ||
// with not enough (T + 'static) lifetime bound.. So, just leak the examples. This should be | ||
// tolerated, considering ::example() should ever be called inside tests, not in production code... | ||
fn leak_and_inhibit_drop<'a, T>(t: T) -> &'a mut T { | ||
Box::leak(Box::new(t)) | ||
} | ||
|
||
impl<T: AbiExample> AbiExample for std::sync::Weak<T> { | ||
fn example() -> Self { | ||
info!("AbiExample for (Arc's Weak<T>): {}", type_name::<Self>()); | ||
// leaking is needed otherwise Arc::upgrade() will always return None... | ||
std::sync::Arc::downgrade(leak_and_inhibit_drop(std::sync::Arc::new(T::example()))) | ||
} | ||
} | ||
|
||
impl<T: AbiExample> AbiExample for std::rc::Rc<T> { | ||
fn example() -> Self { | ||
info!("AbiExample for (Rc<T>): {}", type_name::<Self>()); | ||
std::rc::Rc::new(T::example()) | ||
} | ||
} | ||
|
||
impl<T: AbiExample> AbiExample for std::rc::Weak<T> { | ||
fn example() -> Self { | ||
info!("AbiExample for (Rc's Weak<T>): {}", type_name::<Self>()); | ||
// leaking is needed otherwise Rc::upgrade() will always return None... | ||
std::rc::Rc::downgrade(leak_and_inhibit_drop(std::rc::Rc::new(T::example()))) | ||
} | ||
} | ||
|
||
impl<T: AbiExample> AbiExample for std::sync::Mutex<T> { | ||
fn example() -> Self { | ||
info!("AbiExample for (Mutex<T>): {}", type_name::<Self>()); | ||
|
@@ -457,6 +494,13 @@ impl AbiExample for std::path::PathBuf { | |
} | ||
} | ||
|
||
#[cfg(not(target_os = "solana"))] | ||
impl AbiExample for std::time::SystemTime { | ||
fn example() -> Self { | ||
std::time::SystemTime::UNIX_EPOCH | ||
} | ||
} | ||
|
||
use std::net::{IpAddr, Ipv4Addr, SocketAddr}; | ||
impl AbiExample for SocketAddr { | ||
fn example() -> Self { | ||
|
@@ -470,13 +514,22 @@ impl AbiExample for IpAddr { | |
} | ||
} | ||
|
||
// This is a control flow indirection needed for digesting all variants of an enum | ||
// This is a control flow indirection needed for digesting all variants of an enum. | ||
// | ||
// All of types (including non-enums) will be processed by this trait, albeit the | ||
// name of this trait. | ||
// User-defined enums usually just need to impl this with namesake derive macro (AbiEnumVisitor). | ||
// | ||
// Note that sometimes this indirection doesn't work for various reasons. For that end, there are | ||
// hacks with marker traits (IgnoreAsHelper/EvenAsOpaque). | ||
pub trait AbiEnumVisitor: Serialize { | ||
fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult; | ||
} | ||
|
||
pub trait IgnoreAsHelper {} | ||
pub trait EvenAsOpaque {} | ||
pub trait EvenAsOpaque { | ||
const TYPE_NAME_MATCHER: &'static str; | ||
} | ||
|
||
impl<T: Serialize + ?Sized> AbiEnumVisitor for T { | ||
default fn visit_for_abi(&self, _digester: &mut AbiDigester) -> DigestResult { | ||
|
@@ -489,7 +542,9 @@ impl<T: Serialize + ?Sized> AbiEnumVisitor for T { | |
|
||
impl<T: Serialize + ?Sized + AbiExample> AbiEnumVisitor for T { | ||
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult { | ||
info!("AbiEnumVisitor for (default): {}", type_name::<T>()); | ||
info!("AbiEnumVisitor for T: {}", type_name::<T>()); | ||
// not calling self.serialize(...) is intentional here as the most generic impl | ||
// consider IgnoreAsHelper and EvenAsOpaque if you're stuck on this.... | ||
T::example() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instantiating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, i was wrong... 35dd937 |
||
.serialize(digester.create_new()) | ||
.map_err(DigestError::wrap_by_type::<T>) | ||
|
@@ -501,7 +556,7 @@ impl<T: Serialize + ?Sized + AbiExample> AbiEnumVisitor for T { | |
// relevant test: TestVecEnum | ||
impl<T: Serialize + ?Sized + AbiEnumVisitor> AbiEnumVisitor for &T { | ||
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult { | ||
info!("AbiEnumVisitor for (&default): {}", type_name::<T>()); | ||
info!("AbiEnumVisitor for &T: {}", type_name::<T>()); | ||
// Don't call self.visit_for_abi(...) to avoid the infinite recursion! | ||
T::visit_for_abi(self, digester) | ||
} | ||
|
@@ -521,9 +576,13 @@ impl<T: Serialize + IgnoreAsHelper> AbiEnumVisitor for &T { | |
// inability of implementing AbiExample for private structs from other crates | ||
impl<T: Serialize + IgnoreAsHelper + EvenAsOpaque> AbiEnumVisitor for &T { | ||
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult { | ||
info!("AbiEnumVisitor for (IgnoreAsOpaque): {}", type_name::<T>()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this |
||
let top_scope = type_name::<T>().split("::").next().unwrap(); | ||
self.serialize(digester.create_new_opaque(top_scope)) | ||
let type_name = type_name::<T>(); | ||
let matcher = T::TYPE_NAME_MATCHER; | ||
info!( | ||
"AbiEnumVisitor for (EvenAsOpaque): {}: matcher: {}", | ||
type_name, matcher | ||
); | ||
self.serialize(digester.create_new_opaque(matcher)) | ||
.map_err(DigestError::wrap_by_type::<T>) | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as you can see, enabling frozen_abi for banking trace files wasn't so joyful... xD