diff --git a/external-crates/move/crates/move-core-types/src/annotated_extractor.rs b/external-crates/move/crates/move-core-types/src/annotated_extractor.rs new file mode 100644 index 0000000000000..72c9a1bc7717d --- /dev/null +++ b/external-crates/move/crates/move-core-types/src/annotated_extractor.rs @@ -0,0 +1,334 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + account_address::AccountAddress, annotated_value as A, annotated_visitor as AV, + language_storage::TypeTag, +}; + +/// Elements are components of paths that select values from the sub-structure of other values. +/// They are split into two categories: +/// +/// - Selectors, which recurse into the sub-structure. +/// - Filters, which check properties of the value at that position in the sub-structure. +#[derive(Debug, Clone)] +pub enum Element<'e> { + // Selectors + /// Select a named field, assuming the value in question is a struct or an enum variant. + Field(&'e str), + + /// Select a positional element. This can be the element of a vector, or it can be a positional + /// field in an enum or a struct. + Index(u64), + + // Filters + /// Confirm that the current value has a certain type. + Type(&'e TypeTag), + + /// Confirm that the current value is an enum and its variant has this name. Note that to + /// filter on both the enum type and the variant name, the path must contain the Type first, + /// and then the Variant. Otherwise the type filter will be assumed + Variant(&'e str), +} + +/// An Extractor is an [`AV::Visitor`] that deserializes a sub-structure of the value. The +/// sub-structure is found at the end of a path of [`Element`]s which select fields from structs, +/// indices from vectors, and variants from enums. Deserialization is delegated to another visitor, +/// of type `V`, with the Extractor returning `Option`: +/// +/// - `Some(v)` if the given path exists in the value, or +/// - `None` if the path did not exist, +/// - Or an error if the underlying visitor failed for some reason. +/// +/// At every stage, the path can optionally start with an [`Element::Type`], which restricts the +/// type of the top-level value being deserialized. From there, the elements expected are driven by +/// the layout being deserialized: +/// +/// - When deserializing a vector, the next element must be an [`Element::Index`] which selects the +/// offset into the vector that the extractor recurses into. +/// - When deserializing a struct, the next element may be an [`Element::Field`] which selects the +/// field of the struct that the extractor recurses into by name, or an [`Element::Index`] which +/// selects the field by its offset. +/// - When deserializing a variant, the next elements may optionally be an [`Element::Variant`] +/// which expects a particular variant of the enum, followed by either an [`Element::Field`] or +/// an [`Element::Index`], similar to a struct. +pub struct Extractor<'p, 'v, V> { + inner: &'v mut V, + path: &'p [Element<'p>], +} + +impl<'p, 'v, 'b, 'l, V: AV::Visitor<'b, 'l>> Extractor<'p, 'v, V> +where + V::Error: std::error::Error + Send + Sync + 'static, +{ + pub fn new(inner: &'v mut V, path: &'p [Element<'p>]) -> Self { + Self { inner, path } + } + + pub fn deserialize_value( + bytes: &'b [u8], + layout: &'l A::MoveTypeLayout, + inner: &'v mut V, + path: Vec>, + ) -> anyhow::Result> { + let mut extractor = Extractor::new(inner, &path); + A::MoveValue::visit_deserialize(bytes, layout, &mut extractor) + } + + pub fn deserialize_struct( + bytes: &'b [u8], + layout: &'l A::MoveStructLayout, + inner: &'v mut V, + path: Vec>, + ) -> anyhow::Result> { + let mut extractor = Extractor::new(inner, &path); + A::MoveStruct::visit_deserialize(bytes, layout, &mut extractor) + } +} + +impl<'p, 'v, 'b, 'l, V: AV::Visitor<'b, 'l>> AV::Visitor<'b, 'l> for Extractor<'p, 'v, V> { + type Value = Option; + type Error = V::Error; + + fn visit_u8( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: u8, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U8)] => Some(self.inner.visit_u8(driver, value)?), + _ => None, + }) + } + + fn visit_u16( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: u16, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U16)] => Some(self.inner.visit_u16(driver, value)?), + _ => None, + }) + } + + fn visit_u32( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: u32, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U32)] => Some(self.inner.visit_u32(driver, value)?), + _ => None, + }) + } + + fn visit_u64( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: u64, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U64)] => Some(self.inner.visit_u64(driver, value)?), + _ => None, + }) + } + + fn visit_u128( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: u128, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U128)] => Some(self.inner.visit_u128(driver, value)?), + _ => None, + }) + } + + fn visit_u256( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: crate::u256::U256, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::U256)] => Some(self.inner.visit_u256(driver, value)?), + _ => None, + }) + } + + fn visit_bool( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: bool, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::Bool)] => Some(self.inner.visit_bool(driver, value)?), + _ => None, + }) + } + + fn visit_address( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::Address)] => { + Some(self.inner.visit_address(driver, value)?) + } + _ => None, + }) + } + + fn visit_signer( + &mut self, + driver: &AV::ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + Ok(match self.path { + [] | [Element::Type(&TypeTag::Signer)] => Some(self.inner.visit_signer(driver, value)?), + _ => None, + }) + } + + fn visit_vector( + &mut self, + driver: &mut AV::VecDriver<'_, 'b, 'l>, + ) -> Result { + use Element as E; + use TypeTag as T; + + // If there is a type element, check that it is a vector type with the correct element + // type, and remove it from the path. + let path = if let [E::Type(t), path @ ..] = self.path { + if !matches!(t, T::Vector(t) if driver.element_layout().is_type(t)) { + return Ok(None); + } + path + } else { + self.path + }; + + // If there are no further path elements, we can delegate to the inner visitor. + let [index, path @ ..] = path else { + return Ok(Some(self.inner.visit_vector(driver)?)); + }; + + // Visiting a vector, the next part of the path must be an index -- anything else is + // guaranteed to fail. + let E::Index(i) = index else { + return Ok(None); + }; + + // Skip all the elements before the index, and then recurse. + while driver.off() < *i && driver.skip_element()? {} + Ok(driver + .next_element(&mut Extractor { + inner: self.inner, + path, + })? + .flatten()) + } + + fn visit_struct( + &mut self, + driver: &mut AV::StructDriver<'_, 'b, 'l>, + ) -> Result { + use Element as E; + use TypeTag as T; + + // If there is a type element, check that it is a struct type with the correct struct tag, + // and remove it from the path. + let path = if let [E::Type(t), path @ ..] = self.path { + if !matches!(t, T::Struct(t) if driver.struct_layout().is_type(t)) { + return Ok(None); + } + path + } else { + self.path + }; + + // If there are no further path elements, we can delegate to the inner visitor. + let [field, path @ ..] = path else { + return Ok(Some(self.inner.visit_struct(driver)?)); + }; + + match field { + // Skip over mismatched fields by name. + E::Field(f) => { + while matches!(driver.peek_field(), Some(l) if l.name.as_str() != *f) { + driver.skip_field()?; + } + } + + // Skip over fields by offset. + E::Index(i) => while driver.off() < *i && driver.skip_field()?.is_some() {}, + + // Any other element is invalid in this position. + _ => return Ok(None), + } + + Ok(driver + .next_field(&mut Extractor { + inner: self.inner, + path, + })? + .and_then(|(_, v)| v)) + } + + fn visit_variant( + &mut self, + driver: &mut AV::VariantDriver<'_, 'b, 'l>, + ) -> Result { + use Element as E; + use TypeTag as T; + + // If there is a type element, check that it is a struct type with the correct struct tag, + // and remove it from the path. + let path = if let [E::Type(t), path @ ..] = self.path { + if !matches!(t, T::Struct(t) if driver.enum_layout().is_type(t)) { + return Ok(None); + } + path + } else { + self.path + }; + + // If there is a variant element, check that it matches and remove it from the path. + let path = if let [E::Variant(v), path @ ..] = path { + if driver.variant_name().as_str() != *v { + return Ok(None); + } + path + } else { + path + }; + + // If there are no further path elements, we can delegate to the inner visitor. + let [field, path @ ..] = path else { + return Ok(Some(self.inner.visit_variant(driver)?)); + }; + + match field { + // Skip over mismatched fields by name. + E::Field(f) => { + while matches!(driver.peek_field(), Some(l) if l.name.as_str() != *f) { + driver.skip_field()?; + } + } + + // Skip over fields by offset. + E::Index(i) => while driver.off() < *i && driver.skip_field()?.is_some() {}, + + // Any other element is invalid in this position. + _ => return Ok(None), + } + + Ok(driver + .next_field(&mut Extractor { + inner: self.inner, + path, + })? + .and_then(|(_, v)| v)) + } +} diff --git a/external-crates/move/crates/move-core-types/src/annotated_value.rs b/external-crates/move/crates/move-core-types/src/annotated_value.rs index f5a31cc571941..22718e716f9b1 100644 --- a/external-crates/move/crates/move-core-types/src/annotated_value.rs +++ b/external-crates/move/crates/move-core-types/src/annotated_value.rs @@ -142,6 +142,43 @@ pub enum MoveTypeLayout { Enum(Box), } +impl MoveStructLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &StructTag) -> bool { + self.type_ == *type_ + } +} + +impl MoveEnumLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &StructTag) -> bool { + self.type_ == *type_ + } +} + +impl MoveTypeLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &TypeTag) -> bool { + use MoveTypeLayout as L; + use TypeTag as T; + + match self { + L::Bool => matches!(type_, T::Bool), + L::U8 => matches!(type_, T::U8), + L::U16 => matches!(type_, T::U16), + L::U32 => matches!(type_, T::U32), + L::U64 => matches!(type_, T::U64), + L::U128 => matches!(type_, T::U128), + L::U256 => matches!(type_, T::U256), + L::Address => matches!(type_, T::Address), + L::Signer => matches!(type_, T::Signer), + L::Vector(l) => matches!(type_, T::Vector(t) if l.is_type(t)), + L::Struct(l) => matches!(type_, T::Struct(t) if l.is_type(t)), + L::Enum(l) => matches!(type_, T::Struct(t) if l.is_type(t)), + } + } +} + impl MoveValue { /// TODO (annotated-visitor): Port legacy uses of this method to `BoundedVisitor`. pub fn simple_deserialize(blob: &[u8], ty: &MoveTypeLayout) -> AResult { diff --git a/external-crates/move/crates/move-core-types/src/annotated_visitor.rs b/external-crates/move/crates/move-core-types/src/annotated_visitor.rs index 9160d4565dc27..6e87e8405e61d 100644 --- a/external-crates/move/crates/move-core-types/src/annotated_visitor.rs +++ b/external-crates/move/crates/move-core-types/src/annotated_visitor.rs @@ -333,7 +333,7 @@ pub struct VecDriver<'c, 'b, 'l> { pub struct StructDriver<'c, 'b, 'l> { inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveStructLayout, - off: usize, + off: u64, } /// Exposes information about a variant being visited (its layout, details about the next field to @@ -345,7 +345,7 @@ pub struct VariantDriver<'c, 'b, 'l> { tag: u16, variant_name: &'l IdentStr, variant_layout: &'l [MoveFieldLayout], - off: usize, + off: u64, } #[derive(thiserror::Error, Debug)] @@ -461,7 +461,12 @@ impl<'c, 'b, 'l> VecDriver<'c, 'b, 'l> { self.layout } - /// The number of elements in this vector + /// The number of elements in this vector that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + + /// The number of elements in this vector. pub fn len(&self) -> u64 { self.len } @@ -532,9 +537,14 @@ impl<'c, 'b, 'l> StructDriver<'c, 'b, 'l> { self.layout } + /// The number of fields in this struct that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + /// The layout of the next field to be visited (if there is one), or `None` otherwise. pub fn peek_field(&self) -> Option<&'l MoveFieldLayout> { - self.layout.fields.get(self.off) + self.layout.fields.get(self.off as usize) } /// Visit the next field in the struct. The driver accepts a visitor to use for this field, @@ -624,9 +634,14 @@ impl<'c, 'b, 'l> VariantDriver<'c, 'b, 'l> { self.variant_name } + /// The number of elements in this vector that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + /// The layout of the next field to be visited (if there is one), or `None` otherwise. pub fn peek_field(&self) -> Option<&'l MoveFieldLayout> { - self.variant_layout.get(self.off) + self.variant_layout.get(self.off as usize) } /// Visit the next field in the variant. The driver accepts a visitor to use for this field, diff --git a/external-crates/move/crates/move-core-types/src/lib.rs b/external-crates/move/crates/move-core-types/src/lib.rs index 8c803b4752091..fa471e38808e4 100644 --- a/external-crates/move/crates/move-core-types/src/lib.rs +++ b/external-crates/move/crates/move-core-types/src/lib.rs @@ -8,6 +8,7 @@ use std::fmt; pub mod abi; pub mod account_address; +pub mod annotated_extractor; pub mod annotated_value; pub mod annotated_visitor; pub mod effects; diff --git a/external-crates/move/crates/move-core-types/src/unit_tests/extractor_test.rs b/external-crates/move/crates/move-core-types/src/unit_tests/extractor_test.rs new file mode 100644 index 0000000000000..9d93abf734207 --- /dev/null +++ b/external-crates/move/crates/move-core-types/src/unit_tests/extractor_test.rs @@ -0,0 +1,852 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use crate::{ + account_address::AccountAddress, + annotated_extractor::{Element as E, Extractor}, + annotated_value::{MoveTypeLayout, MoveValue}, + language_storage::TypeTag, + unit_tests::visitor_test::{ + enum_layout_, serialize, struct_layout_, struct_value_, variant_value_, PrintVisitor, + }, +}; + +#[test] +fn struct_() { + let expect = r#" +[0] struct 0x0::foo::Bar { + a: u8, + b: u16, + c: u32, + d: u64, + e: u128, + f: u256, + g: bool, + h: address, + i: signer, + j: vector, + k: struct 0x0::foo::Baz { + l: u8, + }, + m: enum 0x0::foo::Qux { + n { + o: u8, + }, + }, + p: vector, +} +[1] 1: u8 +[1] 2: u16 +[1] 3: u32 +[1] 4: u64 +[1] 5: u128 +[1] 6: u256 +[1] true: bool +[1] 0000000000000000000000000000000000000000000000000000000000000000: address +[1] 0000000000000000000000000000000000000000000000000000000000000000: signer +[1] vector +[2] 7: u8 +[2] 8: u8 +[2] 9: u8 +[1] struct 0x0::foo::Baz { + l: u8, +} +[2] 10: u8 +[1] enum 0x0::foo::Qux { + n { + o: u8, + }, +} +[2] 11: u8 +[1] vector +[2] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[3] 12: u8 +[3] true: bool +[2] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[3] 13: u8 +[3] false: bool + "#; + + for path in enumerate_paths(vec![C::Opt(E::Type(&type_("0x0::foo::Bar")))]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_a() { + let expect = r#" +[0] 1: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("a"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_b() { + let expect = r#" +[0] 2: u16 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("b"), E::Index(1)]), + C::Opt(E::Type(&type_("u16"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_c() { + let expect = r#" +[0] 3: u32 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("c"), E::Index(2)]), + C::Opt(E::Type(&type_("u32"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_d() { + let expect = r#" +[0] 4: u64 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("d"), E::Index(3)]), + C::Opt(E::Type(&type_("u64"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_e() { + let expect = r#" +[0] 5: u128 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("e"), E::Index(4)]), + C::Opt(E::Type(&type_("u128"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_f() { + let expect = r#" +[0] 6: u256 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("f"), E::Index(5)]), + C::Opt(E::Type(&type_("u256"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_g() { + let expect = r#" +[0] true: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("g"), E::Index(6)]), + C::Opt(E::Type(&type_("bool"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_h() { + let expect = r#" +[0] 0000000000000000000000000000000000000000000000000000000000000000: address + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("h"), E::Index(7)]), + C::Opt(E::Type(&type_("address"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_i() { + let expect = r#" +[0] 0000000000000000000000000000000000000000000000000000000000000000: signer + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("i"), E::Index(8)]), + C::Opt(E::Type(&type_("signer"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_j() { + let expect = r#" +[0] vector +[1] 7: u8 +[1] 8: u8 +[1] 9: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("j"), E::Index(9)]), + C::Opt(E::Type(&type_("vector"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_j_0() { + let expect = r#" +[0] 7: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("j"), E::Index(9)]), + C::Opt(E::Type(&type_("vector"))), + C::Req(vec![E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_j_1() { + let expect = r#" +[0] 8: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("j"), E::Index(9)]), + C::Opt(E::Type(&type_("vector"))), + C::Req(vec![E::Index(1)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_j_2() { + let expect = r#" +[0] 9: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("j"), E::Index(9)]), + C::Opt(E::Type(&type_("vector"))), + C::Req(vec![E::Index(2)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_k() { + let expect = r#" +[0] struct 0x0::foo::Baz { + l: u8, +} +[1] 10: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("k"), E::Index(10)]), + C::Opt(E::Type(&type_("0x0::foo::Baz"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_k_l() { + let expect = r#" +[0] 10: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("k"), E::Index(10)]), + C::Opt(E::Type(&type_("0x0::foo::Baz"))), + C::Req(vec![E::Field("l"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_m() { + let expect = r#" +[0] enum 0x0::foo::Qux { + n { + o: u8, + }, +} +[1] 11: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("m"), E::Index(11)]), + C::Opt(E::Type(&type_("0x0::foo::Qux"))), + C::Opt(E::Variant("n")), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_m_o() { + let expect = r#" +[0] 11: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("m"), E::Index(11)]), + C::Opt(E::Type(&type_("0x0::foo::Qux"))), + C::Opt(E::Variant("n")), + C::Req(vec![E::Field("o"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p() { + let expect = r#" +[0] vector +[1] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[2] 12: u8 +[2] true: bool +[1] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[2] 13: u8 +[2] false: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_0() { + let expect = r#" +[0] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[1] 12: u8 +[1] true: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(0)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_0_q() { + let expect = r#" +[0] 12: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(0)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + C::Req(vec![E::Field("q"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_0_r() { + let expect = r#" +[0] true: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(0)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + C::Req(vec![E::Field("r"), E::Index(1)]), + C::Opt(E::Type(&type_("bool"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_1() { + let expect = r#" +[0] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[1] 13: u8 +[1] false: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(1)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_1_q() { + let expect = r#" +[0] 13: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(1)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + C::Req(vec![E::Field("q"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn struct_p_1_r() { + let expect = r#" +[0] false: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Bar"))), + C::Req(vec![E::Field("p"), E::Index(12)]), + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(1)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + C::Req(vec![E::Field("r"), E::Index(1)]), + C::Opt(E::Type(&type_("bool"))), + ]) { + assert_path(test_struct(), path, expect); + } +} + +#[test] +fn vector_() { + let expect = r#" +[0] vector +[1] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[2] 12: u8 +[2] true: bool +[1] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[2] 13: u8 +[2] false: bool + "#; + + for path in enumerate_paths(vec![C::Opt(E::Type(&type_("vector<0x0::foo::Quy>")))]) { + assert_path(test_vector(), path, expect); + } +} + +#[test] +fn vector_0() { + let expect = r#" +[0] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[1] 12: u8 +[1] true: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(0)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + ]) { + assert_path(test_vector(), path, expect); + } +} + +#[test] +fn vector_1() { + let expect = r#" +[0] struct 0x0::foo::Quy { + q: u8, + r: bool, +} +[1] 13: u8 +[1] false: bool + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("vector<0x0::foo::Quy>"))), + C::Req(vec![E::Index(1)]), + C::Opt(E::Type(&type_("0x0::foo::Quy"))), + ]) { + assert_path(test_vector(), path, expect); + } +} + +#[test] +fn enum_() { + let expect = r#" +[0] enum 0x0::foo::Qux { + n { + o: u8, + }, +} +[1] 11: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Qux"))), + C::Opt(E::Variant("n")), + ]) { + assert_path(test_enum(), path, expect); + } +} + +#[test] +fn enum_o() { + let expect = r#" +[0] 11: u8 + "#; + + for path in enumerate_paths(vec![ + C::Opt(E::Type(&type_("0x0::foo::Qux"))), + C::Opt(E::Variant("n")), + C::Req(vec![E::Field("o"), E::Index(0)]), + C::Opt(E::Type(&type_("u8"))), + ]) { + assert_path(test_enum(), path, expect); + } +} + +#[test] +fn field_not_found() { + for path in [ + vec![E::Field("z")], + // Trying to access a field on a primitive + vec![E::Field("a"), E::Field("z")], + // Nested field doesn't exist + vec![E::Field("k"), E::Field("z")], + // Nested field on an enum (that doesn't exist) + vec![E::Field("m"), E::Field("z")], + // Trying to access a field on a vector + vec![E::Field("p"), E::Field("z")], + // Nested field on a struct in a vector + vec![E::Field("p"), E::Index(0), E::Field("z")], + ] { + assert_no_path(test_struct(), path); + } +} + +#[test] +fn index_out_of_bounds() { + for path in [ + // Positional access of field, out of bounds + vec![E::Index(1000)], + // Trying to access index on a primitive + vec![E::Field("a"), E::Index(1000)], + // Out of bounds on primitive vector + vec![E::Field("j"), E::Index(1000)], + // Out of bounds field on nested struct + vec![E::Field("k"), E::Index(1000)], + // Out of bounds field on nested enum + vec![E::Field("m"), E::Index(1000)], + // Out of bounds field on struct vector + vec![E::Field("p"), E::Index(1000)], + // Out of bounds field on struct in vector + vec![E::Field("p"), E::Index(0), E::Index(1000)], + ] { + assert_no_path(test_struct(), path); + } +} + +#[test] +fn type_mismatch() { + for path in [ + // Wrong root type + vec![E::Type(&type_("0x0::foo::Baz"))], + // Wrong primitive type + vec![E::Field("a"), E::Type(&type_("u16"))], + // Wrong nested struct + vec![E::Field("k"), E::Type(&type_("0x0::foo::Bar"))], + // Wrong type with further nesting + vec![ + E::Field("k"), + E::Type(&type_("0x0::foo::Bar")), + E::Field("l"), + ], + // Wrong primitive vector + vec![E::Field("j"), E::Type(&type_("vector"))], + vec![E::Field("j"), E::Type(&type_("u8"))], + // Wrong enum type + vec![E::Field("m"), E::Type(&type_("0x0::foo::Bar"))], + // Wrong type nested inside enum + vec![E::Field("m"), E::Field("o"), E::Type(&type_("u16"))], + ] { + assert_no_path(test_struct(), path); + } +} + +#[test] +fn variant_not_found() { + assert_no_path(test_enum(), vec![E::Variant("z")]); + assert_no_path(test_struct(), vec![E::Field("m"), E::Variant("z")]); +} + +/// Components are used to generate paths. Each component offers a number of options for the +/// element that goes in the same position in the generated path. +enum C<'p> { + /// This element is optional -- paths are geneated with and without this element at the + /// component's position. + Opt(E<'p>), + + /// This element is required, and is picked from the provided list. + Req(Vec>), +} + +/// Generate a list of paths as a cartesian product of the provided components. +fn enumerate_paths(components: Vec>) -> Vec>> { + let mut paths = vec![vec![]]; + + for component in components { + let mut new_paths = vec![]; + + for path in paths { + match &component { + C::Opt(element) => { + new_paths.push(path.clone()); + let mut path = path.clone(); + path.push(element.clone()); + new_paths.push(path); + } + C::Req(elements) => { + new_paths.extend(elements.iter().map(|e| { + let mut path = path.clone(); + path.push(e.clone()); + path + })); + } + } + } + + paths = new_paths; + } + + paths +} + +fn assert_path((value, layout): (MoveValue, MoveTypeLayout), path: Vec>, expect: &str) { + let bytes = serialize(value); + let mut printer = PrintVisitor::default(); + + assert!( + Extractor::deserialize_value(&bytes, &layout, &mut printer, path.clone()) + .unwrap() + .is_some(), + "Failed to extract value {path:?}", + ); + + assert_eq!( + printer.output.trim(), + expect.trim(), + "Failed to match value at {path:?}" + ); +} + +fn assert_no_path((value, layout): (MoveValue, MoveTypeLayout), path: Vec>) { + let bytes = serialize(value); + let mut printer = PrintVisitor::default(); + + assert!( + Extractor::deserialize_value(&bytes, &layout, &mut printer, path.clone()) + .unwrap() + .is_none(), + "Expected not to find something at {path:?}", + ); + + assert!( + printer.output.is_empty(), + "Expected not to delegate to the inner visitor for {path:?}" + ); +} + +fn type_(t: &str) -> TypeTag { + TypeTag::from_str(t).unwrap() +} + +fn test_struct() -> (MoveValue, MoveTypeLayout) { + use MoveTypeLayout as T; + use MoveValue as V; + + let (vector, vector_layout) = test_vector(); + let (variant, enum_layout) = test_enum(); + + let value = struct_value_( + "0x0::foo::Bar", + vec![ + ("a", V::U8(1)), + ("b", V::U16(2)), + ("c", V::U32(3)), + ("d", V::U64(4)), + ("e", V::U128(5)), + ("f", V::U256(6u32.into())), + ("g", V::Bool(true)), + ("h", V::Address(AccountAddress::ZERO)), + ("i", V::Signer(AccountAddress::ZERO)), + ("j", V::Vector(vec![V::U8(7), V::U8(8), V::U8(9)])), + ("k", struct_value_("0x0::foo::Baz", vec![("l", V::U8(10))])), + ("m", variant), + ("p", vector), + ], + ); + + let layout = struct_layout_( + "0x0::foo::Bar", + vec![ + ("a", T::U8), + ("b", T::U16), + ("c", T::U32), + ("d", T::U64), + ("e", T::U128), + ("f", T::U256), + ("g", T::Bool), + ("h", T::Address), + ("i", T::Signer), + ("j", T::Vector(Box::new(T::U8))), + ("k", struct_layout_("0x0::foo::Baz", vec![("l", T::U8)])), + ("m", enum_layout), + ("p", vector_layout), + ], + ); + + (value, layout) +} + +fn test_enum() -> (MoveValue, MoveTypeLayout) { + use MoveTypeLayout as T; + use MoveValue as V; + + let value = variant_value_("0x0::foo::Qux", "n", 0, vec![("o", V::U8(11))]); + let layout = enum_layout_("0x0::foo::Qux", vec![("n", vec![("o", T::U8)])]); + + (value, layout) +} + +fn test_vector() -> (MoveValue, MoveTypeLayout) { + use MoveTypeLayout as T; + use MoveValue as V; + + let value = V::Vector(vec![ + struct_value_( + "0x0::foo::Quy", + vec![("q", V::U8(12)), ("r", V::Bool(true))], + ), + struct_value_( + "0x0::foo::Quy", + vec![("q", V::U8(13)), ("r", V::Bool(false))], + ), + ]); + + let layout = T::Vector(Box::new(struct_layout_( + "0x0::foo::Quy", + vec![("q", T::U8), ("r", T::Bool)], + ))); + + (value, layout) +} diff --git a/external-crates/move/crates/move-core-types/src/unit_tests/mod.rs b/external-crates/move/crates/move-core-types/src/unit_tests/mod.rs index 6788ebac27bde..237660ea9a80a 100644 --- a/external-crates/move/crates/move-core-types/src/unit_tests/mod.rs +++ b/external-crates/move/crates/move-core-types/src/unit_tests/mod.rs @@ -2,6 +2,7 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 +mod extractor_test; mod identifier_test; mod language_storage_test; mod value_test; diff --git a/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs b/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs index 7d0c06b8696af..3d64ecb1d598e 100644 --- a/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs +++ b/external-crates/move/crates/move-core-types/src/unit_tests/visitor_test.rs @@ -19,125 +19,284 @@ use crate::{ VARIANT_COUNT_MAX, }; -#[test] -fn traversal() { - use MoveTypeLayout as T; - use MoveValue as V; +#[derive(Default)] +pub(crate) struct CountingTraversal(usize); - #[derive(Default)] - struct CountingTraversal(usize); +#[derive(Default)] +pub(crate) struct PrintVisitor { + depth: usize, + pub output: String, +} - impl<'b, 'l> Traversal<'b, 'l> for CountingTraversal { - type Error = annotated_visitor::Error; +impl<'b, 'l> Traversal<'b, 'l> for CountingTraversal { + type Error = annotated_visitor::Error; - fn traverse_u8( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u8, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u8( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u8, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_u16( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u16, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u16( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u16, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_u32( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u32, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u32( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u32, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_u64( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u64, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u64( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u64, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_u128( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u128, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u128( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u128, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_u256( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: U256, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_u256( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: U256, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_bool( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: bool, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_bool( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: bool, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_address( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: AccountAddress, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_address( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: AccountAddress, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_signer( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: AccountAddress, - ) -> Result<(), Self::Error> { - self.0 += 1; - Ok(()) - } + fn traverse_signer( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: AccountAddress, + ) -> Result<(), Self::Error> { + self.0 += 1; + Ok(()) + } - fn traverse_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - self.0 += 1; - while driver.next_element(self)?.is_some() {} - Ok(()) + fn traverse_vector(&mut self, driver: &mut VecDriver<'_, 'b, 'l>) -> Result<(), Self::Error> { + self.0 += 1; + while driver.next_element(self)?.is_some() {} + Ok(()) + } + + fn traverse_struct( + &mut self, + driver: &mut StructDriver<'_, 'b, 'l>, + ) -> Result<(), Self::Error> { + self.0 += 1; + while driver.next_field(self)?.is_some() {} + Ok(()) + } + + fn traverse_variant( + &mut self, + driver: &mut VariantDriver<'_, 'b, 'l>, + ) -> Result<(), Self::Error> { + self.0 += 1; + while driver.next_field(self)?.is_some() {} + Ok(()) + } +} + +impl<'b, 'l> Visitor<'b, 'l> for PrintVisitor { + type Value = MoveValue; + type Error = annotated_visitor::Error; + + fn visit_u8( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: u8, + ) -> Result { + write!(self.output, "\n[{}] {value}: u8", self.depth).unwrap(); + Ok(MoveValue::U8(value)) + } + + fn visit_u16( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: u16, + ) -> Result { + write!(self.output, "\n[{}] {value}: u16", self.depth).unwrap(); + Ok(MoveValue::U16(value)) + } + + fn visit_u32( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: u32, + ) -> Result { + write!(self.output, "\n[{}] {value}: u32", self.depth).unwrap(); + Ok(MoveValue::U32(value)) + } + + fn visit_u64( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: u64, + ) -> Result { + write!(self.output, "\n[{}] {value}: u64", self.depth).unwrap(); + Ok(MoveValue::U64(value)) + } + + fn visit_u128( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: u128, + ) -> Result { + write!(self.output, "\n[{}] {value}: u128", self.depth).unwrap(); + Ok(MoveValue::U128(value)) + } + + fn visit_u256( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: U256, + ) -> Result { + write!(self.output, "\n[{}] {value}: u256", self.depth).unwrap(); + Ok(MoveValue::U256(value)) + } + + fn visit_bool( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: bool, + ) -> Result { + write!(self.output, "\n[{}] {value}: bool", self.depth).unwrap(); + Ok(MoveValue::Bool(value)) + } + + fn visit_address( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + write!(self.output, "\n[{}] {value}: address", self.depth).unwrap(); + Ok(MoveValue::Address(value)) + } + + fn visit_signer( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + write!(self.output, "\n[{}] {value}: signer", self.depth).unwrap(); + Ok(MoveValue::Signer(value)) + } + + fn visit_vector( + &mut self, + driver: &mut VecDriver<'_, 'b, 'l>, + ) -> Result { + let layout = driver.element_layout(); + write!(self.output, "\n[{}] vector<{layout:#}>", self.depth).unwrap(); + + let mut elems = vec![]; + let mut elem_visitor = Self { + depth: self.depth + 1, + output: std::mem::take(&mut self.output), + }; + + while let Some(elem) = driver.next_element(&mut elem_visitor)? { + elems.push(elem) } - fn traverse_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - self.0 += 1; - while driver.next_field(self)?.is_some() {} - Ok(()) + self.output = elem_visitor.output; + Ok(MoveValue::Vector(elems)) + } + + fn visit_struct( + &mut self, + driver: &mut StructDriver<'_, 'b, 'l>, + ) -> Result { + let layout = driver.struct_layout(); + write!(self.output, "\n[{}] {layout:#}", self.depth).unwrap(); + + let mut fields = vec![]; + let mut field_visitor = Self { + depth: self.depth + 1, + output: std::mem::take(&mut self.output), + }; + + while let Some((field, value)) = driver.next_field(&mut field_visitor)? { + fields.push((field.name.clone(), value)); } - fn traverse_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - self.0 += 1; - while driver.next_field(self)?.is_some() {} - Ok(()) + self.output = field_visitor.output; + let type_ = driver.struct_layout().type_.clone(); + Ok(MoveValue::Struct(MoveStruct { type_, fields })) + } + + fn visit_variant( + &mut self, + driver: &mut VariantDriver<'_, 'b, 'l>, + ) -> Result { + let layout = driver.enum_layout(); + write!(self.output, "\n[{}] {layout:#}", self.depth).unwrap(); + + let mut fields = vec![]; + let mut field_visitor = Self { + depth: self.depth + 1, + output: std::mem::take(&mut self.output), + }; + + while let Some((field, value)) = driver.next_field(&mut field_visitor)? { + fields.push((field.name.clone(), value)); } + + self.output = field_visitor.output; + let type_ = driver.enum_layout().type_.clone(); + Ok(MoveValue::Variant(MoveVariant { + type_, + variant_name: driver.variant_name().to_owned(), + tag: driver.tag(), + fields, + })) } +} + +#[test] +fn traversal() { + use MoveTypeLayout as T; + use MoveValue as V; let type_layout = struct_layout_( "0x0::foo::Bar", @@ -334,168 +493,6 @@ fn nested_datatype_visit() { use MoveTypeLayout as T; use MoveValue as V; - #[derive(Default)] - struct PrintVisitor { - depth: usize, - output: String, - } - - impl<'b, 'l> Visitor<'b, 'l> for PrintVisitor { - type Value = MoveValue; - type Error = annotated_visitor::Error; - - fn visit_u8( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: u8, - ) -> Result { - write!(self.output, "\n[{}] {value}: u8", self.depth).unwrap(); - Ok(V::U8(value)) - } - - fn visit_u16( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: u16, - ) -> Result { - write!(self.output, "\n[{}] {value}: u16", self.depth).unwrap(); - Ok(V::U16(value)) - } - - fn visit_u32( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: u32, - ) -> Result { - write!(self.output, "\n[{}] {value}: u32", self.depth).unwrap(); - Ok(V::U32(value)) - } - - fn visit_u64( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: u64, - ) -> Result { - write!(self.output, "\n[{}] {value}: u64", self.depth).unwrap(); - Ok(V::U64(value)) - } - - fn visit_u128( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: u128, - ) -> Result { - write!(self.output, "\n[{}] {value}: u128", self.depth).unwrap(); - Ok(V::U128(value)) - } - - fn visit_u256( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: U256, - ) -> Result { - write!(self.output, "\n[{}] {value}: u256", self.depth).unwrap(); - Ok(V::U256(value)) - } - - fn visit_bool( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: bool, - ) -> Result { - write!(self.output, "\n[{}] {value}: bool", self.depth).unwrap(); - Ok(V::Bool(value)) - } - - fn visit_address( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - write!(self.output, "\n[{}] {value}: address", self.depth).unwrap(); - Ok(V::Address(value)) - } - - fn visit_signer( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - write!(self.output, "\n[{}] {value}: signer", self.depth).unwrap(); - Ok(V::Signer(value)) - } - - fn visit_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result { - let layout = driver.element_layout(); - write!(self.output, "\n[{}] vector<{layout:#}>", self.depth).unwrap(); - - let mut elems = vec![]; - let mut elem_visitor = Self { - depth: self.depth + 1, - output: std::mem::take(&mut self.output), - }; - - while let Some(elem) = driver.next_element(&mut elem_visitor)? { - elems.push(elem) - } - - self.output = elem_visitor.output; - Ok(V::Vector(elems)) - } - - fn visit_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result { - let layout = driver.struct_layout(); - write!(self.output, "\n[{}] {layout:#}", self.depth).unwrap(); - - let mut fields = vec![]; - let mut field_visitor = Self { - depth: self.depth + 1, - output: std::mem::take(&mut self.output), - }; - - while let Some((field, value)) = driver.next_field(&mut field_visitor)? { - fields.push((field.name.clone(), value)); - } - - self.output = field_visitor.output; - let type_ = driver.struct_layout().type_.clone(); - Ok(V::Struct(MoveStruct { type_, fields })) - } - - fn visit_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result { - let layout = driver.enum_layout(); - write!(self.output, "\n[{}] {layout:#}", self.depth).unwrap(); - - let mut fields = vec![]; - let mut field_visitor = Self { - depth: self.depth + 1, - output: std::mem::take(&mut self.output), - }; - - while let Some((field, value)) = driver.next_field(&mut field_visitor)? { - fields.push((field.name.clone(), value)); - } - - self.output = field_visitor.output; - let type_ = driver.enum_layout().type_.clone(); - Ok(V::Variant(MoveVariant { - type_, - variant_name: driver.variant_name().to_owned(), - tag: driver.tag(), - fields, - })) - } - } - let type_layout = struct_layout_( "0x0::foo::Bar", vec![ @@ -1058,7 +1055,7 @@ fn byte_offset_test() { } /// Create a struct value for test purposes. -fn struct_value_(rep: &str, fields: Vec<(&str, MoveValue)>) -> MoveValue { +pub(crate) fn struct_value_(rep: &str, fields: Vec<(&str, MoveValue)>) -> MoveValue { let type_ = StructTag::from_str(rep).unwrap(); let fields = fields .into_iter() @@ -1069,7 +1066,7 @@ fn struct_value_(rep: &str, fields: Vec<(&str, MoveValue)>) -> MoveValue { } /// Create a struct layout for test purposes. -fn struct_layout_(rep: &str, fields: Vec<(&str, MoveTypeLayout)>) -> MoveTypeLayout { +pub(crate) fn struct_layout_(rep: &str, fields: Vec<(&str, MoveTypeLayout)>) -> MoveTypeLayout { let type_ = StructTag::from_str(rep).unwrap(); let fields = fields .into_iter() @@ -1083,7 +1080,12 @@ fn struct_layout_(rep: &str, fields: Vec<(&str, MoveTypeLayout)>) -> MoveTypeLay } /// Create a variant value for test purposes. -fn variant_value_(rep: &str, name: &str, tag: u16, fields: Vec<(&str, MoveValue)>) -> MoveValue { +pub(crate) fn variant_value_( + rep: &str, + name: &str, + tag: u16, + fields: Vec<(&str, MoveValue)>, +) -> MoveValue { let type_ = StructTag::from_str(rep).unwrap(); let fields = fields .into_iter() @@ -1099,7 +1101,10 @@ fn variant_value_(rep: &str, name: &str, tag: u16, fields: Vec<(&str, MoveValue) } /// Create an enum layout for test purposes. -fn enum_layout_(rep: &str, variants: Vec<(&str, Vec<(&str, MoveTypeLayout)>)>) -> MoveTypeLayout { +pub(crate) fn enum_layout_( + rep: &str, + variants: Vec<(&str, Vec<(&str, MoveTypeLayout)>)>, +) -> MoveTypeLayout { let type_ = StructTag::from_str(rep).unwrap(); let variants = variants .into_iter() @@ -1117,6 +1122,6 @@ fn enum_layout_(rep: &str, variants: Vec<(&str, Vec<(&str, MoveTypeLayout)>)>) - } /// BCS encode Move value. -fn serialize(value: MoveValue) -> Vec { +pub(crate) fn serialize(value: MoveValue) -> Vec { value.clone().undecorate().simple_serialize().unwrap() }