diff --git a/IPA.Loader/Config/Stores/GeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStore.cs index cbc87a24..29f615b0 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -237,7 +237,7 @@ namespace IPA.Config.Stores public bool AllowNull; public bool IsVirtual; public bool IsField; - public bool IsNullable; + public bool IsNullable; // signifies whether this is a Nullable // invalid for objects with IsNullabe false public Type NullableWrappedType => Nullable.GetUnderlyingType(Type); @@ -245,6 +245,8 @@ namespace IPA.Config.Stores public PropertyInfo Nullable_HasValue => Type.GetProperty(nameof(Nullable.HasValue)); // invalid for objects with IsNullabe false public PropertyInfo Nullable_Value => Type.GetProperty(nameof(Nullable.Value)); + // invalid for objects with IsNullabe false + public ConstructorInfo Nullable_Construct => Type.GetConstructor(new[] { NullableWrappedType }); } private static Func MakeCreator(Type type) @@ -853,11 +855,12 @@ namespace IPA.Config.Stores if (member.AllowNull) { var passedNull = il.DefineLabel(); + il.Emit(OpCodes.Dup); + if (member.IsNullable) + il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod()); il.Emit(OpCodes.Brtrue, passedNull); - // TODO: add special check for nullables - il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Br, endSerialize); @@ -865,6 +868,9 @@ namespace IPA.Config.Stores il.MarkLabel(passedNull); } + if (member.IsNullable) + il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); + var targetType = GetExpectedValueTypeForType(member.Type); if (targetType == typeof(Text)) { // only happens when arg is a string or char @@ -954,6 +960,12 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); } + private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, Func GetLocal) + { + EmitDeserializeValue(il, member.NullableWrappedType, expected, GetLocal); + il.Emit(OpCodes.Newobj, member.Nullable_Construct); + } + // 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, Type targetType, Type expected, Func GetLocal) @@ -1006,7 +1018,7 @@ namespace IPA.Config.Stores var implLabel = il.DefineLabel(); var passedTypeCheck = il.DefineLabel(); - var expectType = GetExpectedValueTypeForType(member.Type); + var expectType = GetExpectedValueTypeForType(member.IsNullable ? member.NullableWrappedType : member.Type); void EmitStore(Action value) { @@ -1028,8 +1040,6 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brtrue_S, implLabel); // null check - // TODO: support Nullable - if (!member.AllowNull) { il.Emit(OpCodes.Pop); @@ -1037,6 +1047,15 @@ namespace IPA.Config.Stores expected: il => EmitTypeof(il, expectType)); il.Emit(OpCodes.Br, nextLabel); } + else if (member.IsNullable) + { + il.Emit(OpCodes.Pop); + var valTLocal = GetLocal(member.Type, 0); + il.Emit(OpCodes.Ldloca, valTLocal); + il.Emit(OpCodes.Initobj, member.Type); + EmitStore(il => il.Emit(OpCodes.Ldloc, valTLocal)); + il.Emit(OpCodes.Br, nextLabel); + } else { il.Emit(OpCodes.Pop); @@ -1095,7 +1114,8 @@ namespace IPA.Config.Stores il.MarkLabel(passedTypeCheck); var local = GetLocal(member.Type, 0); - EmitDeserializeValue(il, member.Type, expectType, GetLocal); + if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal); + else EmitDeserializeValue(il, member.Type, expectType, GetLocal); il.Emit(OpCodes.Stloc, local); EmitStore(il => il.Emit(OpCodes.Ldloc, local)); } diff --git a/Refs/MainAssembly.dll b/Refs/MainAssembly.dll index 9c9dec71..ede90612 100644 Binary files a/Refs/MainAssembly.dll and b/Refs/MainAssembly.dll differ diff --git a/Refs/Unity.TextMeshPro.dll b/Refs/Unity.TextMeshPro.dll index 8214d4da..c531b466 100644 Binary files a/Refs/Unity.TextMeshPro.dll and b/Refs/Unity.TextMeshPro.dll differ diff --git a/Refs/UnityEngine.CoreModule.Net4.dll b/Refs/UnityEngine.CoreModule.Net4.dll index f6df15fd..6362cdf1 100644 Binary files a/Refs/UnityEngine.CoreModule.Net4.dll and b/Refs/UnityEngine.CoreModule.Net4.dll differ