using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core
{
///
/// Provides utilities to simplify reflection.
///
///
/// Readings:
/// * CIL instructions: https://en.wikipedia.org/wiki/List_of_CIL_instructions
/// * ECMA 335: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf
/// * MSIL programming: http://www.blackbeltcoder.com/Articles/net/msil-programming-part-1
///
/// Supports emitting constructors, instance and static methods, instance property getters and
/// setters. Does not support static properties yet.
///
public static class ReflectionUtilities
{
///
/// Emits a property getter.
///
/// The declaring type.
/// The property type.
/// The name of the property.
/// A value indicating whether the property and its getter must exist.
/// A property getter function. If is false, returns null when the property or its getter does not exist.
/// Occurs when is null or empty.
/// Occurs when the property or its getter does not exist.
/// Occurs when does not match the type of the property.
public static Func EmitPropertyGetter(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName))
throw new ArgumentNullOrEmptyException(nameof(propertyName));
var property = typeof (TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property == null || property.GetMethod == null)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find getter for {typeof(TDeclaring)}.{propertyName}.");
}
return EmitMethod>(property.GetMethod);
}
///
/// Emits a property setter.
///
/// The declaring type.
/// The property type.
/// The name of the property.
/// A value indicating whether the property and its setter must exist.
/// A property setter function. If is false, returns null when the property or its setter does not exist.
/// Occurs when is null or empty.
/// Occurs when the property or its setter does not exist.
/// Occurs when does not match the type of the property.
public static Action EmitPropertySetter(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName))
throw new ArgumentNullOrEmptyException(nameof(propertyName));
var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property == null || property.SetMethod == null)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find setter for {typeof(TDeclaring)}.{propertyName}.");
}
return EmitMethod>(property.SetMethod);
}
///
/// Emits a property getter and setter.
///
/// The declaring type.
/// The property type.
/// The name of the property.
/// A value indicating whether the property and its getter and setter must exist.
/// A property getter and setter functions. If is false, returns null when the property or its getter or setter does not exist.
/// Occurs when is null or empty.
/// Occurs when the property or its getter or setter does not exist.
/// Occurs when does not match the type of the property.
public static (Func, Action) EmitPropertyGetterAndSetter(string propertyName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(propertyName))
throw new ArgumentNullOrEmptyException(nameof(propertyName));
var property = typeof(TDeclaring).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property == null || property.GetMethod == null || property.SetMethod == null)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find getter and/or setter for {typeof(TDeclaring)}.{propertyName}.");
}
return (
EmitMethod>(property.GetMethod),
EmitMethod>(property.SetMethod));
}
///
/// Emits a property getter.
///
/// The declaring type.
/// The property type.
/// The property info.
/// A property getter function.
/// Occurs when is null.
/// Occurs when the property has no getter.
/// Occurs when does not match the type of the property.
public static Func EmitPropertyGetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
if (propertyInfo.GetMethod == null)
throw new ArgumentException("Property has no getter.", nameof(propertyInfo));
return EmitMethod> (propertyInfo.GetMethod);
}
///
/// Emits a property setter.
///
/// The declaring type.
/// The property type.
/// The property info.
/// A property setter function.
/// Occurs when is null.
/// Occurs when the property has no setter.
/// Occurs when does not match the type of the property.
public static Action EmitPropertySetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
if (propertyInfo.SetMethod == null)
throw new ArgumentException("Property has no setter.", nameof(propertyInfo));
return EmitMethod>(propertyInfo.SetMethod);
}
///
/// Emits a property getter and setter.
///
/// The declaring type.
/// The property type.
/// The property info.
/// A property getter and setter functions.
/// Occurs when is null.
/// Occurs when the property has no getter or no setter.
/// Occurs when does not match the type of the property.
public static (Func, Action) EmitPropertyGetterAndSetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
if (propertyInfo.GetMethod == null || propertyInfo.SetMethod == null)
throw new ArgumentException("Property has no getter and/or no setter.", nameof(propertyInfo));
return (
EmitMethod>(propertyInfo.GetMethod),
EmitMethod>(propertyInfo.SetMethod));
}
///
/// Emits a constructor.
///
/// A lambda representing the constructor.
/// A value indicating whether the constructor must exist.
/// The optional type of the class to construct.
/// A constructor function. If is false, returns null when the constructor does not exist.
///
/// When is not specified, it is the type returned by .
/// The constructor arguments are determined by generic arguments.
/// The type returned by does not need to be exactly ,
/// when e.g. that type is not known at compile time, but it has to be a parent type (eg an interface, or object).
///
/// Occurs when the constructor does not exist and is true.
/// Occurs when is not a Func or when
/// is specified and does not match the function's returned type.
public static TLambda EmitCtor(bool mustExist = true, Type declaring = null)
{
// validate lambda type
ValidateCtorLambda();
// get instance and arguments types
var genericArgs = typeof(TLambda).GetGenericArguments();
var args = new Type[genericArgs.Length - 1];
for (var i = 0; i < args.Length; i++)
args[i] = genericArgs[i];
var returned = genericArgs[args.Length];
if (declaring == null)
declaring = returned;
else if (!returned.IsAssignableFrom(declaring))
throw new ArgumentException($"Type {returned} is not assignable from type {declaring}.", nameof(declaring));
// get the constructor infos
var ctor = declaring.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, args, null);
if (ctor == null)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find constructor {declaring}.ctor({string.Join(", ", (IEnumerable) args)}).");
}
// emit
return EmitCtor(declaring, args, ctor);
}
///
/// Emits a constructor.
///
/// A lambda representing the constructor.
/// The constructor info.
/// A constructor function.
/// Occurs when is not a Func or when its generic
/// arguments do not match those of .
/// Occurs when is null.
public static TLambda EmitCtor(ConstructorInfo ctor)
{
if (ctor == null)
throw new ArgumentNullException(nameof(ctor));
// get type and args
var declaring = ctor.DeclaringType;
var args = ctor.GetParameters().Select(x => x.ParameterType).ToArray();
// validate lambda type
ValidateCtorLambda();
// validate arguments
var genericArgs = typeof(TLambda).GetGenericArguments();
if (genericArgs.Length != args.Length + 1)
ThrowInvalidLambda("ctor", declaring, args);
for (var i = 0; i < args.Length; i++)
if (args[i] != genericArgs[i])
ThrowInvalidLambda("ctor", declaring, args);
if (!genericArgs[args.Length].IsAssignableFrom(declaring))
ThrowInvalidLambda("ctor", declaring, args);
// emit
return EmitCtor(declaring, args, ctor);
}
///
/// Emits a constructor.
///
/// A lambda representing the constructor.
/// The constructor info.
/// A constructor function.
///
/// The constructor is emitted in an unsafe way, using the lambda arguments without verifying
/// them at all. This assumes that the calling code is taking care of all verifications, in order
/// to avoid cast errors.
///
/// Occurs when is not a Func or when its generic
/// arguments do not match those of .
/// Occurs when is null.
public static TLambda EmitCtorUnsafe(ConstructorInfo ctor)
{
if (ctor == null)
throw new ArgumentNullException(nameof(ctor));
// get type and args
var declaring = ctor.DeclaringType;
var module = declaring?.Module;
if (module == null)
throw new ArgumentException("Failed to get ctor's declaring type module.", nameof(ctor));
// validate lambda type
ValidateCtorLambda();
// unsafe - use lambda's args and assume they are correct
//var args = ctor.GetParameters().Select(x => x.ParameterType).ToArray();
var genArgs = typeof(TLambda).GetGenericArguments();
var args = new Type[genArgs.Length - 1];
Array.Copy(genArgs, 0, args, 0, args.Length);
// emit
var dm = new DynamicMethod(string.Empty, declaring, args, module, true);
var ilgen = dm.GetILGenerator();
EmitLdargs(ilgen, args.Length);
ilgen.Emit(OpCodes.Newobj, ctor); // ok to just return, it's only objects
ilgen.Emit(OpCodes.Ret);
return (TLambda) (object) dm.CreateDelegate(typeof(TLambda));
}
private static void ValidateCtorLambda()
{
var typeLambda = typeof(TLambda);
var genericDefinition = typeLambda.IsGenericType ? typeLambda.GetGenericTypeDefinition() : null;
if (genericDefinition == null || genericDefinition.FullName == null || !genericDefinition.FullName.StartsWith("System.Func`"))
throw new ArgumentException($"Lambda {typeLambda} is not a Func.", nameof(TLambda));
}
private static TLambda EmitCtor(Type declaring, Type[] args, ConstructorInfo ctor)
{
var dm = new DynamicMethod(string.Empty, declaring, args, declaring.Module, true);
var ilgen = dm.GetILGenerator();
EmitLdargs(ilgen, args.Length);
ilgen.Emit(OpCodes.Newobj, ctor); // ok to just return, it's only objects
ilgen.Emit(OpCodes.Ret);
return (TLambda) (object) dm.CreateDelegate(typeof(TLambda));
}
private static void ValidateMethodLambda(out bool isFunction)
{
isFunction = false;
var typeLambda = typeof(TLambda);
var genericDefinition = typeLambda.IsGenericType ? typeLambda.GetGenericTypeDefinition() : null;
if (typeLambda.FullName == "System.Action")
return;
if (genericDefinition == null
|| genericDefinition.FullName == null
|| !genericDefinition.FullName.StartsWith("System.Func`") && !genericDefinition.FullName.StartsWith("System.Action`"))
throw new ArgumentException($"Lambda {typeLambda} is not a Func nor an Action.", nameof(TLambda));
isFunction = genericDefinition.FullName.StartsWith("System.Func`");
}
///
/// Emits a method.
///
/// A lambda representing the method.
/// The method info.
/// The method.
/// Occurs when is null.
/// Occurs when Occurs when does not match the method signature.
public static TLambda EmitMethod(MethodInfo method)
{
if (method == null)
throw new ArgumentNullException(nameof(method));
// get type and args
var type = method.DeclaringType;
var returned = method.ReturnType;
var args = method.GetParameters().Select(x => x.ParameterType).ToArray();
// validate lambda type
ValidateMethodLambda(out var isFunction);
var genericArgs = typeof(TLambda).GetGenericArguments();
var isStatic = method.IsStatic;
var ax = 0;
var gx = 0;
// must match the expected number of args
var expectedCount = (isStatic ? 0 : 1) + args.Length + (isFunction ? 1 : 0);
if (expectedCount != genericArgs.Length)
ThrowInvalidLambda(method.Name, returned, args);
// if not static then the first generic arg must be the declaring type
if (!isStatic && genericArgs[gx++] != type)
ThrowInvalidLambda(method.Name, returned, args);
// all other generic args must match parameters
// except the last one, if it's a function, 'cos then it's the returned type
while (gx < genericArgs.Length - (isFunction ? 1 : 0))
if (genericArgs[gx++] != args[ax++])
ThrowInvalidLambda(method.Name, returned, args);
// if it's a function then the last one must match the returned type
if (isFunction)
{
if (genericArgs[gx] != returned)
{
if (genericArgs[gx].IsAssignableFrom(returned))
{
returned = genericArgs[gx]; // FIXME cast etc?!
}
else ThrowInvalidLambda(method.Name, returned, args);
}
}
//if (isFunction && !genericArgs[gx].IsAssignableFrom(returned))
// ThrowInvalidLambda(method.Name, returned, args);
// emit
return EmitMethod(returned, args, method);
}
///
/// Emits an instance method.
///
/// A lambda representing the method.
/// The name of the method.
/// A value indicating whether the constructor must exist.
/// The method. If is false, returns null when the method does not exist.
///
/// The method arguments are determined by generic arguments.
///
/// Occurs when is null or empty.
/// Occurs when no proper method with name could be found.
/// Occurs when Occurs when does not match the method signature.
public static TLambda EmitMethod(string methodName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(methodName))
throw new ArgumentNullOrEmptyException(nameof(methodName));
// validate lambda type
ValidateMethodLambda(out var isFunction);
// get instance and arguments types
var genericArgs = typeof(TLambda).GetGenericArguments();
var gx = 0;
var declaring = genericArgs[gx++];
var args = new Type[genericArgs.Length - 1 - (isFunction ? 1 : 0)];
for (var i = 0; i < args.Length; i++)
args[i] = genericArgs[gx++];
var returned = isFunction ? genericArgs[gx] : typeof (void);
// get the method infos
var method = declaring.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, args, null);
if (method == null || isFunction && method.ReturnType != returned)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find method {declaring}.{methodName}({string.Join(", ", (IEnumerable) args)}).");
}
// emit
return EmitMethod(returned, args, method);
}
///
/// Emits a static method.
///
/// The declaring type.
/// A lambda representing the method.
/// The name of the method.
/// A value indicating whether the constructor must exist.
/// The method. If is false, returns null when the method does not exist.
///
/// The method arguments are determined by generic arguments.
///
/// Occurs when is null or empty.
/// Occurs when no proper method with name could be found.
/// Occurs when Occurs when does not match the method signature.
public static TLambda EmitMethod(string methodName, bool mustExist = true)
{
return EmitMethod(typeof (TDeclaring), methodName, mustExist);
}
///
/// Emits a static method.
///
/// A lambda representing the method.
/// The declaring type.
/// The name of the method.
/// A value indicating whether the constructor must exist.
/// The method. If is false, returns null when the method does not exist.
///
/// The method arguments are determined by generic arguments.
///
/// Occurs when is null or empty.
/// Occurs when no proper method with name could be found.
/// Occurs when Occurs when does not match the method signature.
public static TLambda EmitMethod(Type declaring, string methodName, bool mustExist = true)
{
if (string.IsNullOrWhiteSpace(methodName))
throw new ArgumentNullOrEmptyException(nameof(methodName));
// validate lambda type
ValidateMethodLambda(out var isFunction);
// get instance and arguments types
var genericArgs = typeof(TLambda).GetGenericArguments();
var args = new Type[genericArgs.Length - (isFunction ? 1 : 0)];
for (var i = 0; i < args.Length; i++)
args[i] = genericArgs[i];
var returned = isFunction ? genericArgs[args.Length] : typeof (void);
// get the method infos
var method = declaring.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static,
null, args, null);
if (method == null || isFunction && method.ReturnType != returned)
{
if (!mustExist) return default;
throw new InvalidOperationException($"Could not find static method {declaring}.{methodName}({string.Join(", ", (IEnumerable) args)}).");
}
// emit
return EmitMethod(returned, args, method);
}
private static TLambda EmitMethod(Type returned, Type[] args, MethodInfo method)
{
var args2 = args;
if (!method.IsStatic)
{
args2 = new Type[args.Length + 1];
args2[0] = method.DeclaringType;
Array.Copy(args, 0, args2, 1, args.Length);
}
var module = method.DeclaringType?.Module;
if (module == null)
throw new ArgumentException("Failed to get method's declaring type module.", nameof(method));
var dm = new DynamicMethod(string.Empty, returned, args2, module, true);
var ilgen = dm.GetILGenerator();
EmitLdargs(ilgen, args2.Length);
ilgen.Emit(method.IsStatic ? OpCodes.Call : OpCodes.Callvirt, method);
ilgen.Emit(OpCodes.Ret);
return (TLambda) (object) dm.CreateDelegate(typeof(TLambda));
}
private static void EmitLdargs(ILGenerator ilgen, int count)
{
if (count < 5)
{
if (count > 0)
ilgen.Emit(OpCodes.Ldarg_0);
if (count > 1)
ilgen.Emit(OpCodes.Ldarg_1);
if (count > 2)
ilgen.Emit(OpCodes.Ldarg_2);
if (count > 3)
ilgen.Emit(OpCodes.Ldarg_3);
}
else
{
for (var i = 0; i < count; i++)
ilgen.Emit(OpCodes.Ldarg, i);
}
}
private static void ThrowInvalidLambda(string methodName, Type returned, Type[] args)
{
throw new ArgumentException($"Lambda {typeof(TLambda)} does not match {methodName}({string.Join(", ", (IEnumerable) args)}):{returned}.", nameof(TLambda));
}
// 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);
}
}
}