Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / System.Activities / System / Activities / ExpressionUtilities.cs / 1305376 / ExpressionUtilities.cs
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities
{
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
static class ExpressionUtilities
{
public static ParameterExpression RuntimeContextParameter = Expression.Parameter(typeof(ActivityContext), "context");
static Assembly linqAssembly = typeof(Func<>).Assembly;
static MethodInfo createLocationFactoryGenericMethod = typeof(ExpressionUtilities).GetMethod("CreateLocationFactory");
static MethodInfo propertyDescriptorGetValue; // PropertyDescriptor.GetValue
// Types cached for use in TryRewriteLambdaExpression
static Type inArgumentGenericType = typeof(InArgument<>);
static Type outArgumentGenericType = typeof(OutArgument<>);
static Type inOutArgumentGenericType = typeof(InOutArgument<>);
static Type variableGenericType = typeof(Variable<>);
static Type delegateInArgumentGenericType = typeof(DelegateInArgument<>);
static Type delegateOutArgumentGenericType = typeof(DelegateOutArgument<>);
static Type activityContextType = typeof(ActivityContext);
static Type locationReferenceType = typeof(LocationReference);
static Type runtimeArgumentType = typeof(RuntimeArgument);
static Type argumentType = typeof(Argument);
static Type variableType = typeof(Variable);
static Type delegateArgumentType = typeof(DelegateArgument);
// MethodInfos cached for use in TryRewriteLambdaExpression
static MethodInfo activityContextGetValueGenericMethod = typeof(ActivityContext).GetMethod("GetValue", new Type[] { typeof(LocationReference) });
static MethodInfo activityContextGetLocationGenericMethod = typeof(ActivityContext).GetMethod("GetLocation", new Type[] { typeof(LocationReference) });
static MethodInfo locationReferenceGetLocationMethod = typeof(LocationReference).GetMethod("GetLocation", new Type[] { typeof(ActivityContext) });
static MethodInfo argumentGetLocationMethod = typeof(Argument).GetMethod("GetLocation", new Type[] { typeof(ActivityContext) });
static MethodInfo variableGetMethod = typeof(Variable).GetMethod("Get", new Type[] { typeof(ActivityContext) });
static MethodInfo delegateArgumentGetMethod = typeof(DelegateArgument).GetMethod("Get", new Type[] { typeof(ActivityContext) });
static MethodInfo PropertyDescriptorGetValue
{
get
{
if (propertyDescriptorGetValue == null)
{
propertyDescriptorGetValue = typeof(PropertyDescriptor).GetMethod("GetValue");
}
return propertyDescriptorGetValue;
}
}
public static Expression CreateIdentifierExpression(LocationReference locationReference)
{
return Expression.Call(RuntimeContextParameter, activityContextGetValueGenericMethod.MakeGenericMethod(locationReference.Type), Expression.Constant(locationReference));
}
public static bool IsLocation(LambdaExpression expression, Type targetType, out string extraErrorMessage)
{
extraErrorMessage = null;
Expression body = expression.Body;
if (targetType != null && body.Type != targetType)
{
// eg) LambdaReference((env) => strVar.Get(env))
// you can have an expressionTree whose LambdaExpression.ReturnType == IComparable,
// while its LambdaExpression.Body.Type == String
// and not ever have Convert node in the tree.
extraErrorMessage = SR.MustMatchReferenceExpressionReturnType;
return false;
}
switch (body.NodeType)
{
case ExpressionType.ArrayIndex:
return true;
case ExpressionType.MemberAccess:
// This also handles variables, which are emitted as "context.GetLocation("v").Value"
MemberExpression memberExpression = (MemberExpression)body;
MemberTypes memberType = memberExpression.Member.MemberType;
if (memberType == MemberTypes.Field)
{
FieldInfo fieldInfo = (FieldInfo)memberExpression.Member;
if (fieldInfo.IsInitOnly)
{
// readOnly field
return false;
}
return true;
}
else if (memberType == MemberTypes.Property)
{
PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
if (!propertyInfo.CanWrite)
{
// no Setter
return false;
}
return true;
}
break;
case ExpressionType.Call:
// Depends on the method being called.
// System.Array.Get --> multi-dimensional array
// get_Item --> might be an indexer property if it's special name & default etc.
MethodCallExpression callExpression = (MethodCallExpression)body;
MethodInfo method = callExpression.Method;
Type declaringType = method.DeclaringType;
if (declaringType.BaseType == TypeHelper.ArrayType && method.Name == "Get")
{
return true;
}
else if (method.IsSpecialName && method.Name.StartsWith("get_", StringComparison.Ordinal))
{
return true;
}
else if (method.Name == "GetValue" && declaringType == activityContextType)
{
return true;
}
else if (method.Name == "Get" && declaringType.IsGenericType)
{
Type declaringTypeGenericDefinition = declaringType.GetGenericTypeDefinition();
if (declaringTypeGenericDefinition == inOutArgumentGenericType ||
declaringTypeGenericDefinition == outArgumentGenericType)
{
return true;
}
}
break;
case ExpressionType.Convert:
// a would-be-valid Location expression that is type converted is treated invalid
extraErrorMessage = SR.MustMatchReferenceExpressionReturnType;
return false;
}
return false;
}
public static LocationFactory CreateLocationFactory(LambdaExpression expression)
{
Expression body = expression.Body;
switch (body.NodeType)
{
case ExpressionType.ArrayIndex:
return new ArrayLocationFactory(expression);
case ExpressionType.MemberAccess:
// This also handles variables, which are emitted as "context.GetLocation("v").Value"
MemberTypes memberType = ((MemberExpression)body).Member.MemberType;
if (memberType == MemberTypes.Field)
{
return new FieldLocationFactory(expression);
}
else if (memberType == MemberTypes.Property)
{
return new PropertyLocationFactory(expression);
}
else
{
throw FxTrace.Exception.AsError(new NotSupportedException("Lvalues of member type " + memberType));
}
case ExpressionType.Call:
// Depends on the method being called.
// System.Array.Get --> multi-dimensional array
// get_Item --> might be an indexer property if it's special name & default etc.
MethodCallExpression callExpression = (MethodCallExpression)body;
MethodInfo method = callExpression.Method;
Type declaringType = method.DeclaringType;
if (declaringType.BaseType == TypeHelper.ArrayType && method.Name == "Get")
{
return new MultidimensionalArrayLocationFactory(expression);
}
else if (method.IsSpecialName && method.Name.StartsWith("get_", StringComparison.Ordinal))
{
return new IndexerLocationFactory(expression);
}
else if (method.Name == "GetValue" && declaringType == activityContextType)
{
return new LocationReferenceFactory(callExpression.Arguments[0], expression.Parameters);
}
else if (method.Name == "Get" && declaringType.IsGenericType)
{
Type declaringTypeGenericDefinition = declaringType.GetGenericTypeDefinition();
if (declaringTypeGenericDefinition == inOutArgumentGenericType ||
declaringTypeGenericDefinition == outArgumentGenericType)
{
return new ArgumentFactory(callExpression.Object, expression.Parameters);
}
}
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExpressionForLocation(body.NodeType)));
default:
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExpressionForLocation(body.NodeType)));
}
}
static LocationFactory CreateParentReference(Expression expression, ReadOnlyCollection lambdaParameters)
{
// create a LambdaExpression to get access to the expression
int parameterCount = lambdaParameters.Count;
Type genericFuncType = linqAssembly.GetType("System.Func`" + (parameterCount + 1), true);
Type[] delegateParameterTypes = new Type[parameterCount + 1];
for (int i = 0; i < parameterCount; ++i)
{
delegateParameterTypes[i] = lambdaParameters[i].Type;
}
delegateParameterTypes[parameterCount] = expression.Type;
Type funcType = genericFuncType.MakeGenericType(delegateParameterTypes);
LambdaExpression parentLambda = Expression.Lambda(funcType, expression, lambdaParameters);
// call CreateLocationFactory(parentLambda);
MethodInfo typedMethod = createLocationFactoryGenericMethod.MakeGenericMethod(expression.Type);
return (LocationFactory)typedMethod.Invoke(null, new object[] { parentLambda });
}
static Func Compile(Expression objectExpression, ReadOnlyCollection parametersCollection)
{
ParameterExpression[] parameters = null;
if (parametersCollection != null)
{
parameters = parametersCollection.ToArray();
}
Expression> objectLambda = Expression.Lambda>(objectExpression, parameters);
return objectLambda.Compile();
}
static T Evaluate(Expression objectExpression, ReadOnlyCollection parametersCollection, ActivityContext context)
{
Func objectFunc = Compile(objectExpression, parametersCollection);
return objectFunc(context);
}
// for single-dimensional arrays
class ArrayLocationFactory : LocationFactory
{
Func arrayFunction;
Func indexFunction;
public ArrayLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.ArrayIndex, "ArrayIndex expression required");
BinaryExpression arrayIndexExpression = (BinaryExpression)expression.Body;
this.arrayFunction = ExpressionUtilities.Compile(arrayIndexExpression.Left, expression.Parameters);
this.indexFunction = ExpressionUtilities.Compile(arrayIndexExpression.Right, expression.Parameters);
}
public override Location CreateLocation(ActivityContext context)
{
return new ArrayLocation(this.arrayFunction(context), this.indexFunction(context));
}
[DataContract]
class ArrayLocation : Location
{
[DataMember]
T[] array;
[DataMember(EmitDefaultValue = false)]
int index;
public ArrayLocation(T[] array, int index)
: base()
{
this.array = array;
this.index = index;
}
public override T Value
{
get
{
return this.array[this.index];
}
set
{
this.array[this.index] = value;
}
}
}
}
class FieldLocationFactory : LocationFactory
{
FieldInfo fieldInfo;
Func ownerFunction;
LocationFactory parentFactory;
public FieldLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.MemberAccess, "field expression required");
MemberExpression memberExpression = (MemberExpression)expression.Body;
Fx.Assert(memberExpression.Member.MemberType == MemberTypes.Field, "member field expected");
this.fieldInfo = (FieldInfo)memberExpression.Member;
if (this.fieldInfo.IsStatic)
{
this.ownerFunction = null;
}
else
{
this.ownerFunction = ExpressionUtilities.Compile