From 61383fd953f5a688895d8909a64fe2bdf8f24094 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Wed, 24 Feb 2021 01:42:39 +0100 Subject: [PATCH 1/5] Don't crash when printing a recursive structure --- .../Serialization/SafeSerializationMgr.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs index 8c2470cef5..515a1c0b8d 100644 --- a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs +++ b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs @@ -36,16 +36,22 @@ public static bool IsPrimitiveStructure(object serialized) return serialized is PrimitiveStructure; } - private object DumpValue(object value, bool includeType) + private object DumpValue(object value, bool includeType, bool allowTruncatedRecursion, List seenList) { var valueDumper = value as IDumper; if (valueDumper != null) { + if (seenList.Contains(valueDumper)) + { + if (!allowTruncatedRecursion) + throw new Exception("Trying to serialize a structure that loops on itself. Unable to serialize non-DAG structures."); + return "...recurse..."; + } return Dump(valueDumper, includeType); } else if (value is Dump) { return value; } else if (value is List) { - return (value as List).Select((v) => DumpValue(v, includeType)).ToList(); + return (value as List).Select((v) => DumpValue(v, includeType, allowTruncatedRecursion, seenList)).ToList(); } else if (IsSerializablePrimitive(value)) { return Structure.ToPrimitive(value); } else { @@ -53,15 +59,20 @@ private object DumpValue(object value, bool includeType) } } - public Dump Dump(IDumper dumper, bool includeType = true) + public Dump Dump(IDumper dumper, bool includeType = true, bool allowTruncatedRecursion = false, List seenList = null) { + if (seenList == null) + seenList = new List(); + seenList = new List(seenList); + seenList.Add(dumper); + var dump = dumper.Dump(); List keys = new List(dump.Keys); foreach (object key in keys) { - dump[key] = DumpValue(dump[key], includeType); + dump[key] = DumpValue(dump[key], includeType, allowTruncatedRecursion, seenList); } if (includeType) @@ -72,9 +83,9 @@ public Dump Dump(IDumper dumper, bool includeType = true) return dump; } - public string Serialize(IDumper serialized, IFormatWriter formatter, bool includeType = true) + public string Serialize(IDumper serialized, IFormatWriter formatter, bool includeType = true, bool allowTruncatedRecursion = false) { - return formatter.Write(Dump(serialized, includeType)); + return formatter.Write(Dump(serialized, includeType, allowTruncatedRecursion)); } public object CreateValue(object value) @@ -221,7 +232,7 @@ public IDumper Deserialize(string input, IFormatReader formatter) public string ToString(IDumper dumper) { - return Serialize(dumper, TerminalFormatter.Instance, false); + return Serialize(dumper, TerminalFormatter.Instance, false, true); } } } From e78078d67dcdab72886904dc8165cef767c204c7 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Wed, 10 Mar 2021 03:21:48 +0100 Subject: [PATCH 2/5] Fix recursion protection for non-lexicons --- .../Serialization/SafeSerializationMgr.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs index 515a1c0b8d..b3d0dfef2d 100644 --- a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs +++ b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs @@ -36,22 +36,25 @@ public static bool IsPrimitiveStructure(object serialized) return serialized is PrimitiveStructure; } - private object DumpValue(object value, bool includeType, bool allowTruncatedRecursion, List seenList) + private object DumpValue(object value, bool includeType, bool allowTruncatedRecursion, List seenList) { var valueDumper = value as IDumper; + if (seenList.Contains(value)) + { + if (!allowTruncatedRecursion) + throw new KOSSerializationException("Trying to serialize a structure that loops on itself. Unable to serialize non-DAG structures."); + return "...recurse..."; + } + if (valueDumper != null) { - if (seenList.Contains(valueDumper)) - { - if (!allowTruncatedRecursion) - throw new Exception("Trying to serialize a structure that loops on itself. Unable to serialize non-DAG structures."); - return "...recurse..."; - } - return Dump(valueDumper, includeType); + return Dump(valueDumper, includeType, allowTruncatedRecursion, seenList); } else if (value is Dump) { return value; } else if (value is List) { - return (value as List).Select((v) => DumpValue(v, includeType, allowTruncatedRecursion, seenList)).ToList(); + var nextSeenList = new List(seenList); + nextSeenList.Add(value); + return (value as List).Select((v) => DumpValue(v, includeType, allowTruncatedRecursion, nextSeenList)).ToList(); } else if (IsSerializablePrimitive(value)) { return Structure.ToPrimitive(value); } else { @@ -59,11 +62,14 @@ private object DumpValue(object value, bool includeType, bool allowTruncatedRecu } } - public Dump Dump(IDumper dumper, bool includeType = true, bool allowTruncatedRecursion = false, List seenList = null) + public Dump Dump(IDumper dumper, bool includeType = true, bool allowTruncatedRecursion = false, List seenList = null) { + // We want to allow DAG-like structure serialization (so nodes can occur in the output more than once. + // Cyclical graphs crash us with a stackoverflow however. To protect from this we check wether we're already + // in the list of objects that are between us and the root. if (seenList == null) - seenList = new List(); - seenList = new List(seenList); + seenList = new List(); + seenList = new List(seenList); seenList.Add(dumper); var dump = dumper.Dump(); From 0cc47da35725f6e8b09788b7583d8188f3e9175c Mon Sep 17 00:00:00 2001 From: Max Maton Date: Wed, 10 Mar 2021 03:48:32 +0100 Subject: [PATCH 3/5] Make error message more friendly --- doc/source/commands/serialization.rst | 11 +++++++++++ src/kOS.Safe/Serialization/SafeSerializationMgr.cs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/source/commands/serialization.rst b/doc/source/commands/serialization.rst index 4e3c245ac1..67ea2b1354 100644 --- a/doc/source/commands/serialization.rst +++ b/doc/source/commands/serialization.rst @@ -25,3 +25,14 @@ They allow to transform data structures into JSON objects and store them in a fi It serializes messages currently stored on message queues to ConfigNode (KSP data format) and adds them to KSP save files. It is **important** to remember that any data that you supply to :ref:`WRITEJSON` and :meth:`Connection:SENDMESSAGE` must be serializable. + +.. note:: + + It's not possible to serialize structures that loop on themselves. Only `Directed Acyclical Graphs `_ are supported. + + An example of a looping structure is: + + SET a TO LIST(). + SET b TO LIST(a). + a:ADD(b). + WRITEJSON(a, "test"). // <-- This will fail \ No newline at end of file diff --git a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs index b3d0dfef2d..6d3348f0d6 100644 --- a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs +++ b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs @@ -43,7 +43,7 @@ private object DumpValue(object value, bool includeType, bool allowTruncatedRecu if (seenList.Contains(value)) { if (!allowTruncatedRecursion) - throw new KOSSerializationException("Trying to serialize a structure that loops on itself. Unable to serialize non-DAG structures."); + throw new KOSSerializationException("Trying to serialize a structure that loops on itself. Only Directed Acyclical Graphs are supported."); return "...recurse..."; } From 8d4a83d173ca62e54d348304c3cd63d667174de2 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Wed, 10 Mar 2021 04:29:51 +0100 Subject: [PATCH 4/5] Mark example as code section --- doc/source/commands/serialization.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/commands/serialization.rst b/doc/source/commands/serialization.rst index 67ea2b1354..6744e6929f 100644 --- a/doc/source/commands/serialization.rst +++ b/doc/source/commands/serialization.rst @@ -30,7 +30,7 @@ It is **important** to remember that any data that you supply to :ref:`WRITEJSON It's not possible to serialize structures that loop on themselves. Only `Directed Acyclical Graphs `_ are supported. - An example of a looping structure is: + An example of a looping structure is:: SET a TO LIST(). SET b TO LIST(a). From 9acf3014d117e14496c5bbb2ba58843e78a66256 Mon Sep 17 00:00:00 2001 From: Max Maton Date: Sat, 27 Mar 2021 21:57:58 +0100 Subject: [PATCH 5/5] Fix strings always being marked as recursive --- src/kOS.Safe/Serialization/SafeSerializationMgr.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs index 6d3348f0d6..bfd2a5db00 100644 --- a/src/kOS.Safe/Serialization/SafeSerializationMgr.cs +++ b/src/kOS.Safe/Serialization/SafeSerializationMgr.cs @@ -40,7 +40,7 @@ private object DumpValue(object value, bool includeType, bool allowTruncatedRecu { var valueDumper = value as IDumper; - if (seenList.Contains(value)) + if (!(value is string) && seenList.Contains(value)) { if (!allowTruncatedRecursion) throw new KOSSerializationException("Trying to serialize a structure that loops on itself. Only Directed Acyclical Graphs are supported.");