using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; namespace Umbraco.Tests.Benchmarks { /// /// Provides utilities to simplify reflection. /// public static class ReflectionUtilitiesForTest { // the code below should NOT be used // // keeping all this around as a reference for now - building expressions into dynamic assemblies, // the resulting methods are fast (as fast as IL-generated methods) whereas the standard compiled // expressions are slowish - alas, the compiled methods do not have access to eg private members // and anything that would violate access control - we're not using it anymore - still used in a // benchmark internal static Func GetCtor() { var type = typeof (TInstance); var type0 = typeof (TArg0); // get the constructor infos var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] { type0 }, null); if (ctor == null) throw new InvalidOperationException($"Could not find constructor {type}.ctor({type0})."); var exprArgs = ctor.GetParameters().Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray(); // ReSharper disable once CoVariantArrayConversion var exprNew = Expression.New(ctor, exprArgs); var expr = Expression.Lambda>(exprNew, exprArgs); return CompileToDelegate(expr); } internal static Func GetCtor() { var type = typeof (TInstance); var type0 = typeof (TArg0); var type1 = typeof (TArg1); // get the constructor infos var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] { type0, type1 }, null); if (ctor == null) throw new InvalidOperationException($"Could not find constructor {type}.ctor({type0}, {type1})."); var exprArgs = ctor.GetParameters().Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray(); // ReSharper disable once CoVariantArrayConversion var exprNew = Expression.New(ctor, exprArgs); var expr = Expression.Lambda>(exprNew, exprArgs); return CompileToDelegate(expr); } internal static TMethod GetMethod(MethodInfo method) { var type = method.DeclaringType; GetMethodParms(out var parameterTypes, out var returnType); return GetStaticMethod(method, method.Name, type, parameterTypes, returnType); } internal static TMethod GetMethod(MethodInfo method) { var type = method.DeclaringType; GetMethodParms(out var parameterTypes, out var returnType); return GetMethod(method, method.Name, type, parameterTypes, returnType); } internal static TMethod GetMethod(string methodName) { var type = typeof (TInstance); GetMethodParms(out var parameterTypes, out var returnType); var method = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, parameterTypes, null); return GetMethod(method, methodName, type, parameterTypes, returnType); } private static void GetMethodParms(out Type[] parameterTypes, out Type returnType) { var typeM = typeof (TMethod); var typeList = new List(); returnType = typeof (void); if (!typeof (MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof (MulticastDelegate)) throw new InvalidOperationException("Invalid TMethod, must be a Func or Action."); var typeName = typeM.FullName; if (typeName == null) throw new InvalidOperationException($"Could not get {typeM} type name."); if (typeName.StartsWith("System.Func`")) { var i = 0; while (i < typeM.GenericTypeArguments.Length - 1) typeList.Add(typeM.GenericTypeArguments[i++]); returnType = typeM.GenericTypeArguments[i]; } else if (typeName.StartsWith("System.Action`")) { var i = 0; while (i < typeM.GenericTypeArguments.Length) typeList.Add(typeM.GenericTypeArguments[i++]); } else if (typeName == "System.Action") { // no args } else throw new InvalidOperationException(typeName); parameterTypes = typeList.ToArray(); } private static void GetMethodParms(out Type[] parameterTypes, out Type returnType) { var type = typeof (TInstance); var typeM = typeof (TMethod); var typeList = new List(); returnType = typeof (void); if (!typeof (MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof (MulticastDelegate)) throw new InvalidOperationException("Invalid TMethod, must be a Func or Action."); var typeName = typeM.FullName; if (typeName == null) throw new InvalidOperationException($"Could not get {typeM} type name."); if (!typeM.IsGenericType) throw new InvalidOperationException($"Type {typeName} is not generic."); if (typeM.GenericTypeArguments[0] != type) throw new InvalidOperationException($"Invalid type {typeName}, the first generic argument must be {type.FullName}."); if (typeName.StartsWith("System.Func`")) { var i = 1; while (i < typeM.GenericTypeArguments.Length - 1) typeList.Add(typeM.GenericTypeArguments[i++]); returnType = typeM.GenericTypeArguments[i]; } else if (typeName.StartsWith("System.Action`")) { var i = 1; while (i < typeM.GenericTypeArguments.Length) typeList.Add(typeM.GenericTypeArguments[i++]); } else throw new InvalidOperationException(typeName); parameterTypes = typeList.ToArray(); } private static TMethod GetStaticMethod(MethodInfo method, string methodName, Type type, Type[] parameterTypes, Type returnType) { if (method == null || method.ReturnType != returnType) throw new InvalidOperationException($"Could not find static method {type}.{methodName}({string.Join(",", parameterTypes.Select(x => x.ToString()))}) : {returnType}"); var e = new List(); foreach (var p in method.GetParameters()) e.Add(Expression.Parameter(p.ParameterType, p.Name)); var exprCallArgs = e.ToArray(); var exprLambdaArgs = exprCallArgs; // ReSharper disable once CoVariantArrayConversion var exprCall = Expression.Call(method, exprCallArgs); var expr = Expression.Lambda(exprCall, exprLambdaArgs); return CompileToDelegate(expr); } private static TMethod GetMethod(MethodInfo method, string methodName, Type type, Type[] parameterTypes, Type returnType) { if (method == null || method.ReturnType != returnType) throw new InvalidOperationException($"Could not find method {type}.{methodName}({string.Join(",", parameterTypes.Select(x => x.ToString()))}) : {returnType}"); var e = new List(); foreach (var p in method.GetParameters()) e.Add(Expression.Parameter(p.ParameterType, p.Name)); var exprCallArgs = e.ToArray(); var exprThis = Expression.Parameter(type, "this"); e.Insert(0, exprThis); var exprLambdaArgs = e.ToArray(); // ReSharper disable once CoVariantArrayConversion var exprCall = Expression.Call(exprThis, method, exprCallArgs); var expr = Expression.Lambda(exprCall, exprLambdaArgs); return expr.Compile(); } internal const AssemblyBuilderAccess DefaultAssemblyBuilderAccess = AssemblyBuilderAccess.Run; internal const AssemblyBuilderAccess NoAssembly = 0; internal static TLambda Compile(Expression expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { return access == NoAssembly ? expr.Compile() : CompileToDelegate(expr, access); } internal static Action CompileToDelegate(Expression expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static); // CompileToMethod requires a static method expr.CompileToMethod(builder); return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } internal static Action CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (void), new[] { typeof (T1) }); expr.CompileToMethod(builder); return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } internal static Action CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (void), new[] { typeof (T1), typeof (T2) }); expr.CompileToMethod(builder); return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } internal static Action CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (void), new[] { typeof (T1), typeof (T2), typeof (T3) }); expr.CompileToMethod(builder); return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method")); } internal static Func CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof (TResult), Array.Empty()); // CompileToMethod requires a static method expr.CompileToMethod(builder); return (Func) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } internal static Func CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (TResult), new[] { typeof (T1) }); expr.CompileToMethod(builder); return (Func) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } internal static Func CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (TResult), new[] { typeof (T1), typeof (T2) }); expr.CompileToMethod(builder); return (Func) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } internal static Func CompileToDelegate(Expression> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method typeof (TResult), new[] { typeof (T1), typeof (T2), typeof (T3) }); expr.CompileToMethod(builder); return (Func) Delegate.CreateDelegate(typeof (Func), typeBuilder.CreateType().GetMethod("Method")); } internal static TMethod CompileToDelegate(Expression expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var typeBuilder = CreateTypeBuilder(access); GetMethodParms(out var parameterTypes, out var returnType); var builder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method returnType, parameterTypes); expr.CompileToMethod(builder); return (TMethod) (object) Delegate.CreateDelegate(typeof (TMethod), typeBuilder.CreateType().GetMethod("Method")); } internal static TMethod[] CompileToDelegates(params Expression[] exprs) => CompileToDelegates(AssemblyBuilderAccess.RunAndCollect, exprs); internal static TMethod[] CompileToDelegates(AssemblyBuilderAccess access, params Expression[] exprs) { var typeBuilder = CreateTypeBuilder(access); GetMethodParms(out var parameterTypes, out var returnType); var i = 0; foreach (var expr in exprs) { var builder = typeBuilder.DefineMethod($"Method_{i++}", MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method returnType, parameterTypes); expr.CompileToMethod(builder); } var type = typeBuilder.CreateType(); var methods = new TMethod[exprs.Length]; for (i = 0; i < exprs.Length; i++) methods[i] = (TMethod) (object) Delegate.CreateDelegate(typeof (TMethod), type.GetMethod($"Method_{i++}")); return methods; } private static TypeBuilder CreateTypeBuilder(AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess) { var assemblyName = new AssemblyName("Umbraco.Core.DynamicAssemblies." + Guid.NewGuid().ToString("N")); var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, access); var module = (access & AssemblyBuilderAccess.Save) > 0 ? assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll") : assembly.DefineDynamicModule(assemblyName.Name); // has to be transient return module.DefineType("Class", TypeAttributes.Public | TypeAttributes.Abstract); } } }