FastAccess.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using System;
  2. using System.Reflection;
  3. using System.Reflection.Emit;
  4. namespace Harmony
  5. {
  6. // Based on https://www.codeproject.com/Articles/14973/Dynamic-Code-Generation-vs-Reflection
  7. public delegate object GetterHandler(object source);
  8. public delegate void SetterHandler(object source, object value);
  9. public delegate object InstantiationHandler();
  10. public class FastAccess
  11. {
  12. public static InstantiationHandler CreateInstantiationHandler(Type type)
  13. {
  14. var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);
  15. if (constructorInfo == null)
  16. {
  17. throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type));
  18. }
  19. var dynamicMethod = new DynamicMethod("InstantiateObject_" + type.Name, MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, type, true);
  20. var generator = dynamicMethod.GetILGenerator();
  21. generator.Emit(OpCodes.Newobj, constructorInfo);
  22. generator.Emit(OpCodes.Ret);
  23. return (InstantiationHandler)dynamicMethod.CreateDelegate(typeof(InstantiationHandler));
  24. }
  25. public static GetterHandler CreateGetterHandler(PropertyInfo propertyInfo)
  26. {
  27. var getMethodInfo = propertyInfo.GetGetMethod(true);
  28. var dynamicGet = CreateGetDynamicMethod(propertyInfo.DeclaringType);
  29. var getGenerator = dynamicGet.GetILGenerator();
  30. getGenerator.Emit(OpCodes.Ldarg_0);
  31. getGenerator.Emit(OpCodes.Call, getMethodInfo);
  32. BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
  33. getGenerator.Emit(OpCodes.Ret);
  34. return (GetterHandler)dynamicGet.CreateDelegate(typeof(GetterHandler));
  35. }
  36. public static GetterHandler CreateGetterHandler(FieldInfo fieldInfo)
  37. {
  38. var dynamicGet = CreateGetDynamicMethod(fieldInfo.DeclaringType);
  39. var getGenerator = dynamicGet.GetILGenerator();
  40. getGenerator.Emit(OpCodes.Ldarg_0);
  41. getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
  42. BoxIfNeeded(fieldInfo.FieldType, getGenerator);
  43. getGenerator.Emit(OpCodes.Ret);
  44. return (GetterHandler)dynamicGet.CreateDelegate(typeof(GetterHandler));
  45. }
  46. public static GetterHandler CreateFieldGetter(Type type, params string[] names)
  47. {
  48. foreach (var name in names)
  49. {
  50. if (AccessTools.Field(typeof(ILGenerator), name) != null)
  51. return CreateGetterHandler(AccessTools.Field(type, name));
  52. if (AccessTools.Property(typeof(ILGenerator), name) != null)
  53. return CreateGetterHandler(AccessTools.Property(type, name));
  54. }
  55. return null;
  56. }
  57. public static SetterHandler CreateSetterHandler(PropertyInfo propertyInfo)
  58. {
  59. var setMethodInfo = propertyInfo.GetSetMethod(true);
  60. var dynamicSet = CreateSetDynamicMethod(propertyInfo.DeclaringType);
  61. var setGenerator = dynamicSet.GetILGenerator();
  62. setGenerator.Emit(OpCodes.Ldarg_0);
  63. setGenerator.Emit(OpCodes.Ldarg_1);
  64. UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
  65. setGenerator.Emit(OpCodes.Call, setMethodInfo);
  66. setGenerator.Emit(OpCodes.Ret);
  67. return (SetterHandler)dynamicSet.CreateDelegate(typeof(SetterHandler));
  68. }
  69. public static SetterHandler CreateSetterHandler(FieldInfo fieldInfo)
  70. {
  71. var dynamicSet = CreateSetDynamicMethod(fieldInfo.DeclaringType);
  72. var setGenerator = dynamicSet.GetILGenerator();
  73. setGenerator.Emit(OpCodes.Ldarg_0);
  74. setGenerator.Emit(OpCodes.Ldarg_1);
  75. UnboxIfNeeded(fieldInfo.FieldType, setGenerator);
  76. setGenerator.Emit(OpCodes.Stfld, fieldInfo);
  77. setGenerator.Emit(OpCodes.Ret);
  78. return (SetterHandler)dynamicSet.CreateDelegate(typeof(SetterHandler));
  79. }
  80. //
  81. static DynamicMethod CreateGetDynamicMethod(Type type)
  82. {
  83. return new DynamicMethod("DynamicGet_" + type.Name, typeof(object), new Type[] { typeof(object) }, type, true);
  84. }
  85. static DynamicMethod CreateSetDynamicMethod(Type type)
  86. {
  87. return new DynamicMethod("DynamicSet_" + type.Name, typeof(void), new Type[] { typeof(object), typeof(object) }, type, true);
  88. }
  89. static void BoxIfNeeded(Type type, ILGenerator generator)
  90. {
  91. if (type.IsValueType)
  92. generator.Emit(OpCodes.Box, type);
  93. }
  94. static void UnboxIfNeeded(Type type, ILGenerator generator)
  95. {
  96. if (type.IsValueType)
  97. generator.Emit(OpCodes.Unbox_Any, type);
  98. }
  99. }
  100. }