Add frozen collections to sandbox and add yaml type serializers (#4721)

This commit is contained in:
Leon Friedrich
2023-12-16 12:36:58 -05:00
committed by GitHub
parent 33d394295e
commit 1f8b89e92f
6 changed files with 205 additions and 56 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -35,7 +36,7 @@ public sealed class UITheme : IPrototype
private ResPath _path;
[DataField("colors", readOnly: true)] // This is a prototype, why is this readonly??
public Dictionary<string, Color>? Colors { get; }
public FrozenDictionary<string, Color>? Colors { get; }
public ResPath Path => _path == default ? new ResPath(DefaultPath+"/"+ID) : _path;
private void ValidateFilePath(IResourceManager manager)

View File

@@ -280,6 +280,11 @@ Types:
SortedList`2: { All: True }
SortedSet`1: { All: True }
Stack`1: { All: True }
System.Collections.Frozen:
FrozenDictionary: { All: True }
FrozenSet: { All: True }
FrozenDictionary`2: { All: True }
FrozenSet`1: { All: True }
System.Collections.Immutable:
IImmutableDictionary`2: { All: True }
IImmutableList`1: { All: True }

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Robust.Shared.Serialization.Manager.Definition;
@@ -80,6 +81,14 @@ public sealed partial class SerializationManager
Expression call;
var sameType = baseType == actualType;
if (baseType.IsGenericType)
{
// Frozen dictionaries/sets are abstract and have a bunch of implementations, but we always serialize them as their abstract type.
var t = baseType.GetGenericTypeDefinition();
if (t == typeof(FrozenDictionary<,>) || t == typeof(FrozenSet<>))
actualType = baseType;
}
var targetVar = sameType ? targetParam : Expression.Variable(actualType);
Expression sourceVar = sameType ? sourceParam : Expression.Convert(sourceParam, actualType);
if (manager._regularSerializerProvider.TryGetTypeSerializer(typeof(ITypeCopier<>), actualType, out var copier))

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Globalization;
using System.Linq.Expressions;
using Robust.Shared.Serialization.Manager.Definition;
@@ -74,6 +75,14 @@ public sealed partial class SerializationManager
? Expression.Convert(objParam, sameType ? baseType.EnsureNotNullableType() : actualType)
: sameType ? objParam : Expression.Convert(objParam, actualType);
if (baseType.IsGenericType)
{
// Frozen dictionaries/sets are abstract and have a bunch of implementations, but we always serialize them as their abstract type.
var t = baseType.GetGenericTypeDefinition();
if (t == typeof(FrozenDictionary<,>) || t == typeof(FrozenSet<>))
actualType = baseType;
}
Expression call;
if (serializationManager._regularSerializerProvider.TryGetTypeSerializer(typeof(ITypeWriter<>), actualType, out var serializer))
{

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.IoC;
@@ -16,42 +17,19 @@ public sealed class DictionarySerializer<TKey, TValue> :
ITypeSerializer<Dictionary<TKey, TValue>, MappingDataNode>,
ITypeSerializer<IReadOnlyDictionary<TKey, TValue>, MappingDataNode>,
ITypeSerializer<SortedDictionary<TKey, TValue>, MappingDataNode>,
ITypeSerializer<FrozenDictionary<TKey, TValue>, MappingDataNode>,
ITypeCopier<Dictionary<TKey, TValue>>,
ITypeCopier<SortedDictionary<TKey, TValue>>,
ITypeCopyCreator<IReadOnlyDictionary<TKey, TValue>>,
ITypeCopier<SortedDictionary<TKey, TValue>> where TKey : notnull
ITypeCopyCreator<FrozenDictionary<TKey, TValue>>
where TKey : notnull
{
private MappingDataNode InterfaceWrite(
ISerializationManager serializationManager,
IDictionary<TKey, TValue> value,
bool alwaysWrite = false,
ISerializationContext? context = null)
#region Validate
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
IDependencyCollection dependencies, ISerializationContext? context = null)
{
var mappingNode = new MappingDataNode();
foreach (var (key, val) in value)
{
mappingNode.Add(
serializationManager.WriteValue(key, alwaysWrite, context),
serializationManager.WriteValue(val, alwaysWrite, context));
}
return mappingNode;
}
public Dictionary<TKey, TValue> Read(ISerializationManager serializationManager,
MappingDataNode node, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context,
ISerializationManager.InstantiationDelegate<Dictionary<TKey, TValue>>? instanceProvider)
{
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<TKey, TValue>();
foreach (var (key, value) in node.Children)
{
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
serializationManager.Read<TValue>(value, hookCtx, context));
}
return dict;
return Validate(serializationManager, node, context);
}
ValidationNode ITypeValidator<SortedDictionary<TKey, TValue>, MappingDataNode>.Validate(
@@ -88,6 +66,34 @@ public sealed class DictionarySerializer<TKey, TValue> :
return new ValidatedMappingNode(mapping);
}
#endregion
#region Write
private MappingDataNode InterfaceWrite(
ISerializationManager serializationManager,
IReadOnlyDictionary<TKey, TValue> value,
bool alwaysWrite = false,
ISerializationContext? context = null)
{
var mappingNode = new MappingDataNode();
foreach (var (key, val) in value)
{
mappingNode.Add(
serializationManager.WriteValue(key, alwaysWrite, context),
serializationManager.WriteValue(val, alwaysWrite, context));
}
return mappingNode;
}
public DataNode Write(ISerializationManager serializationManager, FrozenDictionary<TKey, TValue> value, IDependencyCollection dependencies,
bool alwaysWrite = false, ISerializationContext? context = null)
{
return InterfaceWrite(serializationManager, value, alwaysWrite, context);
}
public DataNode Write(ISerializationManager serializationManager, Dictionary<TKey, TValue> value,
IDependencyCollection dependencies,
bool alwaysWrite = false,
@@ -112,6 +118,48 @@ public sealed class DictionarySerializer<TKey, TValue> :
return InterfaceWrite(serializationManager, value.ToDictionary(k => k.Key, v => v.Value), alwaysWrite, context);
}
#endregion
#region Read
public Dictionary<TKey, TValue> Read(ISerializationManager serializationManager,
MappingDataNode node, IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context,
ISerializationManager.InstantiationDelegate<Dictionary<TKey, TValue>>? instanceProvider)
{
var dict = instanceProvider != null ? instanceProvider() : new Dictionary<TKey, TValue>();
foreach (var (key, value) in node.Children)
{
dict.Add(serializationManager.Read<TKey>(key, hookCtx, context),
serializationManager.Read<TValue>(value, hookCtx, context));
}
return dict;
}
public FrozenDictionary<TKey, TValue> Read(ISerializationManager serializationManager, MappingDataNode node,
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null,
ISerializationManager.InstantiationDelegate<FrozenDictionary<TKey, TValue>>? instanceProvider = null)
{
if (instanceProvider != null)
{
Logger.Warning(
$"Provided value to a Read-call for a {nameof(FrozenDictionary<TKey, TValue>)}. Ignoring...");
}
var array = new KeyValuePair<TKey, TValue>[node.Children.Count];
int i = 0;
foreach (var (key, value) in node.Children)
{
var k = serializationManager.Read<TKey>(key, hookCtx, context);
var v = serializationManager.Read<TValue>(value, hookCtx, context);
array[i++] = new(k,v);
}
return array.ToFrozenDictionary();
}
IReadOnlyDictionary<TKey, TValue> ITypeReader<IReadOnlyDictionary<TKey, TValue>, MappingDataNode>.Read(
ISerializationManager serializationManager, MappingDataNode node,
IDependencyCollection dependencies,
@@ -152,6 +200,10 @@ public sealed class DictionarySerializer<TKey, TValue> :
return dict;
}
#endregion
#region Copy
public void CopyTo(ISerializationManager serializationManager, Dictionary<TKey, TValue> source, ref Dictionary<TKey, TValue> target,
IDependencyCollection dependencies,
SerializationHookContext hookCtx,
@@ -182,8 +234,7 @@ public sealed class DictionarySerializer<TKey, TValue> :
public IReadOnlyDictionary<TKey, TValue> CreateCopy(ISerializationManager serializationManager, IReadOnlyDictionary<TKey, TValue> source,
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var target = new Dictionary<TKey, TValue>();
target.EnsureCapacity(source.Count);
var target = new Dictionary<TKey, TValue>(source.Count);
foreach (var value in source)
{
target.Add(
@@ -193,4 +244,21 @@ public sealed class DictionarySerializer<TKey, TValue> :
return target;
}
public FrozenDictionary<TKey, TValue> CreateCopy(ISerializationManager serializationManager, FrozenDictionary<TKey, TValue> source,
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var array = new KeyValuePair<TKey, TValue>[source.Count];
int i = 0;
foreach (var value in source)
{
var k = serializationManager.CreateCopy(value.Key, hookCtx, context);
var v = serializationManager.CreateCopy(value.Value, hookCtx, context);
array[i++] = new(k, v);
}
return array.ToFrozenDictionary();
}
#endregion
}

View File

@@ -1,3 +1,4 @@
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@@ -15,10 +16,14 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
[TypeSerializer]
public sealed class HashSetSerializer<T> :
ITypeSerializer<HashSet<T>, SequenceDataNode>,
ITypeSerializer<FrozenSet<T>, SequenceDataNode>,
ITypeSerializer<ImmutableHashSet<T>, SequenceDataNode>,
ITypeCopier<HashSet<T>>,
ITypeCopyCreator<ImmutableHashSet<T>>
ITypeCopyCreator<ImmutableHashSet<T>>,
ITypeCopyCreator<FrozenSet<T>>
{
#region Read
HashSet<T> ITypeReader<HashSet<T>, SequenceDataNode>.Read(ISerializationManager serializationManager,
SequenceDataNode node,
IDependencyCollection dependencies,
@@ -36,6 +41,51 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
return set;
}
public FrozenSet<T> Read(ISerializationManager serializationManager, SequenceDataNode node, IDependencyCollection dependencies,
SerializationHookContext hookCtx, ISerializationContext? context = null, ISerializationManager.InstantiationDelegate<FrozenSet<T>>? instanceProvider = null)
{
if (instanceProvider != null)
Logger.Warning($"Provided value to a Read-call for a {nameof(FrozenSet<T>)}. Ignoring...");
var array = new T[node.Sequence.Count];
var i = 0;
foreach (var dataNode in node.Sequence)
{
array[i++] = serializationManager.Read<T>(dataNode, hookCtx, context);
}
return array.ToFrozenSet();
}
ImmutableHashSet<T> ITypeReader<ImmutableHashSet<T>, SequenceDataNode>.Read(
ISerializationManager serializationManager,
SequenceDataNode node,
IDependencyCollection dependencies,
SerializationHookContext hookCtx,
ISerializationContext? context,
ISerializationManager.InstantiationDelegate<ImmutableHashSet<T>>? instanceProvider)
{
if (instanceProvider != null)
Logger.Warning($"Provided value to a Read-call for a {nameof(ImmutableHashSet<T>)}. Ignoring...");
var set = ImmutableHashSet.CreateBuilder<T>();
foreach (var dataNode in node.Sequence)
{
set.Add(serializationManager.Read<T>(dataNode, hookCtx, context));
}
return set.ToImmutable();
}
#endregion
#region Validate
public ValidationNode Validate(ISerializationManager serializationManager, SequenceDataNode node,
IDependencyCollection dependencies, ISerializationContext? context = null)
{
return Validate(serializationManager, node, context);
}
ValidationNode ITypeValidator<ImmutableHashSet<T>, SequenceDataNode>.Validate(
ISerializationManager serializationManager,
SequenceDataNode node, IDependencyCollection dependencies, ISerializationContext? context)
@@ -61,6 +111,10 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
return new ValidatedSequenceNode(list);
}
#endregion
#region Write
public DataNode Write(ISerializationManager serializationManager, ImmutableHashSet<T> value,
IDependencyCollection dependencies,
bool alwaysWrite = false,
@@ -69,6 +123,12 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
return Write(serializationManager, value.ToHashSet(), dependencies, alwaysWrite, context);
}
public DataNode Write(ISerializationManager serializationManager, FrozenSet<T> value, IDependencyCollection dependencies,
bool alwaysWrite = false, ISerializationContext? context = null)
{
return Write(serializationManager, value.ToHashSet(), dependencies, alwaysWrite, context);
}
public DataNode Write(ISerializationManager serializationManager, HashSet<T> value,
IDependencyCollection dependencies, bool alwaysWrite = false,
ISerializationContext? context = null)
@@ -83,25 +143,9 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
return sequence;
}
ImmutableHashSet<T> ITypeReader<ImmutableHashSet<T>, SequenceDataNode>.Read(
ISerializationManager serializationManager,
SequenceDataNode node,
IDependencyCollection dependencies,
SerializationHookContext hookCtx,
ISerializationContext? context,
ISerializationManager.InstantiationDelegate<ImmutableHashSet<T>>? instanceProvider)
{
if (instanceProvider != null)
Logger.Warning($"Provided value to a Read-call for a {nameof(ImmutableHashSet<T>)}. Ignoring...");
var set = ImmutableHashSet.CreateBuilder<T>();
#endregion
foreach (var dataNode in node.Sequence)
{
set.Add(serializationManager.Read<T>(dataNode, hookCtx, context));
}
return set.ToImmutable();
}
#region Copy
public void CopyTo(ISerializationManager serializationManager, HashSet<T> source, ref HashSet<T> target,
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
@@ -118,8 +162,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
public ImmutableHashSet<T> CreateCopy(ISerializationManager serializationManager, ImmutableHashSet<T> source,
IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var target = new HashSet<T>();
target.EnsureCapacity(source.Count);
var target = new HashSet<T>(source.Count);
foreach (var val in source)
{
@@ -128,5 +171,19 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic
return target.ToImmutableHashSet();
}
public FrozenSet<T> CreateCopy(ISerializationManager serializationManager, FrozenSet<T> source, IDependencyCollection dependencies,
SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var array = new T[source.Count];
var i = 0;
foreach (var val in source)
{
array[i++] = serializationManager.CreateCopy(val, hookCtx, context);
}
return array.ToFrozenSet();
}
#endregion
}
}