e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1986 lines
90 KiB
C#
1986 lines
90 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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 }));
|
|
}
|
|
}
|
|
}
|