Browse Source

Added support for Nullable<T> in the generated config

4.0.0-beta
Anairkoen Schno 5 years ago
parent
commit
5e03407676
4 changed files with 27 additions and 7 deletions
  1. +27
    -7
      IPA.Loader/Config/Stores/GeneratedStore.cs
  2. BIN
      Refs/MainAssembly.dll
  3. BIN
      Refs/Unity.TextMeshPro.dll
  4. BIN
      Refs/UnityEngine.CoreModule.Net4.dll

+ 27
- 7
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -237,7 +237,7 @@ namespace IPA.Config.Stores
public bool AllowNull; public bool AllowNull;
public bool IsVirtual; public bool IsVirtual;
public bool IsField; public bool IsField;
public bool IsNullable;
public bool IsNullable; // signifies whether this is a Nullable<T>
// invalid for objects with IsNullabe false // invalid for objects with IsNullabe false
public Type NullableWrappedType => Nullable.GetUnderlyingType(Type); public Type NullableWrappedType => Nullable.GetUnderlyingType(Type);
@ -245,6 +245,8 @@ namespace IPA.Config.Stores
public PropertyInfo Nullable_HasValue => Type.GetProperty(nameof(Nullable<int>.HasValue)); public PropertyInfo Nullable_HasValue => Type.GetProperty(nameof(Nullable<int>.HasValue));
// invalid for objects with IsNullabe false // invalid for objects with IsNullabe false
public PropertyInfo Nullable_Value => Type.GetProperty(nameof(Nullable<int>.Value)); public PropertyInfo Nullable_Value => Type.GetProperty(nameof(Nullable<int>.Value));
// invalid for objects with IsNullabe false
public ConstructorInfo Nullable_Construct => Type.GetConstructor(new[] { NullableWrappedType });
} }
private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type) private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
@ -853,11 +855,12 @@ namespace IPA.Config.Stores
if (member.AllowNull) if (member.AllowNull)
{ {
var passedNull = il.DefineLabel(); var passedNull = il.DefineLabel();
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
if (member.IsNullable)
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brtrue, passedNull); il.Emit(OpCodes.Brtrue, passedNull);
// TODO: add special check for nullables
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Br, endSerialize); il.Emit(OpCodes.Br, endSerialize);
@ -865,6 +868,9 @@ namespace IPA.Config.Stores
il.MarkLabel(passedNull); il.MarkLabel(passedNull);
} }
if (member.IsNullable)
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
var targetType = GetExpectedValueTypeForType(member.Type); var targetType = GetExpectedValueTypeForType(member.Type);
if (targetType == typeof(Text)) if (targetType == typeof(Text))
{ // only happens when arg is a string or char { // only happens when arg is a string or char
@ -954,6 +960,12 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
} }
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, Func<Type, int, LocalBuilder> 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 // 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 // after, top of stack will be thing to write to field
private static void EmitDeserializeValue(ILGenerator il, Type targetType, Type expected, Func<Type, int, LocalBuilder> GetLocal) private static void EmitDeserializeValue(ILGenerator il, Type targetType, Type expected, Func<Type, int, LocalBuilder> GetLocal)
@ -1006,7 +1018,7 @@ namespace IPA.Config.Stores
var implLabel = il.DefineLabel(); var implLabel = il.DefineLabel();
var passedTypeCheck = il.DefineLabel(); var passedTypeCheck = il.DefineLabel();
var expectType = GetExpectedValueTypeForType(member.Type);
var expectType = GetExpectedValueTypeForType(member.IsNullable ? member.NullableWrappedType : member.Type);
void EmitStore(Action<ILGenerator> value) void EmitStore(Action<ILGenerator> value)
{ {
@ -1028,8 +1040,6 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue_S, implLabel); // null check il.Emit(OpCodes.Brtrue_S, implLabel); // null check
// TODO: support Nullable<T>
if (!member.AllowNull) if (!member.AllowNull)
{ {
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
@ -1037,6 +1047,15 @@ namespace IPA.Config.Stores
expected: il => EmitTypeof(il, expectType)); expected: il => EmitTypeof(il, expectType));
il.Emit(OpCodes.Br, nextLabel); 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 else
{ {
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
@ -1095,7 +1114,8 @@ namespace IPA.Config.Stores
il.MarkLabel(passedTypeCheck); il.MarkLabel(passedTypeCheck);
var local = GetLocal(member.Type, 0); 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); il.Emit(OpCodes.Stloc, local);
EmitStore(il => il.Emit(OpCodes.Ldloc, local)); EmitStore(il => il.Emit(OpCodes.Ldloc, local));
} }


BIN
Refs/MainAssembly.dll View File


BIN
Refs/Unity.TextMeshPro.dll View File


BIN
Refs/UnityEngine.CoreModule.Net4.dll View File


Loading…
Cancel
Save