/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Microsoft Public License. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Microsoft Public License, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Microsoft Public License.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Dynamic.Utils;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Runtime.CompilerServices;
#if SILVERLIGHT
using System.Core;
#endif
#if CLR2
namespace Microsoft.Scripting.Ast{
#else
namespace System.Linq.Expressions {
#endif
using Compiler;
/// <summary>
/// Creates a <see cref="LambdaExpression"/> node.
/// This captures a block of code that is similar to a .NET method body.
/// </summary>
/// <remarks>
/// Lambda expressions take input through parameters and are expected to be fully bound.
/// </remarks>
#if !SILVERLIGHT
[DebuggerTypeProxy(typeof(Expression.LambdaExpressionProxy))]
#endif
public abstract class LambdaExpression : Expression {
private readonly string _name;
private readonly Expression _body;
private readonly ReadOnlyCollection<ParameterExpression> _parameters;
private readonly Type _delegateType;
private readonly bool _tailCall;
internal LambdaExpression(
Type delegateType,
string name,
Expression body,
bool tailCall,
ReadOnlyCollection<ParameterExpression> parameters
) {
Debug.Assert(delegateType != null);
_name = name;
_body = body;
_parameters = parameters;
_delegateType = delegateType;
_tailCall = tailCall;
}
/// <summary>
/// Gets the static type of the expression that this <see cref="Expression" /> represents. (Inherited from <see cref="Expression"/>.)
/// </summary>
/// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
public sealed override Type Type {
get { return _delegateType; }
}
/// <summary>
/// Returns the node type of this <see cref="Expression" />. (Inherited from <see cref="Expression" />.)
/// </summary>
/// <returns>The <see cref="ExpressionType"/> that represents this expression.</returns>
public sealed override ExpressionType NodeType {
get { return ExpressionType.Lambda; }
}
/// <summary>
/// Gets the parameters of the lambda expression.
/// </summary>
public ReadOnlyCollection<ParameterExpression> Parameters {
get { return _parameters; }
}
/// <summary>
/// Gets the name of the lambda expression.
/// </summary>
/// <remarks>Used for debugging purposes.</remarks>
public string Name {
get { return _name; }
}
/// <summary>
/// Gets the body of the lambda expression.
/// </summary>
public Expression Body {
get { return _body; }
}
/// <summary>
/// Gets the return type of the lambda expression.
/// </summary>
public Type ReturnType {
get { return Type.GetMethod("Invoke").ReturnType; }
}
/// <summary>
/// Gets the value that indicates if the lambda expression will be compiled with
/// tail call optimization.
/// </summary>
public bool TailCall {
get { return _tailCall; }
}
/// <summary>
/// Produces a delegate that represents the lambda expression.
/// </summary>
/// <returns>A delegate containing the compiled version of the lambda.</returns>
public Delegate Compile() {
return LambdaCompiler.Compile(this, null);
}
/// <summary>
/// Produces a delegate that represents the lambda expression.
/// </summary>
/// <param name="debugInfoGenerator">Debugging information generator used by the compiler to mark sequence points and annotate local variables.</param>
/// <returns>A delegate containing the compiled version of the lambda.</returns>
public Delegate Compile(DebugInfoGenerator debugInfoGenerator) {
ContractUtils.RequiresNotNull(debugInfoGenerator, "debugInfoGenerator");
return LambdaCompiler.Compile(this, debugInfoGenerator);
}
/// <summary>
/// Compiles the lambda into a method definition.
/// </summary>
/// <param name="method">A <see cref="MethodBuilder"/> which will be used to hold the lambda's IL.</param>
public void CompileToMethod(MethodBuilder method) {
CompileToMethodInternal(method, null);
}
/// <summary>
/// Compiles the lambda into a method definition and custom debug information.
/// </summary>
/// <param name="method">A <see cref="MethodBuilder"/> which will be used to hold the lambda's IL.</param>
/// <param name="debugInfoGenerator">Debugging information generator used by the compiler to mark sequence points and annotate local variables.</param>
public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
ContractUtils.RequiresNotNull(debugInfoGenerator, "debugInfoGenerator");
CompileToMethodInternal(method, debugInfoGenerator);
}
private void CompileToMethodInternal(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) {
ContractUtils.RequiresNotNull(method, "method");
ContractUtils.Requires(method.IsStatic, "method");
var type = method.DeclaringType as TypeBuilder;
if (type == null) throw Error.MethodBuilderDoesNotHaveTypeBuilder();
LambdaCompiler.Compile(this, method, debugInfoGenerator);
}
internal abstract LambdaExpression Accept(StackSpiller spiller);
}
/// <summary>
/// Defines a <see cref="Expression{TDelegate}"/> node.
/// This captures a block of code that is similar to a .NET method body.
/// </summary>
/// <typeparam name="TDelegate">The type of the delegate.</typeparam>
/// <remarks>
/// Lambda expressions take input through parameters and are expected to be fully bound.
/// </remarks>
public sealed class Expression<TDelegate> : LambdaExpression {
internal Expression(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters)
: base(typeof(TDelegate), name, body, tailCall, parameters) {
}
/// <summary>
/// Produces a delegate that represents the lambda expression.
/// </summary>
/// <returns>A delegate containing the compiled version of the lambda.</returns>
public new TDelegate Compile() {
return (TDelegate)(object)LambdaCompiler.Compile(this, null);
}
/// <summary>
/// Produces a delegate that represents the lambda expression.
/// </summary>
/// <param name="debugInfoGenerator">Debugging information generator used by the compiler to mark sequence points and annotate local variables.</param>
/// <returns>A delegate containing the compiled version of the lambda.</returns>
public new TDelegate Compile(DebugInfoGenerator debugInfoGenerator) {
ContractUtils.RequiresNotNull(debugInfoGenerator, "debugInfoGenerator");
return (TDelegate)(object)LambdaCompiler.Compile(this, debugInfoGenerator);
}
/// <summary>
/// Creates a new expression that is like this one, but using the
/// supplied children. If all of the children are the same, it will
/// return this expression.
/// </summary>
/// <param name="body">The <see cref="LambdaExpression.Body">Body</see> property of the result.</param>
/// <param name="parameters">The <see cref="LambdaExpression.Parameters">Parameters</see> property of the result.</param>
/// <returns>This expression if no children changed, or an expression with the updated children.</returns>
public Expression<TDelegate> Update(Expression body, IEnumerable<ParameterExpression> parameters) {
if (body == Body && parameters == Parameters) {
return this;
}
return Expression.Lambda<TDelegate>(body, Name, TailCall, parameters);
}
/// <summary>
/// Dispatches to the specific visit method for this node type.
/// </summary>
protected internal override Expression Accept(ExpressionVisitor visitor) {
return visitor.VisitLambda(this);
}
internal override LambdaExpression Accept(StackSpiller spiller) {
return spiller.Rewrite(this);
}
internal static LambdaExpression Create(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) {
return new Expression<TDelegate>(body, name, tailCall, parameters);
}
}
public partial class Expression {
/// <summary>
/// Creates an Expression{T} given the delegate type. Caches the
/// factory method to speed up repeated creations for the same T.
/// </summary>
internal static LambdaExpression CreateLambda(Type delegateType, Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters) {
// Get or create a delegate to the public Expression.Lambda<T>
// method and call that will be used for creating instances of this
// delegate type
LambdaFactory fastPath;
if (_LambdaFactories == null) {
// NOTE: this must be Interlocked assigment since we use _LambdaFactories for locking.
Interlocked.CompareExchange(ref _LambdaFactories, new CacheDict<Type, LambdaFactory>(50), null);
}
MethodInfo create = null;
lock (_LambdaFactories) {
if (!_LambdaFactories.TryGetValue(delegateType, out fastPath)) {
create = typeof(Expression<>).MakeGenericType(delegateType).GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic);
if (TypeUtils.CanCache(delegateType)) {
_LambdaFactories[delegateType] = fastPath = (LambdaFactory)Delegate.CreateDelegate(typeof(LambdaFactory), create);
}
}
}
if (fastPath != null) {
return fastPath(body, name, tailCall, parameters);
}
Debug.Assert(create != null);
return (LambdaExpression)create.Invoke(null, new object[] { body, name, tailCall, parameters });
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters) {
return Lambda<TDelegate>(body, false, (IEnumerable<ParameterExpression>)parameters);
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, bool tailCall, params ParameterExpression[] parameters) {
return Lambda<TDelegate>(body, tailCall, (IEnumerable<ParameterExpression>)parameters);
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, IEnumerable<ParameterExpression> parameters) {
return Lambda<TDelegate>(body, null, false, parameters);
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, bool tailCall, IEnumerable<ParameterExpression> parameters) {
return Lambda<TDelegate>(body, null, tailCall, parameters);
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="name">The name of the lambda. Used for generating debugging info.</param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, String name, IEnumerable<ParameterExpression> parameters) {
return Lambda<TDelegate>(body, name, false, parameters);
}
/// <summary>
/// Creates an <see cref="Expression{TDelegate}"/> where the delegate type is known at compile time.
/// </summary>
/// <typeparam name="TDelegate">The delegate type. </typeparam>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="name">The name of the lambda. Used for generating debugging info.</param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <returns>An <see cref="Expression{TDelegate}"/> that has the <see cref="P:NodeType"/> property equal to <see cref="P:Lambda"/> and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, String name, bool tailCall, IEnumerable<ParameterExpression> parameters) {
var parameterList = parameters.ToReadOnly();
ValidateLambdaArgs(typeof(TDelegate), ref body, parameterList);
return new Expression<TDelegate>(body, name, tailCall, parameterList);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, params ParameterExpression[] parameters) {
return Lambda(body, false, (IEnumerable<ParameterExpression>)parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, bool tailCall, params ParameterExpression[] parameters) {
return Lambda(body, tailCall, (IEnumerable<ParameterExpression>)parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, IEnumerable<ParameterExpression> parameters) {
return Lambda(body, null, false, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, bool tailCall, IEnumerable<ParameterExpression> parameters) {
return Lambda(body, null, tailCall, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[] parameters) {
return Lambda(delegateType, body, null, false, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An array that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, bool tailCall, params ParameterExpression[] parameters) {
return Lambda(delegateType, body, null, tailCall, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, IEnumerable<ParameterExpression> parameters) {
return Lambda(delegateType, body, null, false, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, bool tailCall, IEnumerable<ParameterExpression> parameters) {
return Lambda(delegateType, body, null, tailCall, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="name">The name for the lambda. Used for emitting debug information.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, string name, IEnumerable<ParameterExpression> parameters) {
return Lambda(body, name, false, parameters);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="name">The name for the lambda. Used for emitting debug information.</param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Expression body, string name, bool tailCall, IEnumerable<ParameterExpression> parameters) {
ContractUtils.RequiresNotNull(body, "body");
var parameterList = parameters.ToReadOnly();
int paramCount = parameterList.Count;
Type[] typeArgs = new Type[paramCount + 1];
if (paramCount > 0) {
var set = new Set<ParameterExpression>(parameterList.Count);
for (int i = 0; i < paramCount; i++) {
var param = parameterList[i];
ContractUtils.RequiresNotNull(param, "parameter");
typeArgs[i] = param.IsByRef ? param.Type.MakeByRefType() : param.Type;
if (set.Contains(param)) {
throw Error.DuplicateVariable(param);
}
set.Add(param);
}
}
typeArgs[paramCount] = body.Type;
Type delegateType = DelegateHelpers.MakeDelegateType(typeArgs);
return CreateLambda(delegateType, body, name, tailCall, parameterList);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <param name="name">The name for the lambda. Used for emitting debug information.</param>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, string name, IEnumerable<ParameterExpression> parameters) {
var paramList = parameters.ToReadOnly();
ValidateLambdaArgs(delegateType, ref body, paramList);
return CreateLambda(delegateType, body, name, false, paramList);
}
/// <summary>
/// Creates a LambdaExpression by first constructing a delegate type.
/// </summary>
/// <param name="delegateType">A <see cref="Type"/> representing the delegate signature for the lambda.</param>
/// <param name="body">An <see cref="Expression"/> to set the <see cref="P:Body"/> property equal to. </param>
/// <param name="name">The name for the lambda. Used for emitting debug information.</param>
/// <param name="tailCall">A <see cref="Boolean"/> that indicates if tail call optimization will be applied when compiling the created expression. </param>
/// <param name="parameters">An <see cref="IEnumerable{T}"/> that contains <see cref="ParameterExpression"/> objects to use to populate the <see cref="P:Parameters"/> collection. </param>
/// <returns>A <see cref="LambdaExpression"/> that has the <see cref="P:NodeType"/> property equal to Lambda and the <see cref="P:Body"/> and <see cref="P:Parameters"/> properties set to the specified values.</returns>
public static LambdaExpression Lambda(Type delegateType, Expression body, string name, bool tailCall, IEnumerable<ParameterExpression> parameters) {
var paramList = parameters.ToReadOnly();
ValidateLambdaArgs(delegateType, ref body, paramList);
return CreateLambda(delegateType, body, name, tailCall, paramList);
}
private static void ValidateLambdaArgs(Type delegateType, ref Expression body, ReadOnlyCollection<ParameterExpression> parameters) {
ContractUtils.RequiresNotNull(delegateType, "delegateType");
RequiresCanRead(body, "body");
if (!typeof(Delegate).IsAssignableFrom(delegateType) || delegateType == typeof(Delegate)) {
throw Error.LambdaTypeMustBeDerivedFromSystemDelegate();
}
MethodInfo mi;
lock (_LambdaDelegateCache) {
if (!_LambdaDelegateCache.TryGetValue(delegateType, out mi)) {
mi = delegateType.GetMethod("Invoke");
if (TypeUtils.CanCache(delegateType)) {
_LambdaDelegateCache[delegateType] = mi;
}
}
}
ParameterInfo[] pis = mi.GetParametersCached();
if (pis.Length > 0) {
if (pis.Length != parameters.Count) {
throw Error.IncorrectNumberOfLambdaDeclarationParameters();
}
var set = new Set<ParameterExpression>(pis.Length);
for (int i = 0, n = pis.Length; i < n; i++) {
ParameterExpression pex = parameters[i];
ParameterInfo pi = pis[i];
RequiresCanRead(pex, "parameters");
Type pType = pi.ParameterType;
if (pex.IsByRef) {
if (!pType.IsByRef) {
//We cannot pass a parameter of T& to a delegate that takes T or any non-ByRef type.
throw Error.ParameterExpressionNotValidAsDelegate(pex.Type.MakeByRefType(), pType);
}
pType = pType.GetElementType();
}
if (!TypeUtils.AreReferenceAssignable(pex.Type, pType)) {
throw Error.ParameterExpressionNotValidAsDelegate(pex.Type, pType);
}
if (set.Contains(pex)) {
throw Error.DuplicateVariable(pex);
}
set.Add(pex);
}
} else if (parameters.Count > 0) {
throw Error.IncorrectNumberOfLambdaDeclarationParameters();
}
if (mi.ReturnType != typeof(void) && !TypeUtils.AreReferenceAssignable(mi.ReturnType, body.Type)) {
if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), mi.ReturnType) && mi.ReturnType.IsAssignableFrom(body.GetType())) {
body = Expression.Quote(body);
} else {
throw Error.ExpressionTypeDoesNotMatchReturn(body.Type, mi.ReturnType);
}
}
}
private static bool ValidateTryGetFuncActionArgs(Type[] typeArgs) {
if (typeArgs == null) {
throw new ArgumentNullException("typeArgs");
}
for (int i = 0, n = typeArgs.Length; i < n; i++) {
var a = typeArgs[i];
if (a == null) {
throw new ArgumentNullException("typeArgs");
}
if (a.IsByRef) {
return false;
}
}
return true;
}
/// <summary>
/// Creates a <see cref="Type"/> object that represents a generic System.Func delegate type that has specific type arguments.
/// The last type argument specifies the return type of the created delegate.
/// </summary>
/// <param name="typeArgs">An array of Type objects that specify the type arguments for the System.Func delegate type.</param>
/// <returns>The type of a System.Func delegate that has the specified type arguments.</returns>
public static Type GetFuncType(params Type[] typeArgs) {
if (!ValidateTryGetFuncActionArgs(typeArgs)) throw Error.TypeMustNotBeByRef();
Type result = DelegateHelpers.GetFuncType(typeArgs);
if (result == null) {
throw Error.IncorrectNumberOfTypeArgsForFunc();
}
return result;
}
/// <summary>
/// Creates a <see cref="Type"/> object that represents a generic System.Func delegate type that has specific type arguments.
/// The last type argument specifies the return type of the created delegate.
/// </summary>
/// <param name="typeArgs">An array of Type objects that specify the type arguments for the System.Func delegate type.</param>
/// <param name="funcType">When this method returns, contains the generic System.Func delegate type that has specific type arguments. Contains null if there is no generic System.Func delegate that matches the <paramref name="typeArgs"/>.This parameter is passed uninitialized.</param>
/// <returns>true if generic System.Func delegate type was created for specific <paramref name="typeArgs"/>; false otherwise.</returns>
public static bool TryGetFuncType(Type[] typeArgs, out Type funcType) {
if (ValidateTryGetFuncActionArgs(typeArgs)) {
return (funcType = DelegateHelpers.GetFuncType(typeArgs)) != null;
}
funcType = null;
return false;
}
/// <summary>
/// Creates a <see cref="Type"/> object that represents a generic System.Action delegate type that has specific type arguments.
/// </summary>
/// <param name="typeArgs">An array of Type objects that specify the type arguments for the System.Action delegate type.</param>
/// <returns>The type of a System.Action delegate that has the specified type arguments.</returns>
public static Type GetActionType(params Type[] typeArgs) {
if (!ValidateTryGetFuncActionArgs(typeArgs)) throw Error.TypeMustNotBeByRef();
Type result = DelegateHelpers.GetActionType(typeArgs);
if (result == null) {
throw Error.IncorrectNumberOfTypeArgsForAction();
}
return result;
}
/// <summary>
/// Creates a <see cref="Type"/> object that represents a generic System.Action delegate type that has specific type arguments.
/// </summary>
/// <param name="typeArgs">An array of Type objects that specify the type arguments for the System.Action delegate type.</param>
/// <param name="actionType">When this method returns, contains the generic System.Action delegate type that has specific type arguments. Contains null if there is no generic System.Action delegate that matches the <paramref name="typeArgs"/>.This parameter is passed uninitialized.</param>
/// <returns>true if generic System.Action delegate type was created for specific <paramref name="typeArgs"/>; false otherwise.</returns>
public static bool TryGetActionType(Type[] typeArgs, out Type actionType) {
if (ValidateTryGetFuncActionArgs(typeArgs)) {
return (actionType = DelegateHelpers.GetActionType(typeArgs)) != null;
}
actionType = null;
return false;
}
/// <summary>
/// Gets a <see cref="Type"/> object that represents a generic System.Func or System.Action delegate type that has specific type arguments.
/// The last type argument determines the return type of the delegate. If no Func or Action is large enough, it will generate a custom
/// delegate type.
/// </summary>
/// <param name="typeArgs">The type arguments of the delegate.</param>
/// <returns>The delegate type.</returns>
/// <remarks>
/// As with Func, the last argument is the return type. It can be set
/// to System.Void to produce an Action.</remarks>
public static Type GetDelegateType(params Type[] typeArgs) {
ContractUtils.RequiresNotEmpty(typeArgs, "typeArgs");
ContractUtils.RequiresNotNullItems(typeArgs, "typeArgs");
return DelegateHelpers.MakeDelegateType(typeArgs);
}
}
}
|