Skip to content

Commit

Permalink
Simplify and document EvenAsOpaque with new usage
Browse files Browse the repository at this point in the history
  • Loading branch information
ryoqun committed Oct 4, 2023
1 parent cfaf370 commit 95b229f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 20 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frozen-abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ cc = { workspace = true, features = ["jobserver", "parallel"] }

[target.'cfg(not(target_os = "solana"))'.dev-dependencies]
solana-logger = { workspace = true }
bitflags = { workspace = true }

[build-dependencies]
rustc_version = { workspace = true }
45 changes: 37 additions & 8 deletions frozen-abi/src/abi_digester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct AbiDigester {
data_types: std::rc::Rc<std::cell::RefCell<Vec<String>>>,
depth: usize,
for_enum: bool,
opaque_scope: Option<String>,
opaque_type_matcher: Option<String>,
}

pub type DigestResult = Result<AbiDigester, DigestError>;
Expand Down Expand Up @@ -70,7 +70,7 @@ impl AbiDigester {
data_types: std::rc::Rc::new(std::cell::RefCell::new(vec![])),
for_enum: false,
depth: 0,
opaque_scope: None,
opaque_type_matcher: None,
}
}

Expand All @@ -81,7 +81,7 @@ impl AbiDigester {
data_types: self.data_types.clone(),
depth: self.depth,
for_enum: false,
opaque_scope: self.opaque_scope.clone(),
opaque_type_matcher: self.opaque_type_matcher.clone(),
}
}

Expand All @@ -90,7 +90,7 @@ impl AbiDigester {
data_types: self.data_types.clone(),
depth: self.depth,
for_enum: false,
opaque_scope: Some(scope.to_owned()),
opaque_type_matcher: Some(scope.to_owned()),
}
}

Expand All @@ -103,7 +103,7 @@ impl AbiDigester {
data_types: self.data_types.clone(),
depth,
for_enum: false,
opaque_scope: self.opaque_scope.clone(),
opaque_type_matcher: self.opaque_type_matcher.clone(),
})
}

Expand All @@ -116,15 +116,15 @@ impl AbiDigester {
data_types: self.data_types.clone(),
depth,
for_enum: true,
opaque_scope: self.opaque_scope.clone(),
opaque_type_matcher: self.opaque_type_matcher.clone(),
})
}

pub fn digest_data<T: ?Sized + Serialize>(&mut self, value: &T) -> DigestResult {
let type_name = normalize_type_name(type_name::<T>());
if type_name.ends_with("__SerializeWith")
|| (self.opaque_scope.is_some()
&& type_name.contains(self.opaque_scope.as_ref().unwrap()))
|| (self.opaque_type_matcher.is_some()
&& type_name.contains(self.opaque_type_matcher.as_ref().unwrap()))
{
// we can't use the AbiEnumVisitor trait for these cases.
value.serialize(self.create_new())
Expand Down Expand Up @@ -661,6 +661,35 @@ mod tests {
#[frozen_abi(digest = "9PMdHRb49BpkywrmPoJyZWMsEmf5E1xgmsFGkGmea5RW")]
type TestBitVec = bv::BitVec<u64>;

mod bitflags {
use crate::abi_example::{AbiExample, EvenAsOpaque, IgnoreAsHelper};

bitflags::bitflags! {
#[frozen_abi(digest = "HYjQ8oiYirTnQoLeRHbq66i8VYfcxPA4QTT3gdUQBgyV")]
#[derive(Serialize, Deserialize)]
struct TestFlags: u8 {
const TestBit = 0b0000_0001;
}
}

impl AbiExample for TestFlags {
fn example() -> Self {
Self::empty()
}
}

impl IgnoreAsHelper for TestFlags {}
// This (EvenAsOpaque) marker trait is needed for bitflags-generated types because we can't
// impl AbiExample for its
// private type:
// thread '...TestFlags_frozen_abi...' panicked at ...:
// derive or implement AbiExample/AbiEnumVisitor for
// solana_frozen_abi::abi_digester::tests::_::InternalBitFlags
impl EvenAsOpaque for TestFlags {
const TYPE_NAME_MATCHER: &'static str = "::_::InternalBitFlags";
}
}

mod skip_should_be_same {
#[frozen_abi(digest = "4LbuvQLX78XPbm4hqqZcHFHpseDJcw4qZL9EUZXSi2Ss")]
#[derive(Serialize, AbiExample)]
Expand Down
32 changes: 21 additions & 11 deletions frozen-abi/src/abi_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,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()
Expand Down Expand Up @@ -494,14 +501,21 @@ 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 {
const SCOPE: Option<&'static str> = None;
const TYPE_NAME_MATCHER: &'static str;
}

impl<T: Serialize + ?Sized> AbiEnumVisitor for T {
Expand Down Expand Up @@ -550,16 +564,12 @@ impl<T: Serialize + IgnoreAsHelper> AbiEnumVisitor for &T {
impl<T: Serialize + IgnoreAsHelper + EvenAsOpaque> AbiEnumVisitor for &T {
default fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult {
let type_name = type_name::<T>();
let scope = if let Some(scope) = T::SCOPE {
scope
} else {
type_name.split("::").next().unwrap()
};
let matcher = T::TYPE_NAME_MATCHER;
info!(
"AbiEnumVisitor for (EvenAsOpaque): {}: scope: {}",
type_name, scope
"AbiEnumVisitor for (EvenAsOpaque): {}: matcher: {}",
type_name, matcher
);
self.serialize(digester.create_new_opaque(scope))
self.serialize(digester.create_new_opaque(matcher))
.map_err(DigestError::wrap_by_type::<T>)
}
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl ::solana_frozen_abi::abi_example::IgnoreAsHelper for PacketFlags {}

#[cfg(RUSTC_WITH_SPECIALIZATION)]
impl ::solana_frozen_abi::abi_example::EvenAsOpaque for PacketFlags {
const SCOPE: Option<&'static str> = Some("InternalBitFlags");
const TYPE_NAME_MATCHER: &'static str = "::_::InternalBitFlags";
}

// serde_as is used as a work around because array isn't supported by serde
Expand Down

0 comments on commit 95b229f

Please sign in to comment.