This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 71855425b feat(rust): support limit max dyn depth (#2730)
71855425b is described below

commit 71855425bc6056767654b71f3f8d16f8b70c9e53
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Oct 9 16:52:53 2025 +0800

    feat(rust): support limit max dyn depth (#2730)
    
    ## Why?
    
    <!-- Describe the purpose of this PR. -->
    
    ## What does this PR do?
    
    1. support limit max dyn depth for better security
    2. add Fory api doc
    
    ## Related issues
    
    Closes #2729
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fory/issues/new/choose) describing the
    need to do so and update the document if necessary.
    
    Delete section if not applicable.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
    
    <!--
    When the PR has an impact on performance (if you don't know whether the
    PR will have an impact on performance, you can submit the PR first, and
    if it will have impact on performance, the code reviewer will explain
    it), be sure to attach a benchmark data here.
    
    Delete section if not applicable.
    -->
---
 rust/README.md                                  |  33 ++
 rust/fory-core/src/fory.rs                      | 396 +++++++++++++++++++++++-
 rust/fory-core/src/lib.rs                       |   1 +
 rust/fory-core/src/resolver/context.rs          |  25 +-
 rust/fory-core/src/serializer/any.rs            |  13 +-
 rust/fory-core/src/serializer/skip.rs           |  14 +
 rust/fory-core/src/serializer/trait_object.rs   |  17 +-
 rust/fory-core/src/serializer/weak.rs           |   5 +-
 rust/fory-derive/src/object/read.rs             |  12 +-
 rust/tests/tests/compatible/test_basic_type.rs  |   8 +-
 rust/tests/tests/compatible/test_container.rs   |  18 +-
 rust/tests/tests/compatible/test_struct_enum.rs |   2 +-
 rust/tests/tests/mod.rs                         |   1 +
 rust/tests/tests/test_cross_language.rs         |  14 +-
 rust/tests/tests/test_max_dyn_depth.rs          | 135 ++++++++
 15 files changed, 662 insertions(+), 32 deletions(-)

diff --git a/rust/README.md b/rust/README.md
index 8b2dbdc84..4bc402dff 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -889,6 +889,39 @@ use fory::Mode;
 let fory = Fory::default().mode(Mode::Compatible);
 ```
 
+## ⚙️ Configuration
+
+### Maximum Dynamic Object Nesting Depth
+
+Apache Fory™ provides protection against stack overflow from deeply nested 
dynamic objects during deserialization. By default, the maximum nesting depth 
is set to 5 levels for trait objects and containers.
+
+**Default configuration:**
+
+```rust
+let fory = Fory::default(); // max_dyn_depth = 5
+```
+
+**Custom depth limit:**
+
+```rust
+let fory = Fory::default().max_dyn_depth(10); // Allow up to 10 levels
+```
+
+**When to adjust:**
+
+- **Increase**: For legitimate deeply nested data structures
+- **Decrease**: For stricter security requirements or shallow data structures
+
+**Protected types:**
+
+- `Box<dyn Any>`, `Rc<dyn Any>`, `Arc<dyn Any>`
+- `Box<dyn Trait>`, `Rc<dyn Trait>`, `Arc<dyn Trait>` (trait objects)
+- `RcWeak<T>`, `ArcWeak<T>`
+- Collection types (Vec, HashMap, HashSet)
+- Nested struct types in Compatible mode
+
+Note: Static data types (non-dynamic types) are secure by nature and not 
subject to depth limits, as their structure is known at compile time.
+
 ## 🛠️ Development
 
 ### Building
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index c7bf9dca8..d4d2aa891 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -36,6 +36,49 @@ use std::rc::Rc;
 
 static EMPTY_STRING: String = String::new();
 
+/// The main Fory serialization framework instance.
+///
+/// `Fory` provides high-performance cross-language serialization and 
deserialization
+/// capabilities with support for multiple modes, reference tracking, and 
trait object serialization.
+///
+/// # Features
+///
+/// - **Cross-language serialization**: Serialize data in Rust and deserialize 
in other languages
+/// - **Multiple modes**: Schema-consistent and compatible serialization modes
+/// - **Reference tracking**: Handles shared and circular references
+/// - **Trait object serialization**: Supports serializing polymorphic trait 
objects
+/// - **Dynamic depth limiting**: Configurable limit for nested dynamic object 
serialization
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```rust, ignore
+/// use fory::Fory;
+/// use fory::ForyObject;
+///
+/// #[derive(ForyObject)]
+/// struct User {
+///     name: String,
+///     age: u32,
+/// }
+///
+/// let fory = Fory::default();
+/// let user = User { name: "Alice".to_string(), age: 30 };
+/// let bytes = fory.serialize(&user);
+/// let deserialized: User = fory.deserialize(&bytes).unwrap();
+/// ```
+///
+/// Custom configuration:
+///
+/// ```rust
+/// use fory_core::{Fory, Mode};
+///
+/// let fory = Fory::default()
+///     .mode(Mode::Compatible)
+///     .compress_string(true)
+///     .max_dyn_depth(10);
+/// ```
 pub struct Fory {
     mode: Mode,
     xlang: bool,
@@ -43,6 +86,7 @@ pub struct Fory {
     type_resolver: TypeResolver,
     metastring_resolver: Rc<RefCell<MetaStringResolver>>,
     compress_string: bool,
+    max_dyn_depth: u32,
 }
 
 impl Default for Fory {
@@ -54,11 +98,39 @@ impl Default for Fory {
             type_resolver: TypeResolver::default(),
             metastring_resolver: 
Rc::from(RefCell::from(MetaStringResolver::default())),
             compress_string: false,
+            max_dyn_depth: 5,
         }
     }
 }
 
 impl Fory {
+    /// Sets the serialization mode for this Fory instance.
+    ///
+    /// # Arguments
+    ///
+    /// * `mode` - The serialization mode to use. Options are:
+    ///   - `Mode::SchemaConsistent`: Schema must be consistent between 
serialization and deserialization.
+    ///     No metadata is shared. This is the fastest mode.
+    ///   - `Mode::Compatible`: Supports schema evolution and type metadata 
sharing for better
+    ///     cross-version compatibility.
+    ///
+    /// # Returns
+    ///
+    /// Returns `self` for method chaining.
+    ///
+    /// # Note
+    ///
+    /// Setting the mode also automatically configures the `share_meta` flag:
+    /// - `Mode::SchemaConsistent` → `share_meta = false`
+    /// - `Mode::Compatible` → `share_meta = true`
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use fory_core::{Fory, Mode};
+    ///
+    /// let fory = Fory::default().mode(Mode::Compatible);
+    /// ```
     pub fn mode(mut self, mode: Mode) -> Self {
         // Setting share_meta individually is not supported currently
         self.share_meta = mode != Mode::SchemaConsistent;
@@ -66,32 +138,150 @@ impl Fory {
         self
     }
 
+    /// Enables or disables cross-language serialization protocol.
+    ///
+    /// # Arguments
+    ///
+    /// * `xlang` - If `true`, uses the cross-language serialization format 
that includes
+    ///   language metadata and magic numbers for compatibility with Fory 
implementations
+    ///   in other languages (Java, Python, C++, etc.). If `false`, uses a 
Rust-only
+    ///   optimized format.
+    ///
+    /// # Returns
+    ///
+    /// Returns `self` for method chaining.
+    ///
+    /// # Default
+    ///
+    /// The default value is `true`.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use fory_core::Fory;
+    ///
+    /// // For cross-language use (default)
+    /// let fory = Fory::default().xlang(true);
+    ///
+    /// // For Rust-only optimization
+    /// let fory = Fory::default().xlang(false);
+    /// ```
     pub fn xlang(mut self, xlang: bool) -> Self {
         self.xlang = xlang;
         self
     }
 
+    /// Enables or disables meta string compression.
+    ///
+    /// # Arguments
+    ///
+    /// * `compress_string` - If `true`, enables meta string compression to 
reduce serialized
+    ///   payload size by deduplicating and encoding frequently used strings 
(such as type names
+    ///   and field names). If `false`, strings are serialized without 
compression.
+    ///
+    /// # Returns
+    ///
+    /// Returns `self` for method chaining.
+    ///
+    /// # Default
+    ///
+    /// The default value is `false`.
+    ///
+    /// # Trade-offs
+    ///
+    /// - **Enabled**: Smaller payload size, slightly higher CPU overhead
+    /// - **Disabled**: Larger payload size, faster 
serialization/deserialization
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use fory_core::Fory;
+    ///
+    /// let fory = Fory::default().compress_string(true);
+    /// ```
     pub fn compress_string(mut self, compress_string: bool) -> Self {
         self.compress_string = compress_string;
         self
     }
 
+    /// Sets the maximum depth for nested dynamic object serialization.
+    ///
+    /// # Arguments
+    ///
+    /// * `max_dyn_depth` - The maximum nesting depth allowed for 
dynamically-typed objects
+    ///   (e.g., trait objects, boxed types). This prevents stack overflow 
from deeply nested
+    ///   structures in dynamic serialization scenarios.
+    ///
+    /// # Returns
+    ///
+    /// Returns `self` for method chaining.
+    ///
+    /// # Default
+    ///
+    /// The default value is `5`.
+    ///
+    /// # Behavior
+    ///
+    /// When the depth limit is exceeded during deserialization, an error is 
returned to prevent
+    /// potential stack overflow or infinite recursion.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use fory_core::Fory;
+    ///
+    /// // Allow deeper nesting for complex object graphs
+    /// let fory = Fory::default().max_dyn_depth(10);
+    ///
+    /// // Restrict nesting for safer deserialization
+    /// let fory = Fory::default().max_dyn_depth(3);
+    /// ```
+    pub fn max_dyn_depth(mut self, max_dyn_depth: u32) -> Self {
+        self.max_dyn_depth = max_dyn_depth;
+        self
+    }
+
+    /// Returns the current serialization mode.
+    ///
+    /// # Returns
+    ///
+    /// A reference to the current `Mode` (either `SchemaConsistent` or 
`Compatible`).
     pub fn get_mode(&self) -> &Mode {
         &self.mode
     }
 
+    /// Returns whether string compression is enabled.
+    ///
+    /// # Returns
+    ///
+    /// `true` if meta string compression is enabled, `false` otherwise.
     pub fn is_compress_string(&self) -> bool {
         self.compress_string
     }
 
+    /// Returns whether metadata sharing is enabled.
+    ///
+    /// # Returns
+    ///
+    /// `true` if metadata sharing is enabled (automatically set based on 
mode), `false` otherwise.
     pub fn is_share_meta(&self) -> bool {
         self.share_meta
     }
 
+    /// Returns a reference to the type resolver.
+    ///
+    /// # Returns
+    ///
+    /// A reference to the internal `TypeResolver` used for type registration 
and lookup.
     pub fn get_type_resolver(&self) -> &TypeResolver {
         &self.type_resolver
     }
 
+    /// Returns a cloned reference to the meta string resolver.
+    ///
+    /// # Returns
+    ///
+    /// An `Rc<RefCell<MetaStringResolver>>` for meta string compression and 
decompression.
     pub fn get_metastring_resolver(&self) -> Rc<RefCell<MetaStringResolver>> {
         Rc::clone(&self.metastring_resolver)
     }
@@ -157,11 +347,47 @@ impl Fory {
         Ok(false)
     }
 
+    /// Deserializes data from a byte slice into a value of type `T`.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The target type to deserialize into. Must implement 
`Serializer` and `ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `bf` - The byte slice containing the serialized data.
+    ///
+    /// # Returns
+    ///
+    /// * `Ok(T)` - The deserialized value on success.
+    /// * `Err(Error)` - An error if deserialization fails (e.g., invalid 
format, type mismatch).
+    ///
+    /// # Panics
+    ///
+    /// Panics in debug mode if there are unread bytes remaining after 
successful deserialization,
+    /// indicating a potential protocol violation.
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory::Fory;
+    /// use fory::ForyObject;
+    ///
+    /// #[derive(ForyObject)]
+    /// struct Point { x: i32, y: i32 }
+    ///
+    /// let fory = Fory::default();
+    /// let point = Point { x: 10, y: 20 };
+    /// let bytes = fory.serialize(&point);
+    /// let deserialized: Point = fory.deserialize(&bytes).unwrap();
+    /// ```
     pub fn deserialize<T: Serializer + ForyDefault>(&self, bf: &[u8]) -> 
Result<T, Error> {
         let reader = Reader::new(bf);
-        let mut context = ReadContext::new(self, reader);
+        let mut context = ReadContext::new(self, reader, self.max_dyn_depth);
         let result = self.deserialize_with_context(&mut context);
-        assert_eq!(context.reader.slice_after_cursor().len(), 0);
+        if result.is_ok() {
+            assert_eq!(context.reader.slice_after_cursor().len(), 0);
+        }
         result
     }
 
@@ -188,6 +414,33 @@ impl Fory {
         result
     }
 
+    /// Serializes a value of type `T` into a byte vector.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The type of the value to serialize. Must implement 
`Serializer`.
+    ///
+    /// # Arguments
+    ///
+    /// * `record` - A reference to the value to serialize.
+    ///
+    /// # Returns
+    ///
+    /// A `Vec<u8>` containing the serialized data.
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory::Fory;
+    /// use fory::ForyObject;
+    ///
+    /// #[derive(ForyObject)]
+    /// struct Point { x: i32, y: i32 }
+    ///
+    /// let fory = Fory::default();
+    /// let point = Point { x: 10, y: 20 };
+    /// let bytes = fory.serialize(&point);
+    /// ```
     pub fn serialize<T: Serializer>(&self, record: &T) -> Vec<u8> {
         let mut writer = Writer::default();
         let mut context: WriteContext<'_> = WriteContext::new(self, &mut 
writer);
@@ -214,6 +467,33 @@ impl Fory {
         context.writer.dump()
     }
 
+    /// Registers a struct type with a numeric type ID for serialization.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The struct type to register. Must implement 
`StructSerializer`, `Serializer`, and `ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `id` - A unique numeric identifier for the type. This ID is used in 
the serialized format
+    ///   to identify the type during deserialization.
+    ///
+    /// # Panics
+    ///
+    /// May panic if the type ID conflicts with an already registered type.
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory::Fory;
+    /// use fory::ForyObject;
+    ///
+    /// #[derive(ForyObject)]
+    /// struct User { name: String, age: u32 }
+    ///
+    /// let mut fory = Fory::default();
+    /// fory.register::<User>(100);
+    /// ```
     pub fn register<T: 'static + StructSerializer + Serializer + 
ForyDefault>(&mut self, id: u32) {
         let actual_type_id = T::fory_actual_type_id(id, false, &self.mode);
         let type_info =
@@ -221,6 +501,36 @@ impl Fory {
         self.type_resolver.register::<T>(&type_info);
     }
 
+    /// Registers a struct type with a namespace and type name for 
cross-language serialization.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The struct type to register. Must implement 
`StructSerializer`, `Serializer`, and `ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `namespace` - The namespace or package name for the type (e.g., 
"com.example.types").
+    ///   Use an empty string for the default namespace.
+    /// * `type_name` - The name of the type (e.g., "User").
+    ///
+    /// # Notes
+    ///
+    /// This registration method is preferred for cross-language serialization 
as it uses
+    /// human-readable type identifiers instead of numeric IDs, which improves 
compatibility
+    /// across different language implementations.
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory::Fory;
+    /// use fory::ForyObject;
+    ///
+    /// #[derive(ForyObject)]
+    /// struct User { name: String, age: u32 }
+    ///
+    /// let mut fory = Fory::default();
+    /// fory.register_by_namespace::<User>("com.example", "User");
+    /// ```
     pub fn register_by_namespace<T: 'static + StructSerializer + Serializer + 
ForyDefault>(
         &mut self,
         namespace: &str,
@@ -231,6 +541,32 @@ impl Fory {
         self.type_resolver.register::<T>(&type_info);
     }
 
+    /// Registers a struct type with a type name (using the default namespace).
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The struct type to register. Must implement 
`StructSerializer`, `Serializer`, and `ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `type_name` - The name of the type (e.g., "User").
+    ///
+    /// # Notes
+    ///
+    /// This is a convenience method that calls `register_by_namespace` with 
an empty namespace string.
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory::Fory;
+    /// use fory::ForyObject;
+    ///
+    /// #[derive(ForyObject)]
+    /// struct User { name: String, age: u32 }
+    ///
+    /// let mut fory = Fory::default();
+    /// fory.register_by_name::<User>("User");
+    /// ```
     pub fn register_by_name<T: 'static + StructSerializer + Serializer + 
ForyDefault>(
         &mut self,
         type_name: &str,
@@ -238,6 +574,33 @@ impl Fory {
         self.register_by_namespace::<T>("", type_name);
     }
 
+    /// Registers a custom serializer type with a numeric type ID.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The type to register. Must implement `Serializer` and 
`ForyDefault`.
+    ///   Unlike `register()`, this does not require `StructSerializer`, 
making it suitable
+    ///   for non-struct types or types with custom serialization logic.
+    ///
+    /// # Arguments
+    ///
+    /// * `id` - A unique numeric identifier for the type.
+    ///
+    /// # Use Cases
+    ///
+    /// Use this method to register:
+    /// - Enum types with custom serialization
+    /// - Wrapper types
+    /// - Types with hand-written `Serializer` implementations
+    ///
+    /// # Examples
+    ///
+    /// ```rust, ignore
+    /// use fory_core::Fory;
+    ///
+    /// let mut fory = Fory::default();
+    /// fory.register_serializer::<MyCustomType>(200);
+    /// ```
     pub fn register_serializer<T: Serializer + ForyDefault>(&mut self, id: 
u32) {
         let actual_type_id = get_ext_actual_type_id(id, false);
         let type_info = TypeInfo::new_with_empty_fields::<T>(
@@ -250,6 +613,22 @@ impl Fory {
         self.type_resolver.register_serializer::<T>(&type_info);
     }
 
+    /// Registers a custom serializer type with a namespace and type name.
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The type to register. Must implement `Serializer` and 
`ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `namespace` - The namespace or package name for the type.
+    /// * `type_name` - The name of the type.
+    ///
+    /// # Notes
+    ///
+    /// This is the namespace-based equivalent of `register_serializer()`, 
preferred for
+    /// cross-language serialization scenarios.
+    ///
     pub fn register_serializer_by_namespace<T: Serializer + ForyDefault>(
         &mut self,
         namespace: &str,
@@ -261,6 +640,19 @@ impl Fory {
         self.type_resolver.register_serializer::<T>(&type_info);
     }
 
+    /// Registers a custom serializer type with a type name (using the default 
namespace).
+    ///
+    /// # Type Parameters
+    ///
+    /// * `T` - The type to register. Must implement `Serializer` and 
`ForyDefault`.
+    ///
+    /// # Arguments
+    ///
+    /// * `type_name` - The name of the type.
+    ///
+    /// # Notes
+    ///
+    /// This is a convenience method that calls 
`register_serializer_by_namespace` with an empty namespace.
     pub fn register_serializer_by_name<T: Serializer + ForyDefault>(&mut self, 
type_name: &str) {
         self.register_serializer_by_namespace::<T>("", type_name);
     }
diff --git a/rust/fory-core/src/lib.rs b/rust/fory-core/src/lib.rs
index e25ee4ba6..957bf6dce 100644
--- a/rust/fory-core/src/lib.rs
+++ b/rust/fory-core/src/lib.rs
@@ -176,3 +176,4 @@ pub use crate::resolver::context::{ReadContext, 
WriteContext};
 pub use crate::resolver::type_resolver::TypeResolver;
 pub use crate::serializer::weak::{ArcWeak, RcWeak};
 pub use crate::serializer::{ForyDefault, Serializer};
+pub use crate::types::{Mode, RefFlag, TypeId};
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index c6704689d..b8415d2da 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -111,15 +111,19 @@ pub struct ReadContext<'de, 'bf: 'de> {
     fory: &'de Fory,
     pub meta_resolver: MetaReaderResolver,
     pub ref_reader: RefReader,
+    max_dyn_depth: u32,
+    current_depth: u32,
 }
 
 impl<'de, 'bf: 'de> ReadContext<'de, 'bf> {
-    pub fn new(fory: &'de Fory, reader: Reader<'bf>) -> ReadContext<'de, 'bf> {
+    pub fn new(fory: &'de Fory, reader: Reader<'bf>, max_dyn_depth: u32) -> 
ReadContext<'de, 'bf> {
         ReadContext {
             reader,
             fory,
             meta_resolver: MetaReaderResolver::default(),
             ref_reader: RefReader::new(),
+            max_dyn_depth,
+            current_depth: 0,
         }
     }
 
@@ -175,4 +179,23 @@ impl<'de, 'bf: 'de> ReadContext<'de, 'bf> {
                 .expect("ID harness not found")
         }
     }
+
+    pub fn inc_depth(&mut self) -> Result<(), crate::error::Error> {
+        self.current_depth += 1;
+        if self.current_depth > self.max_dyn_depth {
+            return 
Err(crate::error::Error::Other(crate::error::AnyhowError::msg(
+                format!(
+                    "Maximum dynamic object nesting depth ({}) exceeded. 
Current depth: {}. \
+                    This may indicate a circular reference or overly deep 
object graph. \
+                    Consider increasing max_dyn_depth if this is expected.",
+                    self.max_dyn_depth, self.current_depth
+                ),
+            )));
+        }
+        Ok(())
+    }
+
+    pub fn dec_depth(&mut self) {
+        self.current_depth = self.current_depth.saturating_sub(1);
+    }
 }
diff --git a/rust/fory-core/src/serializer/any.rs 
b/rust/fory-core/src/serializer/any.rs
index f72a0a086..d82b162a7 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -36,6 +36,7 @@ pub fn serialize_any_box(any_box: &Box<dyn Any>, context: 
&mut WriteContext, is_
 
 /// Helper function to deserialize to `Box<dyn Any>`
 pub fn deserialize_any_box(context: &mut ReadContext) -> Result<Box<dyn Any>, 
Error> {
+    context.inc_depth()?;
     let ref_flag = context.reader.read_i8();
     if ref_flag != RefFlag::NotNullValue as i8 {
         return Err(Error::Other(anyhow::anyhow!(
@@ -44,7 +45,9 @@ pub fn deserialize_any_box(context: &mut ReadContext) -> 
Result<Box<dyn Any>, Er
     }
     let harness = context.read_any_typeinfo();
     let deserializer_fn = harness.get_read_data_fn();
-    deserializer_fn(context, true)
+    let result = deserializer_fn(context, true);
+    context.dec_depth();
+    result
 }
 
 impl ForyDefault for Box<dyn Any> {
@@ -133,15 +136,19 @@ impl Serializer for Rc<dyn Any> {
                     })
             }
             RefFlag::NotNullValue => {
+                context.inc_depth()?;
                 let harness = context.read_any_typeinfo();
                 let deserializer_fn = harness.get_read_data_fn();
                 let boxed = deserializer_fn(context, true)?;
+                context.dec_depth();
                 Ok(Rc::<dyn Any>::from(boxed))
             }
             RefFlag::RefValue => {
+                context.inc_depth()?;
                 let harness = context.read_any_typeinfo();
                 let deserializer_fn = harness.get_read_data_fn();
                 let boxed = deserializer_fn(context, true)?;
+                context.dec_depth();
                 let rc: Rc<dyn Any> = Rc::from(boxed);
                 context.ref_reader.store_rc_ref(rc.clone());
                 Ok(rc)
@@ -216,15 +223,19 @@ impl Serializer for Arc<dyn Any> {
                     })
             }
             RefFlag::NotNullValue => {
+                context.inc_depth()?;
                 let harness = context.read_any_typeinfo();
                 let deserializer_fn = harness.get_read_data_fn();
                 let boxed = deserializer_fn(context, true)?;
+                context.dec_depth();
                 Ok(Arc::<dyn Any>::from(boxed))
             }
             RefFlag::RefValue => {
+                context.inc_depth()?;
                 let harness = context.read_any_typeinfo();
                 let deserializer_fn = harness.get_read_data_fn();
                 let boxed = deserializer_fn(context, true)?;
+                context.dec_depth();
                 let arc: Arc<dyn Any> = Arc::from(boxed);
                 context.ref_reader.store_arc_ref(arc.clone());
                 Ok(arc)
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
index b43f7c82e..c1538dbfe 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -89,9 +89,11 @@ pub fn skip_field_value(
                     let is_same_type = (header & IS_SAME_TYPE) != 0;
                     let skip_ref_flag = is_same_type && !has_null;
                     let elem_type = field_type.generics.first().unwrap();
+                    context.inc_depth()?;
                     for _ in 0..length {
                         skip_field_value(context, elem_type, !skip_ref_flag)?;
                     }
+                    context.dec_depth();
                 } else if type_id == TypeId::MAP {
                     let length = context.reader.read_varuint32();
                     if length == 0 {
@@ -113,23 +115,29 @@ pub fn skip_field_value(
                         }
                         if header & crate::serializer::map::KEY_NULL != 0 {
                             // let read_ref_flag = 
get_read_ref_flag(value_type);
+                            context.inc_depth()?;
                             skip_field_value(context, value_type, false)?;
+                            context.dec_depth();
                             len_counter += 1;
                             continue;
                         }
                         if header & crate::serializer::map::VALUE_NULL != 0 {
                             // let read_ref_flag = get_read_ref_flag(key_type);
+                            context.inc_depth()?;
                             skip_field_value(context, key_type, false)?;
+                            context.dec_depth();
                             len_counter += 1;
                             continue;
                         }
                         let chunk_size = context.reader.read_u8();
+                        context.inc_depth()?;
                         for _ in (0..chunk_size).enumerate() {
                             // let read_ref_flag = get_read_ref_flag(key_type);
                             skip_field_value(context, key_type, false)?;
                             // let read_ref_flag = 
get_read_ref_flag(value_type);
                             skip_field_value(context, value_type, false)?;
                         }
+                        context.dec_depth();
                         len_counter += chunk_size as u32;
                     }
                 }
@@ -143,12 +151,14 @@ pub fn skip_field_value(
                 let meta_index = context.reader.read_varuint32();
                 let type_meta = context.get_meta(meta_index as usize);
                 let field_infos = type_meta.get_field_infos().to_vec();
+                context.inc_depth()?;
                 for field_info in field_infos.iter() {
                     let nullable_field_type =
                         NullableFieldType::from(field_info.field_type.clone());
                     let read_ref_flag = 
get_read_ref_flag(&nullable_field_type);
                     skip_field_value(context, &nullable_field_type, 
read_ref_flag)?;
                 }
+                context.dec_depth();
                 Ok(())
             } else if type_id == TypeId::NAMED_EXT {
                 let remote_type_id = context.reader.read_varuint32();
@@ -175,21 +185,25 @@ pub fn skip_field_value(
                 let type_meta = context.get_meta(meta_index as usize);
                 assert_eq!(remote_type_id, type_meta.get_type_id());
                 let field_infos = type_meta.get_field_infos().to_vec();
+                context.inc_depth()?;
                 for field_info in field_infos.iter() {
                     let nullable_field_type =
                         NullableFieldType::from(field_info.field_type.clone());
                     let read_ref_flag = 
get_read_ref_flag(&nullable_field_type);
                     skip_field_value(context, &nullable_field_type, 
read_ref_flag)?;
                 }
+                context.dec_depth();
             } else if internal_id == ENUM_ID {
                 let _ordinal = context.reader.read_varuint32();
             } else if internal_id == EXT_ID {
                 let remote_type_id = context.reader.read_varuint32();
                 assert_eq!(remote_type_id, type_id_num);
+                context.inc_depth()?;
                 let type_resolver = context.get_fory().get_type_resolver();
                 type_resolver
                     .get_ext_harness(type_id_num)
                     .get_read_data_fn()(context, true)?;
+                context.dec_depth();
             } else {
                 unreachable!("unimplemented skipped type: {:?}", type_id_num);
             }
diff --git a/rust/fory-core/src/serializer/trait_object.rs 
b/rust/fory-core/src/serializer/trait_object.rs
index ba454b3ff..d4ce4c39a 100644
--- a/rust/fory-core/src/serializer/trait_object.rs
+++ b/rust/fory-core/src/serializer/trait_object.rs
@@ -238,12 +238,15 @@ macro_rules! register_trait_type {
             }
 
             fn fory_read(context: &mut $crate::resolver::context::ReadContext, 
is_field: bool) -> Result<Self, $crate::error::Error> {
+                context.inc_depth()?;
                 let fory_type_id = 
$crate::serializer::trait_object::read_trait_object_headers(context)?;
-                $crate::resolve_and_deserialize!(
+                let result = $crate::resolve_and_deserialize!(
                     fory_type_id, context, is_field,
                     |obj| Box::new(obj) as Box<dyn $trait_name>,
                     $trait_name, $($impl_type),+
-                )
+                );
+                context.dec_depth();
+                result
             }
 
             fn fory_read_data(_context: &mut 
$crate::resolver::context::ReadContext, _is_field: bool) -> Result<Self, 
$crate::error::Error> {
@@ -434,9 +437,11 @@ macro_rules! impl_smart_pointer_serializer {
                             ).into())
                     }
                     RefFlag::NotNullValue => {
+                        context.inc_depth()?;
                         let harness = context.read_any_typeinfo();
                         let deserializer_fn = harness.get_read_data_fn();
                         let boxed_any = deserializer_fn(context, is_field)?;
+                        context.dec_depth();
 
                         $(
                             if boxed_any.is::<$impl_type>() {
@@ -452,9 +457,11 @@ macro_rules! impl_smart_pointer_serializer {
                         ).into())
                     }
                     RefFlag::RefValue => {
+                        context.inc_depth()?;
                         let harness = context.read_any_typeinfo();
                         let deserializer_fn = harness.get_read_data_fn();
                         let boxed_any = deserializer_fn(context, is_field)?;
+                        context.dec_depth();
 
                         $(
                             if boxed_any.is::<$impl_type>() {
@@ -472,7 +479,6 @@ macro_rules! impl_smart_pointer_serializer {
                     }
                 }
             }
-
             fn fory_read_data(context: &mut 
$crate::resolver::context::ReadContext, is_field: bool) -> Result<Self, 
$crate::error::Error> {
                 let concrete_fory_type_id = context.reader.read_varuint32();
                 $crate::resolve_and_deserialize!(
@@ -566,7 +572,7 @@ impl Serializer for Box<dyn Serializer> {
 
     fn fory_read(context: &mut ReadContext, is_field: bool) -> Result<Self, 
Error> {
         let fory_type_id = read_trait_object_headers(context)?;
-
+        context.inc_depth()?;
         let type_resolver = context.get_fory().get_type_resolver();
 
         if let Some(harness) = type_resolver.get_harness(fory_type_id) {
@@ -574,9 +580,11 @@ impl Serializer for Box<dyn Serializer> {
             let to_serializer_fn = harness.get_to_serializer();
             let boxed_any = deserializer_fn(context, is_field, true)?;
             let trait_object = to_serializer_fn(boxed_any)?;
+            context.dec_depth();
             Ok(trait_object)
         } else {
             use crate::types::TypeId;
+            context.dec_depth();
             match fory_type_id {
                 id if id == TypeId::LIST as u32 => {
                     Err(Error::Other(anyhow::anyhow!(
@@ -605,7 +613,6 @@ impl Serializer for Box<dyn Serializer> {
             }
         }
     }
-
     fn fory_read_data(_context: &mut ReadContext, _is_field: bool) -> 
Result<Self, Error> {
         panic!("fory_read_data should not be called directly on Box<dyn 
Serializer>");
     }
diff --git a/rust/fory-core/src/serializer/weak.rs 
b/rust/fory-core/src/serializer/weak.rs
index 10b5099d4..57b722e37 100644
--- a/rust/fory-core/src/serializer/weak.rs
+++ b/rust/fory-core/src/serializer/weak.rs
@@ -338,7 +338,9 @@ impl<T: Serializer + ForyDefault + 'static> Serializer for 
RcWeak<T> {
         match ref_flag {
             RefFlag::Null => Ok(RcWeak::new()),
             RefFlag::RefValue => {
+                context.inc_depth()?;
                 let data = T::fory_read_data(context, _is_field)?;
+                context.dec_depth();
                 let rc = Rc::new(data);
                 let ref_id = context.ref_reader.store_rc_ref(rc);
                 let rc = context.ref_reader.get_rc_ref::<T>(ref_id).unwrap();
@@ -435,7 +437,9 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> 
Serializer for ArcWeak
         match ref_flag {
             RefFlag::Null => Ok(ArcWeak::new()),
             RefFlag::RefValue => {
+                context.inc_depth()?;
                 let data = T::fory_read_data(context, _is_field)?;
+                context.dec_depth();
                 let arc = Arc::new(data);
                 let ref_id = context.ref_reader.store_arc_ref(arc);
                 let arc = context.ref_reader.get_arc_ref::<T>(ref_id).unwrap();
@@ -465,7 +469,6 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> 
Serializer for ArcWeak
             _ => Err(anyhow!("Weak can only be Null, RefValue or Ref, got 
{:?}", ref_flag).into()),
         }
     }
-
     fn fory_read_data(context: &mut ReadContext, is_field: bool) -> 
Result<Self, Error> {
         Self::fory_read(context, is_field)
     }
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index d14d332da..cff396ac9 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -68,6 +68,11 @@ fn assign_value(fields: &[&Field]) -> Vec<TokenStream> {
                         #name: #var_name
                     }
                 }
+                StructField::ContainsTraitObject => {
+                    quote! {
+                        #name: #var_name.unwrap()
+                    }
+                }
                 _ => {
                     quote! {
                         #name: #var_name.unwrap_or_default()
@@ -262,6 +267,11 @@ pub fn gen_read_data(fields: &[&Field]) -> TokenStream {
                         #original_ident: #private_ident
                     }
                 }
+                StructField::ContainsTraitObject => {
+                    quote! {
+                        #original_ident: #private_ident.unwrap()
+                    }
+                }
                 _ => {
                     quote! {
                         #original_ident: #private_ident.unwrap_or_default()
@@ -381,7 +391,7 @@ fn gen_read_compatible_match_arm(field: &Field, var_name: 
&Ident) -> TokenStream
             quote! {
                 if _field.field_name.as_str() == #field_name_str {
                     let skip_ref_flag = 
fory_core::serializer::get_skip_ref_flag::<#ty>(context.get_fory());
-                    #var_name = 
Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, 
skip_ref_flag, false).unwrap());
+                    #var_name = 
Some(fory_core::serializer::read_ref_info_data::<#ty>(context, true, 
skip_ref_flag, false)?);
                 }
             }
         }
diff --git a/rust/tests/tests/compatible/test_basic_type.rs 
b/rust/tests/tests/compatible/test_basic_type.rs
index 1c9c9d69b..7756dad9b 100644
--- a/rust/tests/tests/compatible/test_basic_type.rs
+++ b/rust/tests/tests/compatible/test_basic_type.rs
@@ -445,7 +445,7 @@ fn basic() {
     // deserialize
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context = ReadContext::new(&fory, reader);
+    let mut read_context = ReadContext::new(&fory, reader, 5);
     deserialize_non_null(&fory, &mut read_context, false, true);
 }
 
@@ -460,7 +460,7 @@ fn basic_nullable() {
     // deserialize
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context = ReadContext::new(&fory, reader);
+    let mut read_context = ReadContext::new(&fory, reader, 5);
     deserialize_nullable(&fory, &mut read_context, false, true);
 }
 
@@ -475,7 +475,7 @@ fn auto_conv() {
     // deserialize_nullable
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context: ReadContext<'_, '_> = ReadContext::new(&fory, 
reader);
+    let mut read_context: ReadContext<'_, '_> = ReadContext::new(&fory, 
reader, 5);
     deserialize_nullable(&fory, &mut read_context, true, true);
     // serialize_nullable
     let mut writer = Writer::default();
@@ -484,6 +484,6 @@ fn auto_conv() {
     // deserialize_non-null
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context = ReadContext::new(&fory, reader);
+    let mut read_context = ReadContext::new(&fory, reader, 5);
     deserialize_non_null(&fory, &mut read_context, true, true);
 }
diff --git a/rust/tests/tests/compatible/test_container.rs 
b/rust/tests/tests/compatible/test_container.rs
index e0227b76a..78ad8e443 100644
--- a/rust/tests/tests/compatible/test_container.rs
+++ b/rust/tests/tests/compatible/test_container.rs
@@ -231,7 +231,7 @@ fn container_outer_auto_conv() {
     // deserialize_outer_nullable
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context = ReadContext::new(&fory, reader);
+    let mut read_context = ReadContext::new(&fory, reader, 5);
     assert_eq!(
         Some(basic_list()),
         fory.deserialize_with_context::<Option<Vec<String>>>(&mut read_context)
@@ -260,7 +260,7 @@ fn container_outer_auto_conv() {
     // deserialize_outer_non-null
     let bytes = write_context.writer.dump();
     let reader = Reader::new(bytes.as_slice());
-    let mut read_context = ReadContext::new(&fory, reader);
+    let mut read_context = ReadContext::new(&fory, reader, 5);
     assert_eq!(
         basic_list(),
         fory.deserialize_with_context::<Vec<String>>(&mut read_context)
@@ -315,7 +315,7 @@ fn collection_inner() {
         // deserialize
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             basic_list(),
             fory.deserialize_with_context::<Vec<String>>(&mut read_context)
@@ -377,7 +377,7 @@ fn collection_inner_auto_conv() {
         // deserialize_nullable
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             nullable_basic_list(true),
             fory.deserialize_with_context::<Vec<Option<String>>>(&mut 
read_context)
@@ -409,7 +409,7 @@ fn collection_inner_auto_conv() {
         // deserialize_non-null
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             basic_list(),
             fory.deserialize_with_context::<Vec<String>>(&mut read_context)
@@ -451,7 +451,7 @@ fn map_inner() {
         // deserialize
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             basic_map(),
             fory.deserialize_with_context::<HashMap<String, String>>(&mut 
read_context)
@@ -493,7 +493,7 @@ fn map_inner_auto_conv() {
         // deserialize_nullable
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             nullable_basic_map(true),
             fory.deserialize_with_context::<HashMap<Option<String>, 
Option<String>>>(
@@ -515,7 +515,7 @@ fn map_inner_auto_conv() {
         // deserialize_non-null
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             basic_map(),
             fory.deserialize_with_context::<HashMap<String, String>>(&mut 
read_context)
@@ -544,7 +544,7 @@ fn complex() {
         fory.serialize_with_context(&complex_container2(), &mut write_context);
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             nested_collection(),
             fory.deserialize_with_context::<Vec<HashSet<Item>>>(&mut 
read_context)
diff --git a/rust/tests/tests/compatible/test_struct_enum.rs 
b/rust/tests/tests/compatible/test_struct_enum.rs
index a44095e94..6b8791a9f 100644
--- a/rust/tests/tests/compatible/test_struct_enum.rs
+++ b/rust/tests/tests/compatible/test_struct_enum.rs
@@ -98,7 +98,7 @@ fn basic() {
         fory.serialize_with_context(&person, &mut write_context);
         let bytes = write_context.writer.dump();
         let reader = Reader::new(bytes.as_slice());
-        let mut read_context = ReadContext::new(&fory, reader);
+        let mut read_context = ReadContext::new(&fory, reader, 5);
         assert_eq!(
             person,
             fory.deserialize_with_context::<Person>(&mut read_context)
diff --git a/rust/tests/tests/mod.rs b/rust/tests/tests/mod.rs
index a86a16572..bc8c4e4d7 100644
--- a/rust/tests/tests/mod.rs
+++ b/rust/tests/tests/mod.rs
@@ -16,3 +16,4 @@
 // under the License.
 
 mod compatible;
+mod test_max_dyn_depth;
diff --git a/rust/tests/tests/test_cross_language.rs 
b/rust/tests/tests/test_cross_language.rs
index 87290e93f..1d25e960f 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -229,13 +229,13 @@ fn test_string_serializer() {
         .mode(Compatible)
         .xlang(true)
         .compress_string(false);
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
     let reader_compress = Reader::new(bytes.as_slice());
     let fory_compress = Fory::default()
         .mode(Compatible)
         .xlang(true)
         .compress_string(true);
-    let mut context_compress = ReadContext::new(&fory_compress, 
reader_compress);
+    let mut context_compress = ReadContext::new(&fory_compress, 
reader_compress, 5);
     let test_strings: Vec<String> = vec![
         // Latin1
         "ab".to_string(),
@@ -291,7 +291,7 @@ fn test_cross_language_serializer() {
     let reader = Reader::new(bytes.as_slice());
     let mut fory = Fory::default().mode(Compatible).xlang(true);
     fory.register::<Color>(101);
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
     assert_de!(fory, context, bool, true);
     assert_de!(fory, context, bool, false);
     assert_de!(fory, context, i32, -1);
@@ -421,7 +421,7 @@ fn test_list() {
     let mut fory = Fory::default().mode(Compatible);
     fory.register::<Item>(102);
     let reader = Reader::new(bytes.as_slice());
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
 
     let str_list = vec![Some("a".to_string()), Some("b".to_string())];
     let str_list2 = vec![None, Some("b".to_string())];
@@ -466,7 +466,7 @@ fn test_map() {
     let mut fory = Fory::default().mode(Compatible);
     fory.register::<Item>(102);
     let reader = Reader::new(bytes.as_slice());
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
 
     let str_map = HashMap::from([
         (Some("k1".to_string()), Some("v1".to_string())),
@@ -536,7 +536,7 @@ fn test_integer() {
     fory.register::<Item2>(101);
     let reader = Reader::new(bytes.as_slice());
 
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
     let f1 = 1;
     let f2 = Some(2);
     let f3 = Some(3);
@@ -698,7 +698,7 @@ fn test_consistent_named() {
     let data_file_path = get_data_file();
     let bytes = fs::read(&data_file_path).unwrap();
     let reader = Reader::new(bytes.as_slice());
-    let mut context = ReadContext::new(&fory, reader);
+    let mut context = ReadContext::new(&fory, reader, 5);
 
     assert_eq!(
         fory.deserialize_with_context::<Color>(&mut context)
diff --git a/rust/tests/tests/test_max_dyn_depth.rs 
b/rust/tests/tests/test_max_dyn_depth.rs
new file mode 100644
index 000000000..045f396f2
--- /dev/null
+++ b/rust/tests/tests/test_max_dyn_depth.rs
@@ -0,0 +1,135 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use fory_core::fory::Fory;
+use fory_derive::ForyObject;
+use std::any::Any;
+
+#[derive(ForyObject, Debug)]
+struct Container {
+    value: i32,
+    nested: Option<Box<dyn Any>>,
+}
+
+#[test]
+fn test_max_dyn_depth_exceeded_box_dyn_any() {
+    use fory_core::types::Mode;
+    for mode in [Mode::SchemaConsistent, Mode::Compatible] {
+        let mut fory = Fory::default().max_dyn_depth(2).mode(mode);
+        fory.register::<Container>(100);
+
+        let level3 = Container {
+            value: 3,
+            nested: None,
+        };
+        let level2 = Container {
+            value: 2,
+            nested: Some(Box::new(level3)),
+        };
+        let level1 = Container {
+            value: 1,
+            nested: Some(Box::new(level2)),
+        };
+
+        let outer: Box<dyn Any> = Box::new(level1);
+        let bytes = fory.serialize(&outer);
+        let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+        assert!(
+            result.is_err(),
+            "Expected deserialization to fail due to max depth"
+        );
+        let err = result.unwrap_err();
+        let err_msg = format!("{:?}", err);
+        assert!(err_msg.contains("Maximum dynamic object nesting depth"));
+    }
+}
+
+#[test]
+fn test_max_dyn_depth_within_limit_box_dyn_any() {
+    let mut fory = Fory::default().max_dyn_depth(3);
+    fory.register::<Container>(100);
+
+    let level3 = Container {
+        value: 3,
+        nested: None,
+    };
+    let level2 = Container {
+        value: 2,
+        nested: Some(Box::new(level3)),
+    };
+    let level1 = Container {
+        value: 1,
+        nested: Some(Box::new(level2)),
+    };
+
+    let outer: Box<dyn Any> = Box::new(level1);
+    let bytes = fory.serialize(&outer);
+    let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+    assert!(result.is_ok());
+}
+
+#[test]
+fn test_max_dyn_depth_default_exceeded() {
+    let mut fory = Fory::default();
+    fory.register::<Container>(100);
+
+    let mut current = Container {
+        value: 6,
+        nested: None,
+    };
+
+    for i in (1..=5).rev() {
+        current = Container {
+            value: i,
+            nested: Some(Box::new(current)),
+        };
+    }
+
+    let outer: Box<dyn Any> = Box::new(current);
+    let bytes = fory.serialize(&outer);
+    let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+
+    assert!(result.is_err());
+    let err = result.unwrap_err();
+    let err_msg = format!("{:?}", err);
+    assert!(err_msg.contains("Maximum dynamic object nesting depth"));
+    assert!(err_msg.contains("5"));
+}
+
+#[test]
+fn test_max_dyn_depth_default_within_limit() {
+    let mut fory = Fory::default();
+    fory.register::<Container>(100);
+
+    let mut current = Container {
+        value: 5,
+        nested: None,
+    };
+
+    for i in (1..=4).rev() {
+        current = Container {
+            value: i,
+            nested: Some(Box::new(current)),
+        };
+    }
+
+    let outer: Box<dyn Any> = Box::new(current);
+    let bytes = fory.serialize(&outer);
+    let result: Result<Box<dyn Any>, _> = fory.deserialize(&bytes);
+
+    assert!(result.is_ok());
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to