mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-01 17:47:24 +02:00
1474 lines
61 KiB
C#
1474 lines
61 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Serialization.Manager.Definition;
|
|
using Robust.Shared.Serialization.Manager.Exceptions;
|
|
using Robust.Shared.Serialization.Markdown;
|
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
|
using Robust.Shared.Serialization.Markdown.Sequence;
|
|
using Robust.Shared.Serialization.Markdown.Value;
|
|
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
|
using Robust.Shared.Utility;
|
|
|
|
// Avoid accidentally mixing up overloads.
|
|
// ReSharper disable RedundantTypeArgumentsOfMethod
|
|
|
|
namespace Robust.Shared.Serialization.Manager
|
|
{
|
|
public partial class SerializationManager
|
|
{
|
|
private delegate object? ReadBoxingDelegate(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null);
|
|
|
|
private delegate T ReadGenericDelegate<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null);
|
|
|
|
private delegate object? ReadDelegate(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
bool notNullableOverride = false
|
|
);
|
|
|
|
private MethodInfo _read = default!;
|
|
private readonly ConcurrentDictionary<(Type type, bool notNullableOverride), ReadBoxingDelegate> _readBoxingDelegates = new();
|
|
private readonly ConcurrentDictionary<(Type baseType, Type actualType, Type node, bool notNullableOverride), object> _readGenericBaseDelegates = new();
|
|
private readonly ConcurrentDictionary<(Type value, Type node, bool notNullableOverride), object> _readGenericDelegates = new();
|
|
private readonly ConcurrentDictionary<Type, Func<DataNode, SerializationHookContext, ISerializationContext?, bool, object>> _reads = new();
|
|
|
|
public T Read<T>(DataNode node, ISerializationContext? context = null, bool skipHook = false, ISerializationManager.InstantiationDelegate<T>? instanceProvider = null, bool notNullableOverride = false)
|
|
{
|
|
return Read<T>(
|
|
node,
|
|
SerializationHookContext.ForSkipHooks(skipHook),
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride);
|
|
}
|
|
|
|
public T[] ReadArray<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T[]>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
{
|
|
var baseType = typeof(T);
|
|
var nullable = baseType.IsNullable();
|
|
var isValueType = baseType.IsValueType;
|
|
|
|
T[]? val = null;
|
|
if (instanceProvider != null)
|
|
val ??= [];
|
|
|
|
if (context != null)
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializerArray<ITypeReader<T[], MappingDataNode>, T, MappingDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = serializer.Read(this, mapping, DependencyCollection, hookCtx, context, instanceProvider);
|
|
if (notNullableOverride)
|
|
Debug.Assert(val != null, "Reader call returned null value! Forbidden!");
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializerArray<ITypeReader<T[], SequenceDataNode>, T, SequenceDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = serializer.Read(this, sequence, DependencyCollection, hookCtx, context, instanceProvider);
|
|
if (notNullableOverride)
|
|
Debug.Assert(val != null, "Reader call returned null value! Forbidden!");
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializerArray<ITypeReader<T[], ValueDataNode>, T, ValueDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = serializer.Read(this, value, DependencyCollection, hookCtx, context, instanceProvider);
|
|
if (notNullableOverride)
|
|
Debug.Assert(val != null, "Reader call returned null value! Forbidden!");
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
if (node.IsNull)
|
|
{
|
|
if (nullable && !notNullableOverride)
|
|
val = null;
|
|
else
|
|
throw new NullNotAllowedException();
|
|
}
|
|
|
|
if (!nullable && !isValueType && val == null)
|
|
throw new ReadCallReturnedNullException();
|
|
|
|
return val!;
|
|
|
|
void RegularRead()
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializerArray<ITypeReader<T[], MappingDataNode>, T, MappingDataNode>(out var reader))
|
|
{
|
|
val = Read<T[], MappingDataNode>(reader, mapping, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializerArray<ITypeReader<T[], SequenceDataNode>, T, SequenceDataNode>(out var reader))
|
|
{
|
|
val = Read<T[], SequenceDataNode>(reader, sequence, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializerArray<ITypeReader<T[], ValueDataNode>, T, ValueDataNode>(out var reader))
|
|
{
|
|
val = Read<T[], ValueDataNode>(reader, value, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
val = node switch
|
|
{
|
|
SequenceDataNode sequence => ReadArraySequence<T>(sequence, hookCtx, context),
|
|
ValueDataNode value => ReadArrayValue<T>(value, hookCtx, context),
|
|
_ => throw new ArgumentException($"Cannot read array from data node type {node.GetType()}")
|
|
};
|
|
}
|
|
}
|
|
|
|
public T ReadStructDefinition<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
where T : struct, ISerializationGenerated<T>
|
|
{
|
|
var baseType = typeof(T);
|
|
var nullable = baseType.IsNullable();
|
|
var isArray = ReadTypeMetadata<T>.IsArray;
|
|
|
|
T val = default!;
|
|
|
|
if (instanceProvider != null)
|
|
val = instanceProvider.Invoke();
|
|
|
|
if (context != null)
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, MappingDataNode>(
|
|
serializer,
|
|
mapping,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, SequenceDataNode>(
|
|
serializer,
|
|
sequence,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, ValueDataNode>(
|
|
serializer,
|
|
value,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
if (node.IsNull)
|
|
{
|
|
if (nullable && !notNullableOverride)
|
|
val = default!;
|
|
else if (baseType == typeof(EntityUid))
|
|
val = default!;
|
|
else
|
|
throw new NullNotAllowedException();
|
|
}
|
|
|
|
return val;
|
|
|
|
void RegularRead()
|
|
{
|
|
var hasSerializer = false;
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, MappingDataNode>(reader, mapping, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, SequenceDataNode>(reader, sequence, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, ValueDataNode>(reader, value, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasSerializer)
|
|
{
|
|
if (isArray)
|
|
{
|
|
val = node switch
|
|
{
|
|
SequenceDataNode sequence => (T)(object)ReadArraySequence<T>(sequence, hookCtx, context),
|
|
ValueDataNode value => (T)(object)ReadArrayValue<T>(value, hookCtx, context),
|
|
_ => throw new ArgumentException($"Cannot read array from data node type {node.GetType()}")
|
|
};
|
|
}
|
|
else if (baseType.IsEnum)
|
|
{
|
|
val = node switch
|
|
{
|
|
SequenceDataNode sequence => ReadEnumSequence<T>(sequence),
|
|
ValueDataNode value => ReadEnumValue<T>(value),
|
|
_ => throw new InvalidNodeTypeException(
|
|
$"Cannot serialize node as {baseType}, unsupported node type {node.GetType()}")
|
|
};
|
|
}
|
|
else if (baseType.IsAssignableTo(typeof(ISelfSerialize)))
|
|
{
|
|
switch (node)
|
|
{
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
var selfSerialize = (ISelfSerialize) val;
|
|
selfSerialize.Deserialize(value.Value);
|
|
val = (T) selfSerialize;
|
|
break;
|
|
}
|
|
default:
|
|
throw new InvalidNodeTypeException($"Cannot read {nameof(ISelfSerialize)} from node type {node.GetType()}. Expected {nameof(ValueDataNode)}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
T.Read(ref val, mapping, this, hookCtx, context);
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
if (value.Value != string.Empty)
|
|
throw new ArgumentException($"No mapping node provided for type {typeof(T)} at line: {node.Start.Line}");
|
|
|
|
break;
|
|
}
|
|
default:
|
|
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
|
|
}
|
|
|
|
TryRunAfterHook(val, hookCtx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public T ReadEnum<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
where T : struct, Enum
|
|
{
|
|
var baseType = typeof(T);
|
|
var nullable = baseType.IsNullable();
|
|
|
|
T val = default!;
|
|
|
|
if (instanceProvider != null)
|
|
val = instanceProvider.Invoke();
|
|
|
|
if (context != null)
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, MappingDataNode>(
|
|
serializer,
|
|
mapping,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, SequenceDataNode>(
|
|
serializer,
|
|
sequence,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, ValueDataNode>(
|
|
serializer,
|
|
value,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
if (node.IsNull)
|
|
{
|
|
if (nullable && !notNullableOverride)
|
|
val = default!;
|
|
else
|
|
throw new NullNotAllowedException();
|
|
}
|
|
|
|
return val;
|
|
|
|
void RegularRead()
|
|
{
|
|
var hasSerializer = false;
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, MappingDataNode>(reader, mapping, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, SequenceDataNode>(reader, sequence, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, ValueDataNode>(reader, value, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasSerializer)
|
|
return;
|
|
|
|
val = node switch
|
|
{
|
|
SequenceDataNode sequence => ReadEnumSequence<T>(sequence),
|
|
ValueDataNode value => ReadEnumValue<T>(value),
|
|
_ => throw new InvalidNodeTypeException(
|
|
$"Cannot serialize node as {baseType}, unsupported node type {node.GetType()}")
|
|
};
|
|
}
|
|
}
|
|
|
|
private object? ReadObject<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
bool notNullableOverride = false)
|
|
{
|
|
return Read<T>(node, hookCtx, context, null, notNullableOverride);
|
|
}
|
|
|
|
public T Read<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
{
|
|
if (node.Tag?.StartsWith("!type:") ?? false)
|
|
{
|
|
var type = ResolveConcreteType(typeof(T), node.Tag.Substring(6));
|
|
if (type.IsInterface || type.IsAbstract)
|
|
{
|
|
throw new ArgumentException($"Interface or abstract type used for !type node. Type: {type}");
|
|
}
|
|
|
|
//!type tag overrides null value on default. i did this because i couldnt come up with a usecase where you'd specify the type but have a null value. yell at me if you found one -paul
|
|
if (node.IsEmpty || node.IsNull)
|
|
{
|
|
if (instanceProvider != null)
|
|
{
|
|
var instantiatedVal = instanceProvider();
|
|
//make this debug-only? -<paul
|
|
if (instantiatedVal?.GetType() != type)
|
|
throw new InvalidInstanceReturnedException(type, instantiatedVal?.GetType());
|
|
|
|
return instantiatedVal;
|
|
}
|
|
|
|
return GetOrCreateInstantiator<T>(false, type)();
|
|
}
|
|
|
|
return ((ReadGenericDelegate<T>)_readGenericBaseDelegates.GetOrAdd(
|
|
(typeof(T), type, node.GetType()!, notNullableOverride),
|
|
static (tuple, manager) => ReadDelegateValueFactory(tuple.baseType, tuple.actualType, tuple.node, tuple.notNullableOverride, manager),
|
|
this))(node, hookCtx, context, instanceProvider);
|
|
}
|
|
|
|
var baseType = typeof(T);
|
|
if (baseType.IsEnum ||
|
|
ReadTypeMetadata<T>.IsArray ||
|
|
(baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(Nullable<>)))
|
|
{
|
|
return ((ReadGenericDelegate<T>)_readGenericDelegates.GetOrAdd((typeof(T), node.GetType()!, notNullableOverride),
|
|
static (tuple, manager) => ReadDelegateValueFactory(tuple.value, tuple.value, tuple.node, tuple.notNullableOverride, manager), this))(node, hookCtx, context, instanceProvider);
|
|
}
|
|
|
|
var nullable = baseType.IsNullable();
|
|
|
|
T val = default!;
|
|
if (node.IsNull)
|
|
{
|
|
if (nullable && !notNullableOverride)
|
|
return default!;
|
|
|
|
if (baseType == typeof(EntityUid))
|
|
return (T) (object) EntityUid.Invalid;
|
|
|
|
throw new NullNotAllowedException();
|
|
}
|
|
|
|
if (instanceProvider != null)
|
|
val = instanceProvider.Invoke();
|
|
|
|
if (context != null)
|
|
{
|
|
switch (node)
|
|
{
|
|
// TODO actual type for type tag
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, MappingDataNode>(
|
|
serializer,
|
|
mapping,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, SequenceDataNode>(
|
|
serializer,
|
|
sequence,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, ValueDataNode>(
|
|
serializer,
|
|
value,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
var isValueType = baseType.IsValueType;
|
|
if (!nullable && !isValueType && val == null)
|
|
throw new ReadCallReturnedNullException();
|
|
|
|
return val;
|
|
|
|
void RegularRead()
|
|
{
|
|
var hasSerializer = false;
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, MappingDataNode>(reader, mapping, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, SequenceDataNode>(reader, sequence, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, ValueDataNode>(reader, value, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasSerializer)
|
|
{
|
|
if (baseType.IsAssignableTo(typeof(ISelfSerialize)))
|
|
{
|
|
switch (node)
|
|
{
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= GetOrCreateInstantiator<T>(false);
|
|
val = instanceProvider();
|
|
var selfSerialize = (ISelfSerialize?) val;
|
|
selfSerialize!.Deserialize(value.Value);
|
|
val = (T) selfSerialize;
|
|
break;
|
|
}
|
|
default:
|
|
throw new InvalidNodeTypeException($"Cannot read {nameof(ISelfSerialize)} from node type {node.GetType()}. Expected {nameof(ValueDataNode)}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
var definition = GetDefinition(typeof(T));
|
|
if (definition == null)
|
|
throw new ArgumentException($"No data definition found for type {baseType} with node type {node.GetType()} when reading");
|
|
|
|
var valObj = instanceProvider == null ? definition.InstantiateObj() : instanceProvider.Invoke()!;
|
|
definition.PopulateObj(ref valObj, mapping, this, hookCtx, context);
|
|
val = (T) valObj;
|
|
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= GetOrCreateInstantiator<T>(false);
|
|
val = instanceProvider();
|
|
if (value.Value != string.Empty)
|
|
throw new ArgumentException($"No mapping node provided for type {baseType} at line: {node.Start.Line}");
|
|
|
|
break;
|
|
}
|
|
default:
|
|
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
|
|
}
|
|
|
|
TryRunAfterHook(val, hookCtx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public T ReadDefinition<T>(
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false) where T : ISerializationGenerated<T>
|
|
{
|
|
if (node.Tag?.StartsWith("!type:") ?? false)
|
|
{
|
|
var type = ResolveConcreteType(typeof(T), node.Tag.Substring(6));
|
|
if (type.IsInterface || type.IsAbstract)
|
|
{
|
|
throw new ArgumentException($"Interface or abstract type used for !type node. Type: {type}");
|
|
}
|
|
|
|
//!type tag overrides null value on default. i did this because i couldnt come up with a usecase where you'd specify the type but have a null value. yell at me if you found one -paul
|
|
if (node.IsEmpty || node.IsNull)
|
|
{
|
|
if (instanceProvider != null)
|
|
{
|
|
var instantiatedVal = instanceProvider();
|
|
//make this debug-only? -<paul
|
|
if (instantiatedVal.GetType() != type)
|
|
throw new InvalidInstanceReturnedException(type, instantiatedVal?.GetType());
|
|
|
|
return instantiatedVal;
|
|
}
|
|
|
|
return GetOrCreateInstantiator<T>(false, type)();
|
|
}
|
|
|
|
return ((ReadGenericDelegate<T>)_readGenericBaseDelegates.GetOrAdd(
|
|
(typeof(T), type, node.GetType()!, notNullableOverride),
|
|
static (tuple, manager) => ReadDelegateValueFactory(tuple.baseType, tuple.actualType, tuple.node, tuple.notNullableOverride, manager),
|
|
this))(node, hookCtx, context, instanceProvider);
|
|
}
|
|
|
|
var baseType = typeof(T);
|
|
var nullable = baseType.IsNullable();
|
|
|
|
T val = default!;
|
|
if (node.IsNull)
|
|
{
|
|
if (nullable && !notNullableOverride)
|
|
return default!;
|
|
|
|
if (baseType == typeof(EntityUid))
|
|
return (T) (object) EntityUid.Invalid;
|
|
|
|
throw new NullNotAllowedException();
|
|
}
|
|
|
|
if (instanceProvider != null)
|
|
val = instanceProvider.Invoke();
|
|
|
|
if (context != null)
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, MappingDataNode>(
|
|
serializer,
|
|
mapping,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, SequenceDataNode>(
|
|
serializer,
|
|
sequence,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (context.SerializerProvider
|
|
.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(
|
|
out var serializer))
|
|
{
|
|
val = Read<T, ValueDataNode>(
|
|
serializer,
|
|
value,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride
|
|
);
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RegularRead();
|
|
}
|
|
|
|
var isValueType = baseType.IsValueType;
|
|
if (!nullable && !isValueType && val == null)
|
|
throw new ReadCallReturnedNullException();
|
|
|
|
return val;
|
|
|
|
void RegularRead()
|
|
{
|
|
var hasSerializer = false;
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, MappingDataNode>, T, MappingDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, MappingDataNode>(reader, mapping, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case SequenceDataNode sequence:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, SequenceDataNode>, T, SequenceDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, SequenceDataNode>(reader, sequence, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
if (_regularSerializerProvider.TryGetTypeNodeSerializer<ITypeReader<T, ValueDataNode>, T, ValueDataNode>(out var reader))
|
|
{
|
|
hasSerializer = true;
|
|
val = Read<T, ValueDataNode>(reader, value, hookCtx, context, instanceProvider, notNullableOverride);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasSerializer)
|
|
{
|
|
if (baseType.IsAssignableTo(typeof(ISelfSerialize)))
|
|
{
|
|
switch (node)
|
|
{
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
var selfSerialize = (ISelfSerialize) val;
|
|
selfSerialize.Deserialize(value.Value);
|
|
val = (T) selfSerialize;
|
|
break;
|
|
}
|
|
default:
|
|
throw new InvalidNodeTypeException($"Cannot read {nameof(ISelfSerialize)} from node type {node.GetType()}. Expected {nameof(ValueDataNode)}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (node)
|
|
{
|
|
case MappingDataNode mapping:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
T.Read(ref val, mapping, this, hookCtx, context);
|
|
break;
|
|
}
|
|
case ValueDataNode value:
|
|
{
|
|
instanceProvider ??= T.StaticInstantiate;
|
|
val = instanceProvider();
|
|
if (value.Value != string.Empty)
|
|
throw new ArgumentException($"No mapping node provided for type {baseType} at line: {node.Start.Line}");
|
|
|
|
break;
|
|
}
|
|
default:
|
|
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
|
|
}
|
|
|
|
TryRunAfterHook(val, hookCtx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public T Read<T, TNode>(
|
|
ITypeReader<T, TNode> reader,
|
|
TNode node,
|
|
ISerializationContext? context = null,
|
|
bool skipHook = false,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
where TNode : DataNode
|
|
{
|
|
return Read<T, TNode>(
|
|
reader,
|
|
node,
|
|
SerializationHookContext.ForSkipHooks(skipHook),
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride);
|
|
}
|
|
|
|
public T Read<T, TNode>(
|
|
ITypeReader<T, TNode> reader,
|
|
TNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
where TNode : DataNode
|
|
{
|
|
var val = reader.Read(this, node, DependencyCollection, hookCtx, context, instanceProvider);
|
|
if (notNullableOverride)
|
|
Debug.Assert(val != null, "Reader call returned null value! Forbidden!");
|
|
|
|
return val;
|
|
}
|
|
|
|
public T Read<T, TNode, TReader>(TNode node, ISerializationContext? context = null,
|
|
bool skipHook = false, ISerializationManager.InstantiationDelegate<T>? instanceProvider = null, bool notNullableOverride = false) where TNode : DataNode
|
|
where TReader : ITypeReader<T, TNode>
|
|
{
|
|
return Read<T, TNode, TReader>(
|
|
node,
|
|
SerializationHookContext.ForSkipHooks(skipHook),
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride);
|
|
}
|
|
|
|
public T Read<T, TNode, TReader>(
|
|
TNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
ISerializationManager.InstantiationDelegate<T>? instanceProvider = null,
|
|
bool notNullableOverride = false)
|
|
where TNode : DataNode
|
|
where TReader : ITypeReader<T, TNode>
|
|
{
|
|
return Read(
|
|
GetOrCreateCustomTypeSerializer<TReader>(),
|
|
node,
|
|
hookCtx,
|
|
context,
|
|
instanceProvider,
|
|
notNullableOverride);
|
|
}
|
|
|
|
public object? Read(Type type, DataNode node, ISerializationContext? context = null, bool skipHook = false, bool notNullableOverride = false)
|
|
{
|
|
return Read(type, node, SerializationHookContext.ForSkipHooks(skipHook), context, notNullableOverride);
|
|
}
|
|
|
|
public object? Read(
|
|
Type type,
|
|
DataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null,
|
|
bool notNullableOverride = false)
|
|
{
|
|
return GetOrCreateBoxingReadDelegate(type, notNullableOverride)(node, hookCtx, context);
|
|
}
|
|
|
|
private ReadBoxingDelegate GetOrCreateBoxingReadDelegate(Type type, bool notNullableOverride = false)
|
|
{
|
|
return _readBoxingDelegates.GetOrAdd((type, notNullableOverride), static (tuple, manager) =>
|
|
{
|
|
var type = tuple.type;
|
|
var managerConst = Expression.Constant(manager);
|
|
|
|
var nodeParam = Expression.Variable(typeof(DataNode));
|
|
var contextParam = Expression.Variable(typeof(ISerializationContext));
|
|
var hookCtxParam = Expression.Variable(typeof(SerializationHookContext));
|
|
|
|
var call = Expression.Convert(Expression.Call(
|
|
managerConst,
|
|
nameof(Read),
|
|
new[] { type },
|
|
nodeParam,
|
|
hookCtxParam,
|
|
contextParam,
|
|
Expression.Constant(null, typeof(ISerializationManager.InstantiationDelegate<>).MakeGenericType(type)),
|
|
Expression.Constant(tuple.notNullableOverride)), typeof(object));
|
|
|
|
return Expression.Lambda<ReadBoxingDelegate>(
|
|
call,
|
|
nodeParam,
|
|
hookCtxParam,
|
|
contextParam).Compile();
|
|
}, this);
|
|
}
|
|
|
|
private static object ReadDelegateValueFactory(Type baseType, Type actualType, Type nodeType, bool notNullableOverride, SerializationManager manager)
|
|
{
|
|
var nullable = actualType.IsNullable();
|
|
|
|
var managerConst = Expression.Constant(manager);
|
|
|
|
var nodeParam = Expression.Parameter(typeof(DataNode), "node");
|
|
var contextParam = Expression.Parameter(typeof(ISerializationContext), "context");
|
|
var hookCtxParam = Expression.Parameter(typeof(SerializationHookContext), "hookCtx");
|
|
var instantiatorParam = Expression.Parameter(typeof(ISerializationManager.InstantiationDelegate<>).MakeGenericType(baseType), "instanceProvider");
|
|
|
|
actualType = actualType.EnsureNotNullableType();
|
|
|
|
Expression BaseInstantiatorToActual()
|
|
{
|
|
Expression nonNullableInstantiator = baseType.IsNullable() && baseType.IsValueType
|
|
? Expression.Call(
|
|
managerConst,
|
|
nameof(UnwrapInstantiationDelegate),
|
|
new[] { baseType.EnsureNotNullableType() },
|
|
instantiatorParam)
|
|
: instantiatorParam;
|
|
|
|
return baseType.EnsureNotNullableType() == actualType
|
|
? nonNullableInstantiator
|
|
: Expression.Call(managerConst,
|
|
nameof(WrapBaseInstantiationDelegate),
|
|
new []{actualType, baseType.EnsureNotNullableType()},
|
|
instantiatorParam);
|
|
}
|
|
|
|
var instantiatorVariable =
|
|
Expression.Variable(typeof(ISerializationManager.InstantiationDelegate<>).MakeGenericType(actualType));
|
|
|
|
var instantiatorCoalesce = Expression.Assign(instantiatorVariable, Expression.Coalesce(
|
|
BaseInstantiatorToActual(),
|
|
Expression.Call(
|
|
managerConst,
|
|
nameof(GetOrCreateInstantiator),
|
|
new[] { actualType },
|
|
Expression.Constant(false),
|
|
Expression.Constant(null, typeof(Type)))));
|
|
|
|
Expression call;
|
|
if (manager._regularSerializerProvider.TryGetTypeNodeSerializer(typeof(ITypeReader<,>), actualType, nodeType, out var reader))
|
|
{
|
|
var readerType = typeof(ITypeReader<,>).MakeGenericType(actualType, nodeType);
|
|
var readerConst = Expression.Constant(reader, readerType);
|
|
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(Read),
|
|
new[] { actualType, nodeType },
|
|
readerConst,
|
|
Expression.Convert(nodeParam, nodeType),
|
|
hookCtxParam,
|
|
contextParam,
|
|
BaseInstantiatorToActual(),
|
|
Expression.Constant(notNullableOverride));
|
|
}
|
|
else if (actualType.IsArray)
|
|
{
|
|
var elementType = actualType.GetElementType()!;
|
|
|
|
if (nodeType == typeof(ValueDataNode))
|
|
{
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(ReadArrayValue),
|
|
new[] { elementType },
|
|
Expression.Convert(nodeParam, typeof(ValueDataNode)),
|
|
hookCtxParam,
|
|
contextParam);
|
|
}
|
|
else if (nodeType == typeof(SequenceDataNode))
|
|
{
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(ReadArraySequence),
|
|
new[] { elementType },
|
|
Expression.Convert(nodeParam, typeof(SequenceDataNode)),
|
|
hookCtxParam,
|
|
contextParam);
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException($"Cannot read array from data node type {nodeType}");
|
|
}
|
|
}
|
|
else if (actualType.IsEnum)
|
|
{
|
|
// Does not include cases where the target type is System.Enum.
|
|
// Those get handled by the generic enum serializer which uses reflection to resolve strings into enums.
|
|
DebugTools.Assert(actualType != typeof(Enum));
|
|
|
|
if (nodeType == typeof(ValueDataNode))
|
|
{
|
|
call = Expression.Call(managerConst, nameof(ReadEnumValue), new[] { actualType },
|
|
Expression.Convert(nodeParam, typeof(ValueDataNode)));
|
|
}
|
|
else if (nodeType == typeof(SequenceDataNode))
|
|
{
|
|
call = Expression.Call(managerConst, nameof(ReadEnumSequence), new[] { actualType },
|
|
Expression.Convert(nodeParam, typeof(SequenceDataNode)));
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidNodeTypeException($"Cannot serialize node as {actualType}, unsupported node type {nodeType}");
|
|
}
|
|
}
|
|
else if (actualType.IsAssignableTo(typeof(ISelfSerialize)))
|
|
{
|
|
if (nodeType != typeof(ValueDataNode))
|
|
{
|
|
throw new InvalidNodeTypeException($"Cannot read {nameof(ISelfSerialize)} from node type {nodeType}. Expected {nameof(ValueDataNode)}");
|
|
}
|
|
|
|
call = Expression.Block(
|
|
new [] {instantiatorVariable},
|
|
instantiatorCoalesce,
|
|
Expression.Call(
|
|
managerConst,
|
|
nameof(ReadSelfSerialize),
|
|
new[] { actualType },
|
|
instantiatorVariable,
|
|
Expression.Convert(nodeParam, typeof(ValueDataNode))));
|
|
}
|
|
else
|
|
{
|
|
if (nodeType == typeof(ValueDataNode))
|
|
{
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(ReadGenericValue),
|
|
new[] { actualType },
|
|
Expression.Convert(nodeParam, typeof(ValueDataNode)),
|
|
hookCtxParam,
|
|
instantiatorVariable);
|
|
}
|
|
else if (nodeType == typeof(MappingDataNode))
|
|
{
|
|
if (actualType.IsAssignableTo(typeof(ISerializationGenerated<>).MakeGenericType(actualType)))
|
|
{
|
|
var definition = manager.GetDefinition(actualType);
|
|
var definitionConst = Expression.Constant(definition, typeof(DataDefinition<>).MakeGenericType(actualType));
|
|
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(ReadGenericMapping),
|
|
new[] { actualType },
|
|
Expression.Convert(nodeParam, typeof(MappingDataNode)),
|
|
definitionConst,
|
|
hookCtxParam,
|
|
contextParam,
|
|
instantiatorVariable);
|
|
}
|
|
else
|
|
{
|
|
call = Expression.Call(
|
|
managerConst,
|
|
nameof(ReadNoSerializer),
|
|
new[] { actualType },
|
|
nodeParam);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException($"No mapping or value node provided for type {actualType}.");
|
|
}
|
|
|
|
call = Expression.Block(
|
|
new[]{instantiatorVariable},
|
|
instantiatorCoalesce,
|
|
call);
|
|
}
|
|
|
|
// check for customtypeserializer
|
|
var serializerType = typeof(ITypeReader<,>).MakeGenericType(actualType, nodeType);
|
|
var serializerVar = Expression.Variable(serializerType);
|
|
call = Expression.Block(new[] { serializerVar },
|
|
Expression.Condition(
|
|
Expression.AndAlso(
|
|
Expression.ReferenceNotEqual(contextParam,
|
|
Expression.Constant(null, typeof(ISerializationContext))),
|
|
Expression.Call(Expression.Property(contextParam, "SerializerProvider"),
|
|
"TryGetTypeNodeSerializer", new[] { serializerType, actualType, nodeType }, serializerVar)),
|
|
Expression.Call(
|
|
managerConst,
|
|
nameof(Read),
|
|
new []{actualType, nodeType},
|
|
serializerVar,
|
|
Expression.Convert(nodeParam, nodeType),
|
|
hookCtxParam,
|
|
contextParam,
|
|
BaseInstantiatorToActual(),
|
|
Expression.Constant(notNullableOverride)),
|
|
call));
|
|
|
|
//wrap our valuetype in nullable<T> if we are nullable so we can assign it to returnValue
|
|
call = WrapNullableIfNeededExpression(call, nullable);
|
|
|
|
// early-out null before anything
|
|
var returnValue = Expression.Variable(nullable ? actualType.EnsureNullableType() : actualType);
|
|
call = Expression.Block(new[] { returnValue },
|
|
Expression.IfThenElse(
|
|
Expression.Call(typeof(SerializationManager), nameof(IsNull), Type.EmptyTypes, nodeParam),
|
|
nullable && !notNullableOverride
|
|
? Expression.Block(typeof(void),
|
|
Expression.Assign(returnValue, GetNullExpression(managerConst, actualType)))
|
|
: actualType == typeof(EntityUid) //todo paul make this not hardcoded
|
|
? Expression.Assign(returnValue, Expression.Constant(EntityUid.Invalid))
|
|
: ExpressionUtils.ThrowExpression<NullNotAllowedException>(),
|
|
Expression.Block(typeof(void),
|
|
Expression.Assign(returnValue, call))),
|
|
returnValue);
|
|
|
|
if (!nullable && !actualType.IsValueType)
|
|
{
|
|
// check that value isn't null
|
|
var finalValue = Expression.Variable(baseType);
|
|
call = Expression.Block(new[] { finalValue },
|
|
Expression.Assign(finalValue, call),
|
|
Expression.IfThen(Expression.Equal(finalValue, GetNullExpression(managerConst, actualType)),
|
|
ExpressionUtils.ThrowExpression<ReadCallReturnedNullException>()),
|
|
finalValue);
|
|
}
|
|
|
|
return Expression.Lambda(
|
|
typeof(ReadGenericDelegate<>).MakeGenericType(baseType),
|
|
call,
|
|
nodeParam,
|
|
hookCtxParam,
|
|
contextParam,
|
|
instantiatorParam)
|
|
.Compile();
|
|
}
|
|
|
|
private ISerializationManager.InstantiationDelegate<T>? UnwrapInstantiationDelegate<T>(
|
|
ISerializationManager.InstantiationDelegate<T?>? instantiationDelegate) where T : struct
|
|
{
|
|
if (instantiationDelegate == null) return null;
|
|
|
|
return () =>
|
|
{
|
|
var val = instantiationDelegate();
|
|
Debug.Assert(val.HasValue, $"{nameof(instantiationDelegate)} returned null value! This should NEVER be allowed to happen!");
|
|
return instantiationDelegate()!.Value;
|
|
};
|
|
}
|
|
|
|
private ISerializationManager.InstantiationDelegate<TActual>? WrapBaseInstantiationDelegate<TActual, TBase>(
|
|
ISerializationManager.InstantiationDelegate<TBase>? instantiationDelegate) where TActual : TBase
|
|
{
|
|
if (instantiationDelegate == null) return null;
|
|
|
|
return () =>
|
|
{
|
|
var val = instantiationDelegate();
|
|
Debug.Assert(val != null, $"{nameof(instantiationDelegate)} returned null value! This should NEVER be allowed to happen!");
|
|
return (TActual)val;
|
|
};
|
|
}
|
|
|
|
|
|
private T[] ReadArrayValue<T>(
|
|
ValueDataNode value,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null)
|
|
{
|
|
var array = new T[1];
|
|
array[0] = Read<T>(value, hookCtx, context);
|
|
return array;
|
|
}
|
|
|
|
private T[] ReadArraySequence<T>(
|
|
SequenceDataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context = null)
|
|
{
|
|
var array = new T[node.Sequence.Count];
|
|
|
|
for (var i = 0; i < node.Sequence.Count; i++)
|
|
{
|
|
array[i] = Read<T>(node.Sequence[i], hookCtx, context);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
private TEnum ReadEnumValue<TEnum>(ValueDataNode node) where TEnum : struct
|
|
{
|
|
return Enum.Parse<TEnum>(node.Value, true);
|
|
}
|
|
|
|
private TEnum ReadEnumSequence<TEnum>(SequenceDataNode node) where TEnum : struct
|
|
{
|
|
return Enum.Parse<TEnum>(string.Join(", ", node.Sequence), true);
|
|
}
|
|
|
|
private TValue ReadSelfSerialize<TValue>(
|
|
ISerializationManager.InstantiationDelegate<TValue> instanceProvider, ValueDataNode node) where TValue : ISelfSerialize
|
|
{
|
|
var val = instanceProvider();
|
|
val.Deserialize(node.Value);
|
|
return val;
|
|
}
|
|
|
|
private TValue ReadGenericValue<TValue>(
|
|
ValueDataNode node,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationManager.InstantiationDelegate<TValue> instanceProvider)
|
|
where TValue : notnull
|
|
{
|
|
var type = typeof(TValue);
|
|
var instance = instanceProvider();
|
|
|
|
if (node.Value != string.Empty)
|
|
{
|
|
throw new ArgumentException($"No mapping node provided for type {type} at line: {node.Start.Line}");
|
|
}
|
|
|
|
TryRunAfterHook(instance, hookCtx);
|
|
|
|
return instance;
|
|
}
|
|
|
|
private TValue ReadGenericMapping<TValue>(
|
|
MappingDataNode node,
|
|
DataDefinition<TValue>? definition,
|
|
SerializationHookContext hookCtx,
|
|
ISerializationContext? context,
|
|
ISerializationManager.InstantiationDelegate<TValue> instanceProvider)
|
|
where TValue : ISerializationGenerated<TValue>
|
|
{
|
|
if (definition == null)
|
|
throw new ArgumentException($"No data definition found for type {typeof(TValue)} with node type {node.GetType()} when reading");
|
|
|
|
var instance = instanceProvider();
|
|
|
|
definition.Populate(ref instance, node, this, hookCtx, context);
|
|
|
|
TryRunAfterHook(instance, hookCtx);
|
|
|
|
return instance;
|
|
}
|
|
|
|
private TValue ReadNoSerializer<TValue>(DataNode node)
|
|
{
|
|
throw new ArgumentException($"No type serializer or data definition found for type {typeof(TValue)} with node type {node.GetType()} when reading");
|
|
}
|
|
|
|
private static class ReadTypeMetadata<T>
|
|
{
|
|
// ReSharper disable once StaticMemberInGenericType
|
|
public static bool IsArray;
|
|
|
|
static ReadTypeMetadata()
|
|
{
|
|
IsArray = typeof(T).IsArray;
|
|
}
|
|
}
|
|
}
|
|
}
|