using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; namespace astealz.SmartSpawnController.Utils { public delegate T PropertyGetter(object instance); public delegate void PropertySetter(object instance, T value); public delegate T MethodInvoker(object instance, T1 arg1); public delegate void MethodInvoker(object instance, T value); // methods in this class creates a dynamic methods (and return a delegate) for quick access to a property or method which cannot be called by a direct call // this approach is about 5x faster than reflection static class Emit { // delegate for property getter public static PropertyGetter CreateDynamicPropertyGetter( PropertyInfo propertyInfo, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance) { if (propertyInfo == null) throw new ArgumentNullException(nameof(propertyInfo)); if (!typeof(T).IsAssignableFrom(propertyInfo.PropertyType)) throw new ArgumentException($"Property type '{propertyInfo.PropertyType}' does not match return type '{typeof(T)}'"); var getterMethodInfo = propertyInfo.GetGetMethod(true); if (getterMethodInfo == null) throw new InvalidOperationException($"Can't find getter for property '{propertyInfo.Name}' in '{propertyInfo.DeclaringType}'"); var dynMethod = new DynamicMethod($"__get_{typeof(T).Name}_prop_{propertyInfo.Name}", typeof(T), new[] { typeof(object) }, propertyInfo.DeclaringType, true); var ilGen = dynMethod.GetILGenerator(); if (getterMethodInfo.IsStatic) { ilGen.Emit(OpCodes.Call, getterMethodInfo); } else { ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); ilGen.Emit(OpCodes.Callvirt, getterMethodInfo); } ilGen.Emit(OpCodes.Ret); return (PropertyGetter)dynMethod.CreateDelegate(typeof(PropertyGetter)); } // delegate for property setter public static PropertySetter CreateDynamicPropertySetter( PropertyInfo propertyInfo, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance) { if (propertyInfo == null) throw new ArgumentNullException(nameof(propertyInfo)); if (!typeof(T).IsAssignableFrom(propertyInfo.PropertyType)) throw new ArgumentException($"Property type '{propertyInfo.PropertyType}' does not match the type of value '{typeof(T)}'"); var setterMethodInfo = propertyInfo.GetSetMethod(true); if (setterMethodInfo == null) throw new InvalidOperationException($"Can't find setter for property '{propertyInfo.Name}' in '{propertyInfo.DeclaringType}'"); var dynMethod = new DynamicMethod($"__set_{typeof(T).Name}_prop_{propertyInfo.Name}", null, new[] { typeof(object), typeof(T) }, propertyInfo.DeclaringType); var ilGen = dynMethod.GetILGenerator(); if (setterMethodInfo.IsStatic) { ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Call, setterMethodInfo); ilGen.Emit(OpCodes.Ret); } else { ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Callvirt, setterMethodInfo); } ilGen.Emit(OpCodes.Ret); return (PropertySetter)dynMethod.CreateDelegate(typeof(PropertySetter)); } // delegate to invoke a method with one argument and return value public static MethodInvoker CreateDynamicMethodInvoker(MethodInfo methodInfo) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); if (!typeof(TR).IsAssignableFrom(methodInfo.ReturnType)) throw new ArgumentException($"Method return type '{methodInfo.ReturnType}' does not match return type '{typeof(TR)}'"); var dynMethod = new DynamicMethod( $"__invoke_{typeof(TR).Name}_method_{methodInfo.Name}", // method name is only for debugging typeof(TR), new[] { typeof(object), typeof(T1) }, // add method arguments here (T2, T3, T4...) methodInfo.DeclaringType, true); var ilGen = dynMethod.GetILGenerator(); if (methodInfo.IsStatic) { // if target method is static push first argument on stack ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Call, methodInfo); } else { // otherwise push instance and cast it to type where method is declared ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // then push method arguments ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Callvirt, methodInfo); } ilGen.Emit(OpCodes.Ret); return (MethodInvoker)dynMethod.CreateDelegate(typeof(MethodInvoker)); } public static MethodInvoker CreateDynamicMethodInvoker(MethodInfo methodInfo) { if (methodInfo == null) throw new ArgumentNullException(nameof(methodInfo)); if (!typeof(void).Equals(methodInfo.ReturnType)) throw new ArgumentException($"Method return type '{methodInfo.ReturnType}' does not match return type 'void'"); var dynMethod = new DynamicMethod( $"__invoke_void_method_{methodInfo.Name}", // method name is only for debugging typeof(void), new[] { typeof(object), typeof(T) }, // add method arguments here (T2, T3, T4...) methodInfo.DeclaringType, true); var ilGen = dynMethod.GetILGenerator(); if (methodInfo.IsStatic) { // if target method is static push first argument on stack ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Call, methodInfo); } else { // otherwise push instance and cast it to type where method is declared ilGen.Emit(OpCodes.Ldarg_0); ilGen.Emit(OpCodes.Castclass, methodInfo.DeclaringType); // then push method arguments ilGen.Emit(OpCodes.Ldarg_1); ilGen.Emit(OpCodes.Callvirt, methodInfo); } ilGen.Emit(OpCodes.Ret); return (MethodInvoker)dynMethod.CreateDelegate(typeof(MethodInvoker)); } } }