Browse Source

Fixed JsonConfigProvider's formatting

Added framework for object serialization in GeneratedStore
pull/46/head
Anairkoen Schno 4 years ago
parent
commit
2e591278c3
2 changed files with 127 additions and 21 deletions
  1. +2
    -1
      IPA.Loader/Config/Providers/JsonConfigProvider.cs
  2. +125
    -20
      IPA.Loader/Config/Stores/GeneratedStore.cs

+ 2
- 1
IPA.Loader/Config/Providers/JsonConfigProvider.cs View File

@ -125,8 +125,9 @@ namespace IPA.Config.Providers
{ {
var tok = VisitToToken(value); var tok = VisitToToken(value);
using var swriter = new StreamWriter(File.OpenWrite());
using var swriter = new StreamWriter(File.Open(FileMode.Create, FileAccess.Write));
using var jwriter = new JsonTextWriter(swriter); using var jwriter = new JsonTextWriter(swriter);
jwriter.Formatting = Formatting.Indented;
tok.WriteTo(jwriter); tok.WriteTo(jwriter);
} }
catch (Exception e) catch (Exception e)


+ 125
- 20
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -115,17 +115,19 @@ namespace IPA.Config.Stores
internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom)); internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
public void ReadFrom(IConfigProvider provider) public void ReadFrom(IConfigProvider provider)
{ {
// TODO: implement
var values = provider.Load();
Logger.config.Debug("Generated impl ReadFrom"); Logger.config.Debug("Generated impl ReadFrom");
Logger.config.Debug($"Read {provider.Load()}");
Logger.config.Debug($"Read {values}");
generated.Values = values;
} }
internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo)); internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
public void WriteTo(IConfigProvider provider) public void WriteTo(IConfigProvider provider)
{ {
var values = generated.Values; var values = generated.Values;
// TODO: implement
Logger.config.Debug("Generated impl WriteTo"); Logger.config.Debug("Generated impl WriteTo");
Logger.config.Debug($"Serialized {values}");
provider.Store(values);
} }
} }
@ -187,6 +189,7 @@ namespace IPA.Config.Stores
public string Name; public string Name;
public MemberInfo Member; public MemberInfo Member;
public bool IsVirtual; public bool IsVirtual;
public bool IsField;
public Type Type; public Type Type;
} }
@ -203,17 +206,16 @@ namespace IPA.Config.Stores
var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly); var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly); var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
var GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
// TODO: possibly move all of this manual IL over to Linq.Expressions
// none of this can be Expressions because CompileToMethod requires a static target method for some dumbass reason
#region Parse base object structure #region Parse base object structure
var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>()); var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing
var structure = new Dictionary<string, SerializedMemberInfo>();
var structure = new List<SerializedMemberInfo>();
// TODO: incorporate attributes
// TODO: incorporate attributes/base types
// only looks at public properties // only looks at public properties
foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
@ -224,10 +226,11 @@ namespace IPA.Config.Stores
Member = prop, Member = prop,
IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) || IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) ||
(prop.GetSetMethod(true)?.IsVirtual ?? false), (prop.GetSetMethod(true)?.IsVirtual ?? false),
IsField = false,
Type = prop.PropertyType Type = prop.PropertyType
}; };
structure.Add(smi.Name, smi);
structure.Add(smi);
} }
// only look at public fields // only look at public fields
@ -238,17 +241,17 @@ namespace IPA.Config.Stores
Name = field.Name, Name = field.Name,
Member = field, Member = field,
IsVirtual = false, IsVirtual = false,
IsField = true,
Type = field.FieldType Type = field.FieldType
}; };
structure.Add(smi.Name, smi);
structure.Add(smi);
} }
#endregion #endregion
#region Constructor #region Constructor
// takes its parent
var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) }); var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
{
{ // because this is a constructor, it has to be raw IL
var il = ctor.GetILGenerator(); var il = ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
@ -261,8 +264,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stfld, parentField); il.Emit(OpCodes.Stfld, parentField);
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldtoken, type);
il.Emit(OpCodes.Call, GetTypeFromHandle); // effectively typeof(type)
EmitTypeof(il, type);
il.Emit(OpCodes.Stfld, typeField); il.Emit(OpCodes.Stfld, typeField);
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
@ -270,12 +272,13 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Newobj, Impl.Ctor); il.Emit(OpCodes.Newobj, Impl.Ctor);
il.Emit(OpCodes.Stfld, implField); il.Emit(OpCodes.Stfld, implField);
foreach (var kvp in structure)
EmitMemberFix(il, kvp.Value);
foreach (var member in structure)
EmitMemberFix(il, member);
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion #endregion
@ -347,8 +350,20 @@ namespace IPA.Config.Stores
{ // this is non-locking because the only code that will call this will already own the correct lock { // this is non-locking because the only code that will call this will already own the correct lock
var il = valuesPropGet.GetILGenerator(); var il = valuesPropGet.GetILGenerator();
// TODO: implement get_Values
il.Emit(OpCodes.Ldnull);
var Map_Add = typeof(Map).GetMethod(nameof(Map.Add));
il.Emit(OpCodes.Call, typeof(Value).GetMethod(nameof(Value.Map)));
// the map is now at the top of the stack
foreach (var member in structure)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldstr, member.Name); // TODO: make this behave with annotations
EmitSerializeMember(il, member);
il.Emit(OpCodes.Call, Map_Add);
}
// the map is still at the top of the stack, return it
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
@ -356,8 +371,70 @@ namespace IPA.Config.Stores
{ // this is non-locking because the only code that will call this will already own the correct lock { // this is non-locking because the only code that will call this will already own the correct lock
var il = valuesPropSet.GetILGenerator(); var il = valuesPropSet.GetILGenerator();
// TODO: implement set_Values
var Map_t = typeof(Map);
var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue));
var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
var valueLocal = il.DeclareLocal(typeof(Value));
var nonNull = il.DefineLabel();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Brtrue, nonNull);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldstr, $"Attempting to deserialize null");
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, LogErrorMethod);
il.Emit(OpCodes.Ret);
il.MarkLabel(nonNull);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Isinst, Map_t);
il.Emit(OpCodes.Dup); // duplicate cloned value
var notMapError = il.DefineLabel();
il.Emit(OpCodes.Brtrue, notMapError);
// handle error
il.Emit(OpCodes.Pop); // removes the duplicate value
EmitTypeof(il, Map_t);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, Object_GetType);
il.Emit(OpCodes.Ldstr, $"Invalid root for deserializing {type.FullName}");
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, LogErrorMethod);
il.Emit(OpCodes.Ret);
var nextLabel = notMapError;
// head of stack is Map instance
foreach (var member in structure)
{
il.MarkLabel(nextLabel);
nextLabel = il.DefineLabel();
var endErrorLabel = il.DefineLabel();
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Ldloca_S, valueLocal);
il.Emit(OpCodes.Call, Map_TryGetValue);
il.Emit(OpCodes.Brtrue_S, endErrorLabel);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ldstr, $"Missing key {member.Name}");
il.Emit(OpCodes.Call, LogErrorMethod);
il.Emit(OpCodes.Br, nextLabel);
il.MarkLabel(endErrorLabel);
il.Emit(OpCodes.Ldloc_S, valueLocal);
EmitDeserializeMember(il, member, nextLabel);
}
il.MarkLabel(nextLabel);
il.Emit(OpCodes.Pop); // removes the duplicate value
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion #endregion
@ -485,18 +562,46 @@ namespace IPA.Config.Stores
{ // register a member map { // register a member map
var dict = new Dictionary<string, Type>(); var dict = new Dictionary<string, Type>();
foreach (var kvp in structure)
dict.Add(kvp.Key, kvp.Value.Type);
foreach (var member in structure)
dict.Add(member.Name, member.Type);
memberMaps.Add(type, dict); memberMaps.Add(type, dict);
} }
return creatorDel; return creatorDel;
} }
private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStore).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
internal static void LogError(Type expected, Type found, string message)
{
Logger.config.Notice($"{message}{(expected == null ? "" : $" (expected {expected}{(found == null ? "" : $", found {found}")})")}");
}
// expects the this param to be on the stack // expects the this param to be on the stack
private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member) private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member)
{ {
// TODO: impl
}
private static readonly MethodInfo Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
private static void EmitTypeof(ILGenerator il, Type type)
{
il.Emit(OpCodes.Ldtoken, type);
il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
}
// emit takes no args, leaves Value at top of stack
private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member)
{
// TODO: impl
il.Emit(OpCodes.Ldnull);
}
// emit takes the value being deserialized, logs on error, leaves nothing on stack
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel)
{
// TODO: impl
il.Emit(OpCodes.Pop);
} }
} }


Loading…
Cancel
Save