diff options
author | Alex Orlenko <zxteam@protonmail.com> | 2024-05-14 00:41:21 +0100 |
---|---|---|
committer | Alex Orlenko <zxteam@protonmail.com> | 2024-05-14 00:42:04 +0100 |
commit | 59c9abbac76b7ec60c470c94698dfb29f6683b2e (patch) | |
tree | c7ab70074e6353966ea9c722aba2315552237ac4 | |
parent | 8f3de8aa19ac7263297b080862c0a7a0ddb5de04 (diff) | |
download | mlua-59c9abbac76b7ec60c470c94698dfb29f6683b2e.zip |
Fix serializing same table multiple times.
Fixes #408
-rw-r--r-- | src/serde/de.rs | 4 | ||||
-rw-r--r-- | src/table.rs | 10 | ||||
-rw-r--r-- | tests/serde.rs | 21 |
3 files changed, 28 insertions, 7 deletions
diff --git a/src/serde/de.rs b/src/serde/de.rs index 6933e4e..3cc182d 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -660,14 +660,14 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> { // Adds `ptr` to the `visited` map and removes on drop // Used to track recursive tables but allow to traverse same tables multiple times -struct RecursionGuard { +pub(crate) struct RecursionGuard { ptr: *const c_void, visited: Rc<RefCell<FxHashSet<*const c_void>>>, } impl RecursionGuard { #[inline] - fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self { + pub(crate) fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self { let visited = Rc::clone(visited); let ptr = table.to_pointer(); visited.borrow_mut().insert(ptr); diff --git a/src/table.rs b/src/table.rs index 69e8a6d..f0b4f82 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1089,7 +1089,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> { where S: Serializer, { - use crate::serde::de::{check_value_for_skip, MapPairs}; + use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard}; use crate::value::SerializableValue; let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res { @@ -1101,7 +1101,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> { let options = self.options; let visited = &self.visited; - visited.borrow_mut().insert(self.table.to_pointer()); + let _guard = RecursionGuard::new(&self.table, visited); // Array let len = self.table.raw_len(); @@ -1109,7 +1109,7 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> { let mut seq = serializer.serialize_seq(Some(len))?; let mut serialize_err = None; let res = self.table.for_each_value::<Value>(|value| { - let skip = check_value_for_skip(&value, self.options, &self.visited) + let skip = check_value_for_skip(&value, self.options, visited) .map_err(|err| Error::SerializeError(err.to_string()))?; if skip { // continue iteration @@ -1129,9 +1129,9 @@ impl<'a, 'lua> Serialize for SerializableTable<'a, 'lua> { let mut map = serializer.serialize_map(None)?; let mut serialize_err = None; let mut process_pair = |key, value| { - let skip_key = check_value_for_skip(&key, self.options, &self.visited) + let skip_key = check_value_for_skip(&key, self.options, visited) .map_err(|err| Error::SerializeError(err.to_string()))?; - let skip_value = check_value_for_skip(&value, self.options, &self.visited) + let skip_value = check_value_for_skip(&value, self.options, visited) .map_err(|err| Error::SerializeError(err.to_string()))?; if skip_key || skip_value { // continue iteration diff --git a/tests/serde.rs b/tests/serde.rs index 1eabf42..11c1d71 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -270,6 +270,27 @@ fn test_serialize_globals() -> LuaResult<()> { } #[test] +fn test_serialize_same_table_twice() -> LuaResult<()> { + let lua = Lua::new(); + + let value = lua + .load( + r#" + local foo = {} + return { + a = foo, + b = foo, + } + "#, + ) + .eval::<Value>()?; + let json = serde_json::to_string(&value.to_serializable().sort_keys(true)).unwrap(); + assert_eq!(json, r#"{"a":{},"b":{}}"#); + + Ok(()) +} + +#[test] fn test_to_value_struct() -> LuaResult<()> { let lua = Lua::new(); let globals = lua.globals(); |