2020-01-23 00:21:03 +00:00
|
|
|
|
#nullable disable
|
|
|
|
|
|
|
|
|
|
using System;
|
2014-05-28 17:31:22 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Common
|
|
|
|
|
{
|
|
|
|
|
public class SettingsUtil
|
|
|
|
|
{
|
2014-07-03 18:02:22 +00:00
|
|
|
|
private sealed class DefaultValueSetter
|
2014-05-28 17:31:22 +00:00
|
|
|
|
{
|
|
|
|
|
public Action<object, object[]> SetDefaultValues;
|
|
|
|
|
public object[] DefaultValues;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static IDictionary<Type, DefaultValueSetter> DefaultValueSetters = new ConcurrentDictionary<Type, DefaultValueSetter>();
|
|
|
|
|
|
2014-07-14 16:10:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// set all properties (not fields!) of obj with a DefaultValueAttribute to that value
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="obj">the obj to act on</param>
|
2014-05-28 17:31:22 +00:00
|
|
|
|
public static void SetDefaultValues<T>(T obj)
|
|
|
|
|
{
|
2020-02-25 19:15:43 +00:00
|
|
|
|
if (!DefaultValueSetters.TryGetValue(typeof(T), out var f))
|
2014-05-28 17:31:22 +00:00
|
|
|
|
{
|
|
|
|
|
f = CreateSetter(typeof(T));
|
|
|
|
|
DefaultValueSetters[typeof(T)] = f;
|
|
|
|
|
}
|
|
|
|
|
f.SetDefaultValues(obj, f.DefaultValues);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-27 21:50:42 +00:00
|
|
|
|
private static Dictionary<Type, OpCode> IntTypes = new Dictionary<Type,OpCode>
|
|
|
|
|
{
|
|
|
|
|
{ typeof(byte), OpCodes.Conv_U1 },
|
|
|
|
|
{ typeof(sbyte), OpCodes.Conv_I1 },
|
|
|
|
|
{ typeof(ushort), OpCodes.Conv_U2 },
|
|
|
|
|
{ typeof(short), OpCodes.Conv_I2 },
|
|
|
|
|
{ typeof(uint), OpCodes.Conv_U4 },
|
|
|
|
|
{ typeof(int), OpCodes.Conv_I4 },
|
|
|
|
|
{ typeof(ulong), OpCodes.Conv_U8 },
|
|
|
|
|
{ typeof(long), OpCodes.Conv_I8 },
|
|
|
|
|
{ typeof(UIntPtr), OpCodes.Conv_U },
|
|
|
|
|
{ typeof(IntPtr), OpCodes.Conv_I },
|
|
|
|
|
};
|
|
|
|
|
|
2014-05-28 17:31:22 +00:00
|
|
|
|
private static DefaultValueSetter CreateSetter(Type t)
|
|
|
|
|
{
|
2019-03-20 05:13:04 +00:00
|
|
|
|
var dyn = new DynamicMethod($"SetDefaultValues_{t.Name}", null, new[] { typeof(object), typeof(object[]) }, false);
|
2014-05-28 17:31:22 +00:00
|
|
|
|
var il = dyn.GetILGenerator();
|
|
|
|
|
List<object> DefaultValues = new List<object>();
|
|
|
|
|
|
|
|
|
|
il.Emit(OpCodes.Ldarg_0); // arg0: object to set properties of
|
|
|
|
|
il.Emit(OpCodes.Castclass, t); // cast to appropriate type
|
|
|
|
|
|
|
|
|
|
foreach (var prop in t.GetProperties())
|
|
|
|
|
{
|
|
|
|
|
if (!prop.CanWrite)
|
|
|
|
|
continue;
|
|
|
|
|
MethodInfo method = prop.GetSetMethod(true);
|
|
|
|
|
foreach (object attr in prop.GetCustomAttributes(true))
|
|
|
|
|
{
|
|
|
|
|
if (attr is DefaultValueAttribute)
|
|
|
|
|
{
|
|
|
|
|
object value = (attr as DefaultValueAttribute).Value;
|
|
|
|
|
Type desiredType = method.GetParameters()[0].ParameterType;
|
2016-02-27 21:50:42 +00:00
|
|
|
|
Type sourceType = value.GetType();
|
2014-05-28 17:31:22 +00:00
|
|
|
|
|
|
|
|
|
int idx = DefaultValues.Count;
|
|
|
|
|
DefaultValues.Add(value);
|
|
|
|
|
|
|
|
|
|
il.Emit(OpCodes.Dup); // object to act on
|
|
|
|
|
il.Emit(OpCodes.Ldarg_1); // arg1: array of default values
|
|
|
|
|
il.Emit(OpCodes.Ldc_I4, idx); // load index
|
|
|
|
|
il.Emit(OpCodes.Ldelem, typeof(object)); // get default value at appropriate index
|
2016-02-27 21:50:42 +00:00
|
|
|
|
|
|
|
|
|
// cast to the expected type of the set method
|
|
|
|
|
if (desiredType.IsAssignableFrom(sourceType))
|
|
|
|
|
{
|
|
|
|
|
il.Emit(OpCodes.Unbox_Any, desiredType);
|
|
|
|
|
}
|
|
|
|
|
else if (IntTypes.ContainsKey(sourceType) && IntTypes.ContainsKey(desiredType))
|
|
|
|
|
{
|
|
|
|
|
il.Emit(OpCodes.Unbox_Any, sourceType);
|
|
|
|
|
il.Emit(IntTypes[desiredType]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-03-20 05:13:04 +00:00
|
|
|
|
throw new InvalidOperationException($"Default value assignment will fail for {t.Name}.{prop.Name}");
|
2016-02-27 21:50:42 +00:00
|
|
|
|
}
|
2014-05-28 17:31:22 +00:00
|
|
|
|
il.Emit(OpCodes.Callvirt, method);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
il.Emit(OpCodes.Pop);
|
|
|
|
|
il.Emit(OpCodes.Ret);
|
|
|
|
|
return new DefaultValueSetter
|
|
|
|
|
{
|
|
|
|
|
SetDefaultValues = (Action<object, object[]>)dyn.CreateDelegate(typeof(Action<object, object[]>)),
|
|
|
|
|
DefaultValues = DefaultValues.ToArray()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|