MethodInvoker.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System;
  2. using System.Reflection;
  3. using System.Reflection.Emit;
  4. namespace Harmony
  5. {
  6. // Based on https://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
  7. public delegate object FastInvokeHandler(object target, object[] paramters);
  8. public class MethodInvoker
  9. {
  10. public static FastInvokeHandler GetHandler(DynamicMethod methodInfo, Module module)
  11. {
  12. return Handler(methodInfo, module);
  13. }
  14. public static FastInvokeHandler GetHandler(MethodInfo methodInfo)
  15. {
  16. return Handler(methodInfo, methodInfo.DeclaringType.Module);
  17. }
  18. static FastInvokeHandler Handler(MethodInfo methodInfo, Module module, bool directBoxValueAccess = false)
  19. {
  20. var dynamicMethod = new DynamicMethod("FastInvoke_" + methodInfo.Name + "_" + (directBoxValueAccess ? "direct" : "indirect"), typeof(object), new Type[] { typeof(object), typeof(object[]) }, module, true);
  21. var il = dynamicMethod.GetILGenerator();
  22. if (!methodInfo.IsStatic)
  23. {
  24. il.Emit(OpCodes.Ldarg_0);
  25. EmitUnboxIfNeeded(il, methodInfo.DeclaringType);
  26. }
  27. var generateLocalBoxValuePtr = true;
  28. var ps = methodInfo.GetParameters();
  29. for (var i = 0; i < ps.Length; i++)
  30. {
  31. var argType = ps[i].ParameterType;
  32. var argIsByRef = argType.IsByRef;
  33. if (argIsByRef)
  34. argType = argType.GetElementType();
  35. var argIsValueType = argType.IsValueType;
  36. if (argIsByRef && argIsValueType && !directBoxValueAccess)
  37. {
  38. // used later when storing back the reference to the new box in the array.
  39. il.Emit(OpCodes.Ldarg_1);
  40. EmitFastInt(il, i);
  41. }
  42. il.Emit(OpCodes.Ldarg_1);
  43. EmitFastInt(il, i);
  44. if (argIsByRef && !argIsValueType)
  45. {
  46. il.Emit(OpCodes.Ldelema, typeof(object));
  47. }
  48. else
  49. {
  50. il.Emit(OpCodes.Ldelem_Ref);
  51. if (argIsValueType)
  52. {
  53. if (!argIsByRef || !directBoxValueAccess)
  54. {
  55. // if !directBoxValueAccess, create a new box if required
  56. il.Emit(OpCodes.Unbox_Any, argType);
  57. if (argIsByRef)
  58. {
  59. // box back
  60. il.Emit(OpCodes.Box, argType);
  61. // store new box value address to local 0
  62. il.Emit(OpCodes.Dup);
  63. il.Emit(OpCodes.Unbox, argType);
  64. if (generateLocalBoxValuePtr)
  65. {
  66. generateLocalBoxValuePtr = false;
  67. // Yes, you're seeing this right - a local of type void* to store the box value address!
  68. il.DeclareLocal(typeof(void*), true);
  69. }
  70. il.Emit(OpCodes.Stloc_0);
  71. // arr and index set up already
  72. il.Emit(OpCodes.Stelem_Ref);
  73. // load address back to stack
  74. il.Emit(OpCodes.Ldloc_0);
  75. }
  76. }
  77. else
  78. {
  79. // if directBoxValueAccess, emit unbox (get value address)
  80. il.Emit(OpCodes.Unbox, argType);
  81. }
  82. }
  83. }
  84. }
  85. #pragma warning disable XS0001
  86. if (methodInfo.IsStatic)
  87. il.EmitCall(OpCodes.Call, methodInfo, null);
  88. else
  89. il.EmitCall(OpCodes.Callvirt, methodInfo, null);
  90. #pragma warning restore XS0001
  91. if (methodInfo.ReturnType == typeof(void))
  92. il.Emit(OpCodes.Ldnull);
  93. else
  94. EmitBoxIfNeeded(il, methodInfo.ReturnType);
  95. il.Emit(OpCodes.Ret);
  96. var invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
  97. return invoder;
  98. }
  99. static void EmitCastToReference(ILGenerator il, Type type)
  100. {
  101. if (type.IsValueType)
  102. il.Emit(OpCodes.Unbox_Any, type);
  103. else
  104. il.Emit(OpCodes.Castclass, type);
  105. }
  106. static void EmitUnboxIfNeeded(ILGenerator il, Type type)
  107. {
  108. if (type.IsValueType)
  109. il.Emit(OpCodes.Unbox_Any, type);
  110. }
  111. static void EmitBoxIfNeeded(ILGenerator il, Type type)
  112. {
  113. if (type.IsValueType)
  114. il.Emit(OpCodes.Box, type);
  115. }
  116. static void EmitFastInt(ILGenerator il, int value)
  117. {
  118. switch (value)
  119. {
  120. case -1:
  121. il.Emit(OpCodes.Ldc_I4_M1);
  122. return;
  123. case 0:
  124. il.Emit(OpCodes.Ldc_I4_0);
  125. return;
  126. case 1:
  127. il.Emit(OpCodes.Ldc_I4_1);
  128. return;
  129. case 2:
  130. il.Emit(OpCodes.Ldc_I4_2);
  131. return;
  132. case 3:
  133. il.Emit(OpCodes.Ldc_I4_3);
  134. return;
  135. case 4:
  136. il.Emit(OpCodes.Ldc_I4_4);
  137. return;
  138. case 5:
  139. il.Emit(OpCodes.Ldc_I4_5);
  140. return;
  141. case 6:
  142. il.Emit(OpCodes.Ldc_I4_6);
  143. return;
  144. case 7:
  145. il.Emit(OpCodes.Ldc_I4_7);
  146. return;
  147. case 8:
  148. il.Emit(OpCodes.Ldc_I4_8);
  149. return;
  150. }
  151. if (value > -129 && value < 128)
  152. il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
  153. else
  154. il.Emit(OpCodes.Ldc_I4, value);
  155. }
  156. }
  157. }