Browse Source

Hopefully latest wave of serialization fixes

pull/100/head
Meivyn 1 year ago
parent
commit
5cd7b83077
No known key found for this signature in database GPG Key ID: 8BDD3E48158B2F71
4 changed files with 62 additions and 46 deletions
  1. +18
    -12
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs
  2. +7
    -8
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs
  3. +2
    -2
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs
  4. +35
    -24
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs

+ 18
- 12
IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs View File

@ -1,17 +1,8 @@
#nullable enable
using IPA.Config.Data;
using IPA.Config.Stores.Attributes;
using IPA.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
#if NET3
using Net3_Proxy;
using Array = Net3_Proxy.Array;
@ -46,12 +37,27 @@ namespace IPA.Config.Stores
var endLabel = il.DefineLabel();
// TODO: when we have a nullable, we need to save to a local to call methods
if (member.IsNullable)
{
il.Emit(OpCodes.Dup);
using var valueTypeLocal = GetLocal.Allocate(member.Type);
var passedNull = il.DefineLabel();
il.Emit(OpCodes.Stloc, valueTypeLocal);
il.Emit(OpCodes.Ldloca, valueTypeLocal);
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brfalse, endLabel);
il.Emit(OpCodes.Brtrue, passedNull);
if (member.ConversionType.IsValueType)
{
il.Emit(OpCodes.Ldloca, valueTypeLocal);
il.Emit(OpCodes.Initobj, member.Type);
}
il.Emit(OpCodes.Ldloc, valueTypeLocal);
il.Emit(OpCodes.Br, endLabel);
il.MarkLabel(passedNull);
il.Emit(OpCodes.Ldloca, valueTypeLocal);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
}


+ 7
- 8
IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs View File

