summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Orlenko <zxteam@protonmail.com>2024-05-14 00:41:21 +0100
committerAlex Orlenko <zxteam@protonmail.com>2024-05-14 00:42:04 +0100
commit59c9abbac76b7ec60c470c94698dfb29f6683b2e (patch)
treec7ab70074e6353966ea9c722aba2315552237ac4
parent8f3de8aa19ac7263297b080862c0a7a0ddb5de04 (diff)
downloadmlua-59c9abbac76b7ec60c470c94698dfb29f6683b2e.zip
Fix serializing same table multiple times.
Fixes #408
-rw-r--r--src/serde/de.rs4
-rw-r--r--src/table.rs10
-rw-r--r--tests/serde.rs21
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();