|
//-----------------------------------------------------------------------------
// 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;
using System.Activities.XamlIntegration;
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, typeof(LocationReference)));
}
// If we ever expand the depth to which we'll look through an expression for a location,
// then we also need to update the depth to which isLocationExpression is propagated in
// ExpressionUtilities.TryRewriteLambdaExpression and VisualBasicHelper.Rewrite.
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<IComparable>((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<T>("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<T> CreateLocationFactory<T>(LambdaExpression expression)
{
Expression body = expression.Body;
switch (body.NodeType)
{
case ExpressionType.ArrayIndex:
return new ArrayLocationFactory<T>(expression);
case ExpressionType.MemberAccess:
// This also handles variables, which are emitted as "context.GetLocation<T>("v").Value"
MemberTypes memberType = ((MemberExpression)body).Member.MemberType;
if (memberType == MemberTypes.Field)
{
return new FieldLocationFactory<T>(expression);
}
else if (memberType == MemberTypes.Property)
{
return new PropertyLocationFactory<T>(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<T>(expression);
}
else if (method.IsSpecialName && method.Name.StartsWith("get_", StringComparison.Ordinal))
{
return new IndexerLocationFactory<T>(expression);
}
else if (method.Name == "GetValue" && declaringType == activityContextType)
{
return new LocationReferenceFactory<T>(callExpression.Arguments[0], expression.Parameters);
}
else if (method.Name == "Get" && declaringType.IsGenericType)
{
Type declaringTypeGenericDefinition = declaringType.GetGenericTypeDefinition();
if (declaringTypeGenericDefinition == inOutArgumentGenericType ||
declaringTypeGenericDefinition == outArgumentGenericType)
{
return new ArgumentFactory<T>(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)));
}
}
internal static bool TryGetInlinedReference(CodeActivityPublicEnvironmentAccessor publicAccessor, LocationReference originalReference,
bool isLocationExpression, out LocationReference inlinedReference)
{
if (isLocationExpression)
{
return publicAccessor.TryGetReferenceToPublicLocation(originalReference, true, out inlinedReference);
}
else
{
return publicAccessor.TryGetAccessToPublicLocation(originalReference, ArgumentDirection.In, true, out inlinedReference);
}
}
static LocationFactory CreateParentReference(Expression expression, ReadOnlyCollection<ParameterExpression> 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.Type>(parentLambda);
MethodInfo typedMethod = createLocationFactoryGenericMethod.MakeGenericMethod(expression.Type);
return (LocationFactory)typedMethod.Invoke(null, new object[] { parentLambda });
}
static Func<ActivityContext, T> Compile<T>(Expression objectExpression, ReadOnlyCollection<ParameterExpression> parametersCollection)
{
ParameterExpression[] parameters = null;
if (parametersCollection != null)
{
parameters = parametersCollection.ToArray<ParameterExpression>();
}
Expression<Func<ActivityContext, T>> objectLambda = Expression.Lambda<Func<ActivityContext, T>>(objectExpression, parameters);
return objectLambda.Compile();
}
static T Evaluate<T>(Expression objectExpression, ReadOnlyCollection<ParameterExpression> parametersCollection, ActivityContext context)
{
Func<ActivityContext, T> objectFunc = Compile<T>(objectExpression, parametersCollection);
return objectFunc(context);
}
// for single-dimensional arrays
class ArrayLocationFactory<T> : LocationFactory<T>
{
Func<ActivityContext, T[]> arrayFunction;
Func<ActivityContext, int> indexFunction;
public ArrayLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.ArrayIndex, "ArrayIndex expression required");
BinaryExpression arrayIndexExpression = (BinaryExpression)expression.Body;
this.arrayFunction = ExpressionUtilities.Compile<T[]>(arrayIndexExpression.Left, expression.Parameters);
this.indexFunction = ExpressionUtilities.Compile<int>(arrayIndexExpression.Right, expression.Parameters);
}
public override Location<T> CreateLocation(ActivityContext context)
{
return new ArrayLocation(this.arrayFunction(context), this.indexFunction(context));
}
[DataContract]
internal class ArrayLocation : Location<T>
{
T[] array;
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;
}
}
[DataMember(Name = "array")]
internal T[] SerializedArray
{
get { return this.array; }
set { this.array = value; }
}
[DataMember(EmitDefaultValue = false, Name = "index")]
internal int SerializedIndex
{
get { return this.index; }
set { this.index = value; }
}
}
}
class FieldLocationFactory<T> : LocationFactory<T>
{
FieldInfo fieldInfo;
Func<ActivityContext, object> 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<object>(
Expression.Convert(memberExpression.Expression, TypeHelper.ObjectType), expression.Parameters);
}
if (this.fieldInfo.DeclaringType.IsValueType)
{
// may want to set a struct, so we need to make an expression in order to set the parent
parentFactory = CreateParentReference(memberExpression.Expression, expression.Parameters);
}
}
public override Location<T> CreateLocation(ActivityContext context)
{
object owner = null;
if (this.ownerFunction != null)
{
owner = this.ownerFunction(context);
}
Location parent = null;
if (parentFactory != null)
{
parent = parentFactory.CreateLocation(context);
}
return new FieldLocation(this.fieldInfo, owner, parent);
}
[DataContract]
internal class FieldLocation : Location<T>
{
FieldInfo fieldInfo;
object owner;
Location parent;
public FieldLocation(FieldInfo fieldInfo, object owner, Location parent)
: base()
{
this.fieldInfo = fieldInfo;
this.owner = owner;
this.parent = parent;
}
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotRaiseReservedExceptionTypes,
Justification = "Need to raise NullReferenceException to match expected failure case in workflows.")]
public override T Value
{
get
{
if (this.owner == null && !this.fieldInfo.IsStatic)
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.fieldInfo.Name)));
}
return (T)this.fieldInfo.GetValue(this.owner);
}
set
{
if (this.owner == null && !this.fieldInfo.IsStatic)
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.fieldInfo.Name)));
}
this.fieldInfo.SetValue(this.owner, value);
if (this.parent != null)
{
// Looks like we are trying to set a field on a struct
// Calling SetValue simply sets the field on the local copy of the struct, which is not very helpful
// Since we have a copy, assign it back to the parent
this.parent.Value = this.owner;
}
}
}
[DataMember(Name = "fieldInfo")]
internal FieldInfo SerializedFieldInfo
{
get { return this.fieldInfo; }
set { this.fieldInfo = value; }
}
[DataMember(EmitDefaultValue = false, Name = "owner")]
internal object SerializedOwner
{
get { return this.owner; }
set { this.owner = value; }
}
[DataMember(EmitDefaultValue = false, Name = "parent")]
internal Location SerializedParent
{
get { return this.parent; }
set { this.parent = value; }
}
}
}
class ArgumentFactory<T> : LocationFactory<T>
{
Func<ActivityContext, Argument> argumentFunction;
public ArgumentFactory(Expression argumentExpression, ReadOnlyCollection<ParameterExpression> expressionParameters)
{
this.argumentFunction = ExpressionUtilities.Compile<Argument>(argumentExpression, expressionParameters);
}
public override Location<T> CreateLocation(ActivityContext context)
{
Argument argument = this.argumentFunction(context);
return argument.RuntimeArgument.GetLocation(context) as Location<T>;
}
}
class LocationReferenceFactory<T> : LocationFactory<T>
{
Func<ActivityContext, LocationReference> locationReferenceFunction;
public LocationReferenceFactory(Expression locationReferenceExpression, ReadOnlyCollection<ParameterExpression> expressionParameters)
{
this.locationReferenceFunction = ExpressionUtilities.Compile<LocationReference>(locationReferenceExpression, expressionParameters);
}
public override Location<T> CreateLocation(ActivityContext context)
{
LocationReference locationReference = this.locationReferenceFunction(context);
return locationReference.GetLocation(context) as Location<T>;
}
}
class IndexerLocationFactory<T> : LocationFactory<T>
{
MethodInfo getItemMethod;
string indexerName;
MethodInfo setItemMethod;
Func<ActivityContext, object>[] setItemArgumentFunctions;
Func<ActivityContext, object> targetObjectFunction;
public IndexerLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.Call, "Call expression required.");
MethodCallExpression callExpression = (MethodCallExpression)expression.Body;
this.getItemMethod = callExpression.Method;
Fx.Assert(this.getItemMethod.IsSpecialName && this.getItemMethod.Name.StartsWith("get_", StringComparison.Ordinal), "Special get_Item method required.");
// Get the set_Item accessor for the same set of parameter/return types if any.
this.indexerName = this.getItemMethod.Name.Substring(4);
string setItemName = "set_" + this.indexerName;
ParameterInfo[] getItemParameters = this.getItemMethod.GetParameters();
Type[] setItemParameterTypes = new Type[getItemParameters.Length + 1];
for (int i = 0; i < getItemParameters.Length; i++)
{
setItemParameterTypes[i] = getItemParameters[i].ParameterType;
}
setItemParameterTypes[getItemParameters.Length] = this.getItemMethod.ReturnType;
this.setItemMethod = this.getItemMethod.DeclaringType.GetMethod(
setItemName, BindingFlags.Public | BindingFlags.Instance, null, setItemParameterTypes, null);
if (this.setItemMethod != null)
{
// Get the target object and all the setter's arguments
// (minus the actual value to be set).
this.targetObjectFunction = ExpressionUtilities.Compile<object>(callExpression.Object, expression.Parameters);
this.setItemArgumentFunctions = new Func<ActivityContext, object>[callExpression.Arguments.Count];
for (int i = 0; i < callExpression.Arguments.Count; i++)
{
// convert value types to objects since Linq doesn't do it automatically
Expression argument = callExpression.Arguments[i];
if (argument.Type.IsValueType)
{
argument = Expression.Convert(argument, TypeHelper.ObjectType);
}
this.setItemArgumentFunctions[i] = ExpressionUtilities.Compile<object>(argument, expression.Parameters);
}
}
}
public override Location<T> CreateLocation(ActivityContext context)
{
object targetObject = null;
object[] setItemArguments = null;
if (this.setItemMethod != null)
{
targetObject = this.targetObjectFunction(context);
setItemArguments = new object[this.setItemArgumentFunctions.Length];
for (int i = 0; i < this.setItemArgumentFunctions.Length; i++)
{
setItemArguments[i] = this.setItemArgumentFunctions[i](context);
}
}
return new IndexerLocation(this.indexerName, this.getItemMethod, this.setItemMethod, targetObject, setItemArguments);
}
[DataContract]
internal class IndexerLocation : Location<T>
{
string indexerName;
MethodInfo getItemMethod;
MethodInfo setItemMethod;
object targetObject;
object[] setItemArguments;
public IndexerLocation(string indexerName, MethodInfo getItemMethod, MethodInfo setItemMethod,
object targetObject, object[] getItemArguments)
: base()
{
this.indexerName = indexerName;
this.getItemMethod = getItemMethod;
this.setItemMethod = setItemMethod;
this.targetObject = targetObject;
this.setItemArguments = getItemArguments;
}
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotRaiseReservedExceptionTypes,
Justification = "Need to raise NullReferenceException to match expected failure case in workflows.")]
public override T Value
{
get
{
if (this.targetObject == null && !this.getItemMethod.IsStatic)
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.getItemMethod.Name)));
}
return (T)this.getItemMethod.Invoke(this.targetObject, this.setItemArguments);
}
set
{
if (this.setItemMethod == null)
{
string targetObjectTypeName = this.targetObject.GetType().Name;
throw FxTrace.Exception.AsError(new InvalidOperationException(
SR.MissingSetAccessorForIndexer(this.indexerName, targetObjectTypeName)));
}
if (this.targetObject == null && !this.setItemMethod.IsStatic)
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.setItemMethod.Name)));
}
object[] localSetItemArguments = new object[this.setItemArguments.Length + 1];
Array.ConstrainedCopy(this.setItemArguments, 0, localSetItemArguments, 0, this.setItemArguments.Length);
localSetItemArguments[localSetItemArguments.Length - 1] = value;
this.setItemMethod.Invoke(this.targetObject, localSetItemArguments);
}
}
[DataMember(Name = "indexerName")]
internal string SerializedIndexerName
{
get { return this.indexerName; }
set { this.indexerName = value; }
}
[DataMember(EmitDefaultValue = false, Name = "getItemMethod")]
internal MethodInfo SerializedGetItemMethod
{
get { return this.getItemMethod; }
set { this.getItemMethod = value; }
}
[DataMember(EmitDefaultValue = false, Name = "setItemMethod")]
internal MethodInfo SerializedSetItemMethod
{
get { return this.setItemMethod; }
set { this.setItemMethod = value; }
}
[DataMember(EmitDefaultValue = false, Name = "targetObject")]
internal object SerializedTargetObject
{
get { return this.targetObject; }
set { this.targetObject = value; }
}
[DataMember(EmitDefaultValue = false, Name = "setItemArguments")]
internal object[] SerializedSetItemArguments
{
get { return this.setItemArguments; }
set { this.setItemArguments = value; }
}
}
}
class MultidimensionalArrayLocationFactory<T> : LocationFactory<T>
{
Func<ActivityContext, Array> arrayFunction;
Func<ActivityContext, int>[] indexFunctions;
public MultidimensionalArrayLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.Call, "Call expression required.");
MethodCallExpression callExpression = (MethodCallExpression)expression.Body;
this.arrayFunction = ExpressionUtilities.Compile<Array>(
callExpression.Object, expression.Parameters);
this.indexFunctions = new Func<ActivityContext, int>[callExpression.Arguments.Count];
for (int i = 0; i < this.indexFunctions.Length; i++)
{
this.indexFunctions[i] = ExpressionUtilities.Compile<int>(
callExpression.Arguments[i], expression.Parameters);
}
}
public override Location<T> CreateLocation(ActivityContext context)
{
int[] indices = new int[this.indexFunctions.Length];
for (int i = 0; i < indices.Length; i++)
{
indices[i] = this.indexFunctions[i](context);
}
return new MultidimensionalArrayLocation(this.arrayFunction(context), indices);
}
[DataContract]
internal class MultidimensionalArrayLocation : Location<T>
{
Array array;
int[] indices;
public MultidimensionalArrayLocation(Array array, int[] indices)
: base()
{
this.array = array;
this.indices = indices;
}
public override T Value
{
get
{
return (T)this.array.GetValue(this.indices);
}
set
{
this.array.SetValue(value, this.indices);
}
}
[DataMember(Name = "array")]
internal Array SerializedArray
{
get { return this.array; }
set { this.array = value; }
}
[DataMember(Name = "indices")]
internal int[] SerializedIndicess
{
get { return this.indices; }
set { this.indices = value; }
}
}
}
class PropertyLocationFactory<T> : LocationFactory<T>
{
Func<ActivityContext, object> ownerFunction;
PropertyInfo propertyInfo;
LocationFactory parentFactory;
public PropertyLocationFactory(LambdaExpression expression)
{
Fx.Assert(expression.Body.NodeType == ExpressionType.MemberAccess, "member access expression required");
MemberExpression memberExpression = (MemberExpression)expression.Body;
Fx.Assert(memberExpression.Member.MemberType == MemberTypes.Property, "property access expression expected");
this.propertyInfo = (PropertyInfo)memberExpression.Member;
if (memberExpression.Expression == null)
{
// static property
this.ownerFunction = null;
}
else
{
this.ownerFunction = ExpressionUtilities.Compile<object>(
Expression.Convert(memberExpression.Expression, TypeHelper.ObjectType), expression.Parameters);
}
if (this.propertyInfo.DeclaringType.IsValueType)
{
// may want to set a struct, so we need to make an expression in order to set the parent
parentFactory = CreateParentReference(memberExpression.Expression, expression.Parameters);
}
}
public override Location<T> CreateLocation(ActivityContext context)
{
object owner = null;
if (this.ownerFunction != null)
{
owner = this.ownerFunction(context);
}
Location parent = null;
if (parentFactory != null)
{
parent = parentFactory.CreateLocation(context);
}
return new PropertyLocation(this.propertyInfo, owner, parent);
}
[DataContract]
internal class PropertyLocation : Location<T>
{
object owner;
PropertyInfo propertyInfo;
Location parent;
public PropertyLocation(PropertyInfo propertyInfo, object owner, Location parent)
: base()
{
this.propertyInfo = propertyInfo;
this.owner = owner;
this.parent = parent;
}
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotRaiseReservedExceptionTypes,
Justification = "Need to raise NullReferenceException to match expected failure case in workflows.")]
public override T Value
{
get
{
// Only allow access to public properties, EXCEPT that Locations are top-level variables
// from the other's perspective, not internal properties, so they're okay as a special case.
// E.g. "[N]" from the user's perspective is not accessing a nonpublic property, even though
// at an implementation level it is.
MethodInfo getMethodInfo = this.propertyInfo.GetGetMethod();
if (getMethodInfo == null && !TypeHelper.AreTypesCompatible(this.propertyInfo.DeclaringType, typeof(Location)))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WriteonlyPropertyCannotBeRead(this.propertyInfo.DeclaringType, this.propertyInfo.Name)));
}
if (this.owner == null && (getMethodInfo == null || !getMethodInfo.IsStatic))
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.propertyInfo.Name)));
}
// Okay, it's public
return (T)this.propertyInfo.GetValue(this.owner, null);
}
set
{
// Only allow access to public properties, EXCEPT that Locations are top-level variables
// from the other's perspective, not internal properties, so they're okay as a special case.
// E.g. "[N]" from the user's perspective is not accessing a nonpublic property, even though
// at an implementation level it is.
MethodInfo setMethodInfo = this.propertyInfo.GetSetMethod();
if (setMethodInfo == null && !TypeHelper.AreTypesCompatible(this.propertyInfo.DeclaringType, typeof(Location)))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ReadonlyPropertyCannotBeSet(this.propertyInfo.DeclaringType, this.propertyInfo.Name)));
}
if (this.owner == null && (setMethodInfo == null || !setMethodInfo.IsStatic))
{
throw FxTrace.Exception.AsError(new NullReferenceException(SR.CannotDereferenceNull(this.propertyInfo.Name)));
}
// Okay, it's public
this.propertyInfo.SetValue(this.owner, value, null);
if (this.parent != null)
{
// Looks like we are trying to set a property on a struct
// Calling SetValue simply sets the property on the local copy of the struct, which is not very helpful
// Since we have a copy, assign it back to the parent
this.parent.Value = this.owner;
}
}
}
[DataMember(EmitDefaultValue = false, Name = "owner")]
internal object SerializedOwner
{
get { return this.owner; }
set { this.owner = value; }
}
[DataMember(Name = "propertyInfo")]
internal PropertyInfo SerializedPropertyInfo
{
get { return this.propertyInfo; }
set { this.propertyInfo = value; }
}
[DataMember(EmitDefaultValue = false, Name = "parent")]
internal Location SerializedParent
{
get { return this.parent; }
set { this.parent = value; }
}
}
}
// Returns true if it changed the expression (newExpression != expression).
// If it returns false then newExpression is set equal to expression.
// This method uses the publicAccessor parameter to generate violations (workflow
// artifacts which are not visible) and to generate inline references
// (references at a higher scope which can be resolved at runtime).
public static bool TryRewriteLambdaExpression(Expression expression, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression = false)
{
newExpression = expression;
if (expression == null)
{
return false;
}
// Share some local declarations across the switch
Expression left = null;
Expression right = null;
Expression other = null;
bool hasChanged = false;
IList<Expression> expressionList = null;
IList<ElementInit> initializerList = null;
IList<MemberBinding> bindingList = null;
MethodCallExpression methodCall = null;
BinaryExpression binaryExpression = null;
NewArrayExpression newArray = null;
UnaryExpression unaryExpression = null;
switch (expression.NodeType)
{
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Coalesce:
case ExpressionType.Divide:
case ExpressionType.Equal:
case ExpressionType.ExclusiveOr:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LeftShift:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.Modulo:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.NotEqual:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.Power:
case ExpressionType.RightShift:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
binaryExpression = (BinaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Left, out left, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Right, out right, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Conversion, out other, publicAccessor);
if (hasChanged)
{
newExpression = Expression.MakeBinary(
binaryExpression.NodeType,
left,
right,
binaryExpression.IsLiftedToNull,
binaryExpression.Method,
(LambdaExpression)other);
}
break;
case ExpressionType.Conditional:
ConditionalExpression conditional = (ConditionalExpression)expression;
hasChanged |= TryRewriteLambdaExpression(conditional.Test, out other, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(conditional.IfTrue, out left, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(conditional.IfFalse, out right, publicAccessor);
if (hasChanged)
{
newExpression = Expression.Condition(
other,
left,
right);
}
break;
case ExpressionType.Constant:
break;
case ExpressionType.Invoke:
InvocationExpression invocation = (InvocationExpression)expression;
hasChanged |= TryRewriteLambdaExpression(invocation.Expression, out other, publicAccessor);
hasChanged |= TryRewriteLambdaExpressionCollection(invocation.Arguments, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.Invoke(
other,
expressionList);
}
break;
case ExpressionType.Lambda:
LambdaExpression lambda = (LambdaExpression)expression;
hasChanged |= TryRewriteLambdaExpression(lambda.Body, out other, publicAccessor, isLocationExpression);
if (hasChanged)
{
newExpression = Expression.Lambda(
lambda.Type,
other,
lambda.Parameters);
}
break;
case ExpressionType.ListInit:
ListInitExpression listInit = (ListInitExpression)expression;
hasChanged |= TryRewriteLambdaExpression(listInit.NewExpression, out other, publicAccessor);
hasChanged |= TryRewriteLambdaExpressionInitializersCollection(listInit.Initializers, out initializerList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.ListInit(
(NewExpression)other,
initializerList);
}
break;
case ExpressionType.Parameter:
break;
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression;
// When creating a location for a member on a struct, we also need a location
// for the struct (so we don't just set the member on a copy of the struct)
bool subTreeIsLocationExpression = isLocationExpression && memberExpression.Member.DeclaringType.IsValueType;
hasChanged |= TryRewriteLambdaExpression(memberExpression.Expression, out other, publicAccessor, subTreeIsLocationExpression);
if (hasChanged)
{
newExpression = Expression.MakeMemberAccess(
other,
memberExpression.Member);
}
break;
case ExpressionType.MemberInit:
MemberInitExpression memberInit = (MemberInitExpression)expression;
hasChanged |= TryRewriteLambdaExpression(memberInit.NewExpression, out other, publicAccessor);
hasChanged |= TryRewriteLambdaExpressionBindingsCollection(memberInit.Bindings, out bindingList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.MemberInit(
(NewExpression)other,
bindingList);
}
break;
case ExpressionType.ArrayIndex:
// ArrayIndex can be a MethodCallExpression or a BinaryExpression
methodCall = expression as MethodCallExpression;
if (methodCall != null)
{
hasChanged |= TryRewriteLambdaExpression(methodCall.Object, out other, publicAccessor);
hasChanged |= TryRewriteLambdaExpressionCollection(methodCall.Arguments, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.ArrayIndex(
other,
expressionList);
}
}
else
{
binaryExpression = (BinaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Left, out left, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Right, out right, publicAccessor);
if (hasChanged)
{
newExpression = Expression.ArrayIndex(
left,
right);
}
}
break;
case ExpressionType.Call:
methodCall = (MethodCallExpression)expression;
// TryRewriteMethodCall does all the real work
hasChanged = TryRewriteMethodCall(methodCall, out newExpression, publicAccessor, isLocationExpression);
break;
case ExpressionType.NewArrayInit:
newArray = (NewArrayExpression)expression;
hasChanged |= TryRewriteLambdaExpressionCollection(newArray.Expressions, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.NewArrayInit(
newArray.Type.GetElementType(),
expressionList);
}
break;
case ExpressionType.NewArrayBounds:
newArray = (NewArrayExpression)expression;
hasChanged |= TryRewriteLambdaExpressionCollection(newArray.Expressions, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.NewArrayBounds(
newArray.Type.GetElementType(),
expressionList);
}
break;
case ExpressionType.New:
NewExpression objectCreationExpression = (NewExpression)expression;
if (objectCreationExpression.Constructor == null)
{
// must be creating a valuetype
Fx.Assert(objectCreationExpression.Arguments.Count == 0, "NewExpression with null Constructor but some arguments");
}
else
{
hasChanged |= TryRewriteLambdaExpressionCollection(objectCreationExpression.Arguments, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = objectCreationExpression.Update(expressionList);
}
}
break;
case ExpressionType.TypeIs:
TypeBinaryExpression typeBinary = (TypeBinaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(typeBinary.Expression, out other, publicAccessor);
if (hasChanged)
{
newExpression = Expression.TypeIs(
other,
typeBinary.TypeOperand);
}
break;
case ExpressionType.ArrayLength:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
unaryExpression = (UnaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(unaryExpression.Operand, out left, publicAccessor);
if (hasChanged)
{
newExpression = Expression.MakeUnary(
unaryExpression.NodeType,
left,
unaryExpression.Type,
unaryExpression.Method);
}
break;
case ExpressionType.UnaryPlus:
unaryExpression = (UnaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(unaryExpression.Operand, out left, publicAccessor);
if (hasChanged)
{
newExpression = Expression.UnaryPlus(
left,
unaryExpression.Method);
}
break;
// Expression Tree V2.0 types. This is due to the hosted VB compiler generating ET V2.0 nodes.
case ExpressionType.Block:
BlockExpression block = (BlockExpression)expression;
hasChanged |= TryRewriteLambdaExpressionCollection(block.Expressions, out expressionList, publicAccessor);
if (hasChanged)
{
// Parameter collections are never rewritten
newExpression = Expression.Block(block.Variables, expressionList);
}
break;
case ExpressionType.Assign:
binaryExpression = (BinaryExpression)expression;
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Left, out left, publicAccessor);
hasChanged |= TryRewriteLambdaExpression(binaryExpression.Right, out right, publicAccessor);
if (hasChanged)
{
newExpression = Expression.Assign(left, right);
}
break;
}
return hasChanged;
}
static bool TryRewriteLambdaExpressionBindingsCollection(IList<MemberBinding> bindings, out IList<MemberBinding> newBindings, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
IList<MemberBinding> temporaryBindings = null;
for (int i = 0; i < bindings.Count; i++)
{
MemberBinding binding = bindings[i];
MemberBinding newBinding;
if (TryRewriteMemberBinding(binding, out newBinding, publicAccessor))
{
if (temporaryBindings == null)
{
// We initialize this list with the unchanged bindings
temporaryBindings = new List<MemberBinding>(bindings.Count);
for (int j = 0; j < i; j++)
{
temporaryBindings.Add(bindings[j]);
}
}
}
// At this point newBinding is either the updated binding (if
// rewrite returned true) or the original binding (if false
// was returned)
if (temporaryBindings != null)
{
temporaryBindings.Add(newBinding);
}
}
if (temporaryBindings != null)
{
newBindings = temporaryBindings;
return true;
}
else
{
newBindings = bindings;
return false;
}
}
static bool TryRewriteMemberBinding(MemberBinding binding, out MemberBinding newBinding, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
newBinding = binding;
bool hasChanged = false;
Expression other = null;
IList<ElementInit> initializerList = null;
IList<MemberBinding> bindingList = null;
switch (binding.BindingType)
{
case MemberBindingType.Assignment:
MemberAssignment assignment = (MemberAssignment)binding;
hasChanged |= TryRewriteLambdaExpression(assignment.Expression, out other, publicAccessor);
if (hasChanged)
{
newBinding = Expression.Bind(assignment.Member, other);
}
break;
case MemberBindingType.ListBinding:
MemberListBinding list = (MemberListBinding)binding;
hasChanged |= TryRewriteLambdaExpressionInitializersCollection(list.Initializers, out initializerList, publicAccessor);
if (hasChanged)
{
newBinding = Expression.ListBind(list.Member, initializerList);
}
break;
case MemberBindingType.MemberBinding:
MemberMemberBinding member = (MemberMemberBinding)binding;
hasChanged |= TryRewriteLambdaExpressionBindingsCollection(member.Bindings, out bindingList, publicAccessor);
if (hasChanged)
{
newBinding = Expression.MemberBind(member.Member, bindingList);
}
break;
}
return hasChanged;
}
static bool TryRewriteLambdaExpressionCollection(IList<Expression> expressions, out IList<Expression> newExpressions, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
IList<Expression> temporaryExpressions = null;
for (int i = 0; i < expressions.Count; i++)
{
Expression expression = expressions[i];
Expression newExpression;
if (TryRewriteLambdaExpression(expression, out newExpression, publicAccessor))
{
if (temporaryExpressions == null)
{
// We initialize the list by copying all of the unchanged
// expressions over
temporaryExpressions = new List<Expression>(expressions.Count);
for (int j = 0; j < i; j++)
{
temporaryExpressions.Add(expressions[j]);
}
}
}
// newExpression will either be set to the new expression (true was
// returned) or the original expression (false was returned)
if (temporaryExpressions != null)
{
temporaryExpressions.Add(newExpression);
}
}
if (temporaryExpressions != null)
{
newExpressions = temporaryExpressions;
return true;
}
else
{
newExpressions = expressions;
return false;
}
}
static bool TryRewriteLambdaExpressionInitializersCollection(IList<ElementInit> initializers, out IList<ElementInit> newInitializers, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
IList<ElementInit> temporaryInitializers = null;
for (int i = 0; i < initializers.Count; i++)
{
ElementInit elementInit = initializers[i];
IList<Expression> newExpressions;
if (TryRewriteLambdaExpressionCollection(elementInit.Arguments, out newExpressions, publicAccessor))
{
if (temporaryInitializers == null)
{
// We initialize the list by copying all of the unchanged
// initializers over
temporaryInitializers = new List<ElementInit>(initializers.Count);
for (int j = 0; j < i; j++)
{
temporaryInitializers.Add(initializers[j]);
}
}
elementInit = Expression.ElementInit(elementInit.AddMethod, newExpressions);
}
if (temporaryInitializers != null)
{
temporaryInitializers.Add(elementInit);
}
}
if (temporaryInitializers != null)
{
newInitializers = temporaryInitializers;
return true;
}
else
{
newInitializers = initializers;
return false;
}
}
static bool TryGetInlinedArgumentReference(MethodCallExpression originalExpression, Expression argumentExpression, out LocationReference inlinedReference, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
inlinedReference = null;
Argument argument = null;
object tempArgument;
if (CustomMemberResolver(argumentExpression, out tempArgument) && tempArgument is Argument)
{
argument = (Argument)tempArgument;
}
else
{
try
{
Expression<Func<Argument>> argumentLambda = Expression.Lambda<Func<Argument>>(argumentExpression);
Func<Argument> argumentFunc = argumentLambda.Compile();
argument = argumentFunc();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(argumentExpression.Type, originalExpression, e));
return false;
}
}
if (argument == null)
{
if (argumentExpression.NodeType == ExpressionType.MemberAccess)
{
MemberExpression memberExpression = (MemberExpression)argumentExpression;
if (memberExpression.Member.MemberType == MemberTypes.Property)
{
RuntimeArgument runtimeArgument = ActivityUtilities.FindArgument(memberExpression.Member.Name, publicAccessor.ActivityMetadata.CurrentActivity);
if (runtimeArgument != null && TryGetInlinedReference(publicAccessor, runtimeArgument, isLocationExpression, out inlinedReference))
{
return true;
}
}
}
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(argumentExpression.Type, originalExpression, SR.SubexpressionResultWasNull(argumentExpression.Type)));
return false;
}
else
{
if (argument.RuntimeArgument == null || !TryGetInlinedReference(publicAccessor, argument.RuntimeArgument, isLocationExpression, out inlinedReference))
{
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(argumentExpression.Type, originalExpression, SR.SubexpressionResultWasNotVisible(argumentExpression.Type)));
return false;
}
else
{
return true;
}
}
}
static bool TryRewriteArgumentGetCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
// We verify that this is a method we are expecting (single parameter
// of type ActivityContext). If not, we won't rewrite it at all
// and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression contextExpression = argumentExpressions[0];
if (contextExpression.Type == activityContextType)
{
LocationReference inlinedReference;
if (TryGetInlinedArgumentReference(originalExpression, originalExpression.Object, out inlinedReference, publicAccessor, isLocationExpression))
{
newExpression = Expression.Call(contextExpression, activityContextGetValueGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
return true;
}
}
}
newExpression = originalExpression;
return false;
}
static bool TryRewriteArgumentGetLocationCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
// We verify that this is a method we are expecting (single parameter
// of type ActivityContext). If not, we won't rewrite it at all
// and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression contextExpression = argumentExpressions[0];
if (contextExpression.Type == activityContextType)
{
LocationReference inlinedReference;
if (TryGetInlinedArgumentReference(originalExpression, originalExpression.Object, out inlinedReference, publicAccessor, true))
{
if (returnType == null)
{
newExpression = Expression.Call(Expression.Constant(inlinedReference, typeof(LocationReference)), locationReferenceGetLocationMethod, contextExpression);
}
else
{
newExpression = Expression.Call(contextExpression, activityContextGetLocationGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
}
return true;
}
}
}
newExpression = originalExpression;
return false;
}
static bool TryRewriteLocationReferenceSubclassGetCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
// We verify that this is a method we are expecting (single parameter
// of type ActivityContext). If not, we won't rewrite it at all
// and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression contextExpression = argumentExpressions[0];
if (contextExpression.Type == activityContextType)
{
LocationReference inlinedReference;
if (TryGetInlinedLocationReference(originalExpression, originalExpression.Object, out inlinedReference, publicAccessor, isLocationExpression))
{
newExpression = Expression.Call(contextExpression, activityContextGetValueGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
return true;
}
}
}
newExpression = originalExpression;
return false;
}
static bool TryRewriteLocationReferenceSubclassGetLocationCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
// We verify that this is a method we are expecting (single parameter
// of type ActivityContext). If not, we won't rewrite it at all
// and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression contextExpression = argumentExpressions[0];
if (contextExpression.Type == activityContextType)
{
LocationReference inlinedReference;
if (TryGetInlinedLocationReference(originalExpression, originalExpression.Object, out inlinedReference, publicAccessor, true))
{
if (returnType == null)
{
newExpression = Expression.Call(Expression.Constant(inlinedReference, typeof(LocationReference)), locationReferenceGetLocationMethod, originalExpression.Arguments[0]);
}
else
{
newExpression = Expression.Call(contextExpression, activityContextGetLocationGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
}
return true;
}
}
}
newExpression = originalExpression;
return false;
}
static bool CustomMemberResolver(Expression expression, out object memberValue)
{
memberValue = null;
switch (expression.NodeType)
{
case ExpressionType.Constant:
ConstantExpression constantExpression = expression as ConstantExpression;
memberValue = constantExpression.Value;
// memberValue = null means:
// 1. The expression does not follow the common patterns(local, field or property)
// which we optimize(do not compile using Linq compiler) and try to resolve directly in this method
// OR 2. The expression actually resolved to null.
// In both these cases, we compile the expression and run it so that we have a single error path.
return memberValue != null;
case ExpressionType.MemberAccess:
MemberExpression memberExpression = expression as MemberExpression;
if (memberExpression.Expression != null)
{
CustomMemberResolver(memberExpression.Expression, out memberValue);
memberValue = GetMemberValue(memberExpression.Member, memberValue);
}
return memberValue != null;
default:
return false;
}
}
static object GetMemberValue(MemberInfo memberInfo, object owner)
{
if (owner == null)
{
// We do not want to throw any exceptions here. We
// will just do the regular compile in this case.
return null;
}
MemberTypes memberType = memberInfo.MemberType;
if (memberType == MemberTypes.Property)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
return propertyInfo.GetValue(owner, null);
}
else if (memberType == MemberTypes.Field)
{
FieldInfo fieldInfo = memberInfo as FieldInfo;
return fieldInfo.GetValue(owner);
}
return null;
}
static bool TryGetInlinedLocationReference(MethodCallExpression originalExpression, Expression locationReferenceExpression, out LocationReference inlinedReference, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
inlinedReference = null;
LocationReference locationReference = null;
object tempLocationReference;
if (CustomMemberResolver(locationReferenceExpression, out tempLocationReference) && tempLocationReference is LocationReference)
{
locationReference = (LocationReference)tempLocationReference;
}
else
{
try
{
Expression<Func<LocationReference>> locationReferenceLambda = Expression.Lambda<Func<LocationReference>>(locationReferenceExpression);
Func<LocationReference> locationReferenceFunc = locationReferenceLambda.Compile();
locationReference = locationReferenceFunc();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(locationReferenceExpression.Type, originalExpression, e));
return false;
}
}
if (locationReference == null)
{
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(locationReferenceExpression.Type, originalExpression, SR.SubexpressionResultWasNull(locationReferenceExpression.Type)));
return false;
}
else if (!TryGetInlinedReference(publicAccessor, locationReference, isLocationExpression, out inlinedReference))
{
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(locationReferenceExpression.Type, originalExpression, SR.SubexpressionResultWasNotVisible(locationReferenceExpression.Type)));
return false;
}
else
{
return true;
}
}
static bool TryRewriteActivityContextGetValueCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
newExpression = originalExpression;
LocationReference inlinedReference = null;
// We verify that this is a method we are expecting (single parameter
// of either LocationReference or Argument type). If not, we won't
// rewrite it at all and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression parameterExpression = argumentExpressions[0];
if (TypeHelper.AreTypesCompatible(parameterExpression.Type, typeof(Argument)))
{
if (!TryGetInlinedArgumentReference(originalExpression, parameterExpression, out inlinedReference, publicAccessor, isLocationExpression))
{
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(parameterExpression.Type, originalExpression, SR.SubexpressionResultWasNotVisible(parameterExpression.Type)));
return false;
}
}
else if (TypeHelper.AreTypesCompatible(parameterExpression.Type, typeof(LocationReference)))
{
if (!TryGetInlinedLocationReference(originalExpression, parameterExpression, out inlinedReference, publicAccessor, isLocationExpression))
{
publicAccessor.ActivityMetadata.AddValidationError(SR.ErrorExtractingValuesForLambdaRewrite(parameterExpression.Type, originalExpression, SR.SubexpressionResultWasNotVisible(parameterExpression.Type)));
return false;
}
}
}
if (inlinedReference != null)
{
newExpression = Expression.Call(originalExpression.Object, activityContextGetValueGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
return true;
}
return false;
}
static bool TryRewriteActivityContextGetLocationCall(MethodCallExpression originalExpression, Type returnType, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor)
{
// We verify that this is a method we are expecting (single parameter
// of LocationReference type). If not, we won't rewrite it at all
// and will just let it fail at runtime.
ReadOnlyCollection<Expression> argumentExpressions = originalExpression.Arguments;
if (argumentExpressions.Count == 1)
{
Expression locationReference = argumentExpressions[0];
if (TypeHelper.AreTypesCompatible(locationReference.Type, locationReferenceType))
{
LocationReference inlinedReference;
if (TryGetInlinedLocationReference(originalExpression, originalExpression.Arguments[0], out inlinedReference, publicAccessor, true))
{
newExpression = Expression.Call(originalExpression.Object, activityContextGetLocationGenericMethod.MakeGenericMethod(returnType), Expression.Constant(inlinedReference, typeof(LocationReference)));
return true;
}
}
}
newExpression = originalExpression;
return false;
}
// Local perf testing leads to the following preference for matching method infos:
// * object.ReferenceEquals(info1, info2) is the fastest
// * info1.Name == "MethodName" is a close second
// * object.ReferenceEquals(info1, type.GetMethod("MethodName")) is very slow by comparison
// * object.ReferenceEquals(info1, genericMethodDefinition.MakeGenericMethod(typeParameter)) is also very
// slow by comparison
static bool TryRewriteMethodCall(MethodCallExpression methodCall, out Expression newExpression, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
// NOTE: Here's the set of method call conversions/rewrites that we are
// performing. The left hand side of the "=>" is the pattern that from
// the original expression using the following shorthand for instances of
// types:
// ctx = ActivityContext
// inArg = InArgument<T>
// inOutArg = InOutArgument<T>
// outArg = OutArgument<T>
// arg = Argument
// runtimeArg = RuntimeArgument
// ref = LocationReference (and subclasses)
//
// The right hand side of the "=>" shows the rewritten method call. When
// the same symbol shows up on both sides that means we will use the same
// expression (IE - ref.Get(ctx) => ctx.GetValue<T>(inline) means that the
// expression for ctx on the left side is the same expression we should use
// on the right side).
//
// "inline" is used in the right hand side to signify the inlined location
// reference. Except where explicitly called out, this is the inlined
// version of the LocationReference (or subclass) from the left hand side.
//
// If the left-hand-side method is Get/GetValue methods, and isLocationExpression
// is false, we create a read-only InlinedLocationReference, which will produce
// a RuntimeArgument<T> with ArgumentDirection.In.
// Otherwise, we create a full-access InlinedLocationReference, which will produce
// a RuntimeArgument<Location<T>> with ArgumentDirection.In.
//
// Finally, "(new)" signifies that the method we are looking for hides a
// method with the same signature on one of the base classes.
//
// ActivityContext
// ctx.GetValue<T>(inArg) => ctx.GetValue<T>(inline) inline = Inline(inArg.RuntimeArgument)
// ctx.GetValue<T>(inOutArg) => ctx.GetValue<T>(inline) inline = Inline(inOutArg.RuntimeArgument)
// ctx.GetValue<T>(outArg) => ctx.GetValue<T>(inline) inline = Inline(outArg.RuntimeArgument)
// ctx.GetValue(arg) => ctx.GetValue<object>(inline) inline = Inline(arg.RuntimeArgument)
// ctx.GetValue(runtimeArg) => ctx.GetValue<object>(inline)
// ctx.GetValue<T>(ref) => ctx.GetValue<T>(inline)
// ctx.GetLocation<T>(ref) => ctx.GetLocation<T>(inline)
//
// LocationReference
// ref.GetLocation(ctx) => inline.GetLocation(ctx)
//
// RuntimeArgument : LocationReference
// ref.Get(ctx) => ctx.GetValue<object>(inline)
// ref.Get<T>(ctx) => ctx.GetValue<T>(inline)
//
// Argument
// arg.Get(ctx) => ctx.GetValue<object>(inline) inline = Inline(arg.RuntimeArgument)
// arg.Get<T>(ctx) => ctx.GetValue<T>(inline) inline = Inline(arg.RuntimeArgument)
// arg.GetLocation(ctx) => inline.GetLocation(ctx) inline = Inline(arg.RuntimeArgument)
//
// InArgument<T> : Argument
// (new) arg.Get(ctx) => ctx.GetValue<T>(inline) inline = Inline(arg.RuntimeArgument)
//
// InOutArgument<T> : Argument
// (new) arg.Get(ctx) => ctx.GetValue<T>(inline) inline = Inline(arg.RuntimeArgument)
// (new) arg.GetLocation<T>(ctx) => ctx.GetLocation<T>(inline) inline = Inline(arg.RuntimeArgument)
//
// OutArgument<T> : Argument
// (new) arg.Get(ctx) => ctx.GetValue<T>(inline) inline = Inline(arg.RuntimeArgument)
// (new) arg.GetLocation<T>(ctx) => ctx.GetLocation<T>(inline) inline = Inline(arg.RuntimeArgument)
//
// Variable : LocationReference
// ref.Get(ctx) => ctx.GetValue<object>(inline)
//
// Variable<T> : Variable
// (new) ref.Get(ctx) => ctx.GetValue<T>(inline)
// (new) ref.GetLocation(ctx) => ctx.GetLocation<T>(inline)
//
// DelegateArgument : LocationReference
// ref.Get(ctx) => ctx.GetValue<object>(inline)
//
// DelegateInArgument<T> : DelegateArgument
// (new) ref.Get(ctx) => ctx.GetValue<T>(inline)
//
// DelegateOutArgument<T> : DelegateArgument
// (new) ref.Get(ctx) => ctx.GetValue<T>(inline)
// (new) ref.GetLocation(ctx) => ctx.GetLocation<T>(inline)
MethodInfo targetMethod = methodCall.Method;
Type targetObjectType = targetMethod.DeclaringType;
if (targetObjectType.IsGenericType)
{
// All of these methods are non-generic methods (they don't introduce a new
// type parameter), but they do make use of the type parameter of the
// generic declaring type. Because of that we can't do MethodInfo comparison
// and fall back to string comparison.
Type targetObjectGenericType = targetObjectType.GetGenericTypeDefinition();
if (targetObjectGenericType == variableGenericType)
{
if (targetMethod.Name == "Get")
{
return TryRewriteLocationReferenceSubclassGetCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor, isLocationExpression);
}
else if (targetMethod.Name == "GetLocation")
{
return TryRewriteLocationReferenceSubclassGetLocationCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor);
}
}
else if (targetObjectGenericType == inArgumentGenericType)
{
if (targetMethod.Name == "Get")
{
return TryRewriteArgumentGetCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor, isLocationExpression);
}
}
else if (targetObjectGenericType == outArgumentGenericType || targetObjectGenericType == inOutArgumentGenericType)
{
if (targetMethod.Name == "Get")
{
return TryRewriteArgumentGetCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor, isLocationExpression);
}
else if (targetMethod.Name == "GetLocation")
{
return TryRewriteArgumentGetLocationCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor);
}
}
else if (targetObjectGenericType == delegateInArgumentGenericType)
{
if (targetMethod.Name == "Get")
{
return TryRewriteLocationReferenceSubclassGetCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor, isLocationExpression);
}
}
else if (targetObjectGenericType == delegateOutArgumentGenericType)
{
if (targetMethod.Name == "Get")
{
return TryRewriteLocationReferenceSubclassGetCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor, isLocationExpression);
}
else if (targetMethod.Name == "GetLocation")
{
return TryRewriteLocationReferenceSubclassGetLocationCall(methodCall, targetObjectType.GetGenericArguments()[0], out newExpression, publicAccessor);
}
}
}
else
{
if (targetObjectType == variableType)
{
if (object.ReferenceEquals(targetMethod, variableGetMethod))
{
return TryRewriteLocationReferenceSubclassGetCall(methodCall, TypeHelper.ObjectType, out newExpression, publicAccessor, isLocationExpression);
}
}
else if (targetObjectType == delegateArgumentType)
{
if (object.ReferenceEquals(targetMethod, delegateArgumentGetMethod))
{
return TryRewriteLocationReferenceSubclassGetCall(methodCall, TypeHelper.ObjectType, out newExpression, publicAccessor, isLocationExpression);
}
}
else if (targetObjectType == activityContextType)
{
// We use the string comparison for these two because
// we have several overloads of GetValue (some generic,
// some not) and GetLocation is a generic method
if (targetMethod.Name == "GetValue")
{
Type returnType = TypeHelper.ObjectType;
if (targetMethod.IsGenericMethod)
{
returnType = targetMethod.GetGenericArguments()[0];
}
return TryRewriteActivityContextGetValueCall(methodCall, returnType, out newExpression, publicAccessor, isLocationExpression);
}
else if (targetMethod.IsGenericMethod && targetMethod.Name == "GetLocation")
{
return TryRewriteActivityContextGetLocationCall(methodCall, targetMethod.GetGenericArguments()[0], out newExpression, publicAccessor);
}
}
else if (targetObjectType == locationReferenceType)
{
if (object.ReferenceEquals(targetMethod, locationReferenceGetLocationMethod))
{
return TryRewriteLocationReferenceSubclassGetLocationCall(methodCall, null, out newExpression, publicAccessor);
}
}
else if (targetObjectType == runtimeArgumentType)
{
// We use string comparison here because we can
// match both overloads with a single check.
if (targetMethod.Name == "Get")
{
Type returnType = TypeHelper.ObjectType;
if (targetMethod.IsGenericMethod)
{
returnType = targetMethod.GetGenericArguments()[0];
}
return TryRewriteLocationReferenceSubclassGetCall(methodCall, returnType, out newExpression, publicAccessor, isLocationExpression);
}
}
else if (targetObjectType == argumentType)
{
// We use string comparison here because we can
// match both overloads with a single check.
if (targetMethod.Name == "Get")
{
Type returnType = TypeHelper.ObjectType;
if (targetMethod.IsGenericMethod)
{
returnType = targetMethod.GetGenericArguments()[0];
}
return TryRewriteArgumentGetCall(methodCall, returnType, out newExpression, publicAccessor, isLocationExpression);
}
else if (object.ReferenceEquals(targetMethod, argumentGetLocationMethod))
{
return TryRewriteArgumentGetLocationCall(methodCall, null, out newExpression, publicAccessor);
}
}
}
// Here's the code for a method call that isn't on our "special" list
newExpression = methodCall;
Expression objectExpression;
IList<Expression> expressionList;
bool hasChanged = TryRewriteLambdaExpression(methodCall.Object, out objectExpression, publicAccessor);
hasChanged |= TryRewriteLambdaExpressionCollection(methodCall.Arguments, out expressionList, publicAccessor);
if (hasChanged)
{
newExpression = Expression.Call(objectExpression, targetMethod, expressionList);
}
return hasChanged;
}
internal static Expression RewriteNonCompiledExpressionTree(LambdaExpression originalLambdaExpression)
{
ExpressionTreeRewriter expressionVisitor = new ExpressionTreeRewriter();
return expressionVisitor.Visit(Expression.Lambda(
typeof(Func<,>).MakeGenericType(typeof(ActivityContext), originalLambdaExpression.ReturnType),
originalLambdaExpression.Body,
new ParameterExpression[] { ExpressionUtilities.RuntimeContextParameter }));
}
}
}
|