@ -41,7 +41,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
}
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, LocalAllocator GetLocal,
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, LocalAllocator GetLocal,
Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{
if (!member.IsNullable) throw new InvalidOperationException("EmitDeserializeNullable called for non-nullable!");
@ -53,7 +53,7 @@ namespace IPA.Config.Stores
// top of stack is the Value to deserialize; the type will be as returned from GetExpectedValueTypeForType
// after, top of stack will be thing to write to field
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal,
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal,
Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{
if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
@ -96,14 +96,14 @@ namespace IPA.Config.Stores
{
using var mapLocal = GetLocal.Allocate(typeof(Map));
using var resultLocal = GetLocal.Allocate(targetType);
using var resultLocal = GetLocal.Allocate(member.IsNullable ? member.Type : targetType);
using var valueLocal = GetLocal.Allocate(typeof(Value));
var structure = ReadObjectMembers(targetType);
if (!structure.Any())
{
Logger.Config.Warn($"Custom value type {targetType.FullName} (when compiling serialization of" +
$" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
Logger.Config.Warn($"Custom value type {targetType.FullName} (when compiling deserialization of" +
$" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members, might need a converter");
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldloca, resultLocal);
il.Emit(OpCodes.Initobj, targetType);
@ -112,7 +112,6 @@ namespace IPA.Config.Stores
{
il.Emit(OpCodes.Stloc, mapLocal);
// TODO: handle creating a nullable, when applicable
EmitLoad(il, member, thisarg);
il.Emit(OpCodes.Stloc, resultLocal);
@ -130,7 +129,7 @@ namespace IPA.Config.Stores
}
}
private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure,
private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure,
LocalBuilder mapLocal, LocalBuilder valueLocal,
LocalAllocator GetLocal, Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{
@ -204,7 +203,7 @@ namespace IPA.Config.Stores
}
// emit takes the value being deserialized, logs on error, leaves nothing on stack
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, LocalAllocator GetLocal,
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, LocalAllocator GetLocal,
Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{
var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));


+ 2
- 2
IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs View File

@ -78,7 +78,7 @@ namespace IPA.Config.Stores
if (converterAttr.UseDefaultConverterForType)
converterAttr = new UseConverterAttribute(Converter.GetDefaultConverterType(member.Type));
if (converterAttr.UseDefaultConverterForType)
throw new InvalidOperationException("How did we get here?");
throw new InvalidOperationException("How did we get here?");
member.Converter = converterAttr.ConverterType;
member.IsGenericConverter = converterAttr.IsGenericConverter;
@ -175,7 +175,7 @@ namespace IPA.Config.Stores
// only look at public/protected fields
foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
if (field.IsPrivate)
if (field.IsPrivate || field.IsAssembly)
continue;
var smi = new SerializedMemberInfo


+ 35
- 24
IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs View File

@ -28,47 +28,58 @@ namespace IPA.Config.Stores
? GetLocal.Allocate(member.Type)
: default;
if (member.IsNullable)
{
il.Emit(OpCodes.Stloc, valueTypeLocal.Local);
il.Emit(OpCodes.Ldloca, valueTypeLocal.Local);
}
var endSerialize = il.DefineLabel();
if (member.AllowNull)
{
var passedNull = il.DefineLabel();
il.Emit(OpCodes.Dup);
if (member.IsNullable)
{
il.Emit(OpCodes.Stloc, valueTypeLocal.Local);
il.Emit(OpCodes.Ldloca, valueTypeLocal.Local);
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
}
il.Emit(OpCodes.Brtrue, passedNull);
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Br, endSerialize);
il.MarkLabel(passedNull);
}
if (member is { IsNullable: true, HasConverter: false })
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
if (member.IsNullable)
{
if (!member.HasConverter)
{
il.Emit(OpCodes.Ldloca, valueTypeLocal.Local);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
}
}
else
{
EmitLoad(il, member, thisarg);
}
}
var memberConversionType = member.ConversionType;
var targetType = GetExpectedValueTypeForType(memberConversionType);
if (member.HasConverter)
{
using var stlocal = GetLocal.Allocate(member.IsNullable ? member.Type : memberConversionType);
using var valLocal = GetLocal.Allocate(typeof(Value));
if (member.IsNullable)
il.Emit(OpCodes.Ldloc_S, valueTypeLocal.Local);
{
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldsfld, member.ConverterField);
il.Emit(OpCodes.Ldloc, valueTypeLocal.Local);
}
else
{
using var stlocal = GetLocal.Allocate(memberConversionType);
il.Emit(OpCodes.Stloc, stlocal);
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldsfld, member.ConverterField);
il.Emit(OpCodes.Ldloc, stlocal);
il.Emit(OpCodes.Stloc, stlocal);
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldsfld, member.ConverterField);
il.Emit(OpCodes.Ldloc, stlocal);
}
if (member.IsGenericConverter)
{
@ -90,18 +101,18 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Call, toValue);
}
il.Emit(OpCodes.Stloc, valLocal);
il.Emit(OpCodes.Stloc_1);
il.BeginCatchBlock(typeof(Exception));
EmitWarnException(il, "Error serializing member using converter");
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Stloc, valLocal);
il.Emit(OpCodes.Stloc_1);
il.EndExceptionBlock();
il.Emit(OpCodes.Ldloc, valLocal);
il.Emit(OpCodes.Ldloc_1);
}
else if (targetType == typeof(Text))
{ // only happens when arg is a string or char
var TextCreate = typeof(Value).GetMethod(nameof(Value.Text));
if (member.Type == typeof(char))
if (memberConversionType == typeof(char))
{
var strFromChar = typeof(char).GetMethod(nameof(char.ToString), new[] { typeof(char) });
il.Emit(OpCodes.Call, strFromChar);
@ -172,7 +183,7 @@ namespace IPA.Config.Stores
if (!structure.Any())
{
Logger.Config.Warn($"Custom value type {memberConversionType.FullName} (when compiling serialization of" +
$" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members");
$" {member.Name} on {member.Member.DeclaringType.FullName}) has no accessible members, might need a converter");
il.Emit(OpCodes.Pop);
}
else


Loading…
Cancel
Save