Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / System.Activities / System / Activities / Expressions / ExpressionServices.cs / 1305376 / ExpressionServices.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------
namespace System.Activities.Expressions
{
using System;
using System.Activities.Statements;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Collections;
public static class ExpressionServices
{
// Reflection is used to call generic function because type information are only known at runtime.
static MethodInfo TryConvertBinaryExpressionHandle = typeof(ExpressionServices).GetMethod("TryConvertBinaryExpressionWorker", BindingFlags.NonPublic | BindingFlags.Static);
static MethodInfo TryConvertUnaryExpressionHandle = typeof(ExpressionServices).GetMethod("TryConvertUnaryExpressionWorker", BindingFlags.NonPublic | BindingFlags.Static);
static MethodInfo TryConvertMemberExpressionHandle = typeof(ExpressionServices).GetMethod("TryConvertMemberExpressionWorker", BindingFlags.NonPublic | BindingFlags.Static);
static MethodInfo TryConvertArgumentExpressionHandle = typeof(ExpressionServices).GetMethod("TryConvertArgumentExpressionWorker", BindingFlags.NonPublic | BindingFlags.Static);
static MethodInfo TryConvertReferenceMemberExpressionHandle = typeof(ExpressionServices).GetMethod("TryConvertReferenceMemberExpressionWorker", BindingFlags.NonPublic | BindingFlags.Static);
static MethodInfo TryConvertIndexerReferenceHandle = typeof(ExpressionServices).GetMethod("TryConvertIndexerReferenceWorker", BindingFlags.NonPublic | BindingFlags.Static);
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
Justification = "The parameter is restricted correctly.")]
public static Activity Convert(Expression> expression)
{
Activity result;
if (expression == null)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("expression", SR.ExpressionRequiredForConversion));
}
TryConvert(expression.Body, true, out result);
return result;
}
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
Justification = "The parameter is restricted correctly.")]
public static bool TryConvert(Expression> expression, out Activity result)
{
if (expression == null)
{
result = null;
return false;
}
return TryConvert(expression.Body, false, out result) == null;
}
static string TryConvert(Expression body, bool throwOnError, out Activity result)
{
result = null;
UnaryExpression unaryExpressionBody = body as UnaryExpression;
if (unaryExpressionBody != null)
{
Type operandType = unaryExpressionBody.Operand.Type;
Type resultType = typeof(TResult);
return TryConvertUnaryExpression(unaryExpressionBody, operandType, throwOnError, out result);
}
BinaryExpression binaryExpressionBody = body as BinaryExpression;
if (binaryExpressionBody != null)
{
Type leftType = binaryExpressionBody.Left.Type;
Type rightType = binaryExpressionBody.Right.Type;
if (binaryExpressionBody.NodeType == ExpressionType.ArrayIndex)
{
return TryConvertArrayItemValue(binaryExpressionBody, leftType, rightType, throwOnError, out result);
}
return TryConvertBinaryExpression(binaryExpressionBody, leftType, rightType, throwOnError, out result);
}
MemberExpression memberExpressionBody = body as MemberExpression;
if (memberExpressionBody != null)
{
Type memberType = memberExpressionBody.Expression == null ? memberExpressionBody.Member.DeclaringType : memberExpressionBody.Expression.Type;
return TryConvertMemberExpression(memberExpressionBody, memberType, throwOnError, out result);
}
MethodCallExpression methodCallExpressionBody = body as MethodCallExpression;
if (methodCallExpressionBody != null)
{
MethodInfo calledMethod = methodCallExpressionBody.Method;
Type declaringType = calledMethod.DeclaringType;
ParameterInfo[] parameters = calledMethod.GetParameters();
if (TypeHelper.AreTypesCompatible(declaringType, typeof(Variable)) && calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertVariableValue(methodCallExpressionBody, throwOnError, out result);
}
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(Argument))
&& calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertArgumentValue(methodCallExpressionBody.Object as MemberExpression, throwOnError, out result);
}
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(DelegateArgument))
&& calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertDelegateArgumentValue(methodCallExpressionBody, throwOnError, out result);
}
//Only ActivityContext.GetValue() on Argument, RuntimeArgument are supported.
//The reason why DelegateArgument and Variable are not supported: for such usage: ctx.GetValue(delegateInArgument),
//C# compiler will complain ambiguous method resolution between ActivityContext.GetValue(Argument) and ActivityContext.GetValue(LocationReference).
//If we use ctx.GetValue((LocationReference)delegateInArgument)instead, we cannot tell whether it's a DelegateArgument.
// Variable has similar issue.
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(ActivityContext)) && calledMethod.Name == "GetValue" && parameters.Length == 1 &&
(TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(Argument)) || TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(RuntimeArgument))))
{
MemberExpression memberExpression = methodCallExpressionBody.Arguments[0] as MemberExpression;
return TryConvertArgumentValue(memberExpression, throwOnError, out result);
}
else
{
return TryConvertMethodCallExpression(methodCallExpressionBody, throwOnError, out result);
}
}
InvocationExpression invocationExpression = body as InvocationExpression;
if (invocationExpression != null)
{
return TryConvertInvocationExpression(invocationExpression, throwOnError, out result);
}
NewExpression newExpression = body as NewExpression;
if (newExpression != null)
{
return TryConvertNewExpression(newExpression, throwOnError, out result);
}
NewArrayExpression newArrayExpression = body as NewArrayExpression;
if (newArrayExpression != null && newArrayExpression.NodeType != ExpressionType.NewArrayInit)
{
return TryConvertNewArrayExpression(newArrayExpression, throwOnError, out result);
}
ConstantExpression constantExpressionBody = body as ConstantExpression;
if (constantExpressionBody != null)
{
// This is to handle the leaf node as a literal value
result = new Literal { Value = (TResult)constantExpressionBody.Value };
return null;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedExpressionType(body.NodeType)));
}
else
{
return SR.UnsupportedExpressionType(body.NodeType);
}
}
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
Justification = "The parameter is restricted correctly.")]
public static Activity> ConvertReference(Expression> expression)
{
Activity> result;
if (expression == null)
{
throw FxTrace.Exception.AsError(new ArgumentNullException("expression", SR.ExpressionRequiredForConversion));
}
TryConvertReference(expression.Body, true, out result);
return result;
}
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
Justification = "The parameter is restricted correctly.")]
public static bool TryConvertReference(Expression> expression, out Activity> result)
{
if (expression == null)
{
result = null;
return false;
}
return TryConvertReference(expression.Body, false, out result) == null;
}
static string TryConvertReference(Expression body, bool throwOnError, out Activity> result)
{
result = null;
MemberExpression memberExpressionBody = body as MemberExpression;
if (memberExpressionBody != null)
{
Type memberType = memberExpressionBody.Expression == null ? memberExpressionBody.Member.DeclaringType : memberExpressionBody.Expression.Type;
return TryConvertReferenceMemberExpression(memberExpressionBody, memberType, throwOnError, out result);
}
BinaryExpression binaryExpressionBody = body as BinaryExpression;
if (binaryExpressionBody != null)
{
Type leftType = binaryExpressionBody.Left.Type;
Type rightType = binaryExpressionBody.Right.Type;
if (binaryExpressionBody.NodeType == ExpressionType.ArrayIndex)
{
return TryConvertArrayItemReference(binaryExpressionBody, leftType, rightType, throwOnError, out result);
}
}
MethodCallExpression methodCallExpressionBody = body as MethodCallExpression;
if (methodCallExpressionBody != null)
{
Type declaringType = methodCallExpressionBody.Method.DeclaringType;
MethodInfo calledMethod = methodCallExpressionBody.Method;
if (declaringType.IsArray && calledMethod.Name == "Get")
{
return TryConvertMultiDimensionalArrayItemReference(methodCallExpressionBody, throwOnError, out result);
}
if (calledMethod.IsSpecialName && calledMethod.Name == "get_Item")
{
return TryConvertIndexerReference(methodCallExpressionBody, throwOnError, out result);
}
ParameterInfo[] parameters = calledMethod.GetParameters();
if (TypeHelper.AreTypesCompatible(declaringType, typeof(Variable)) && calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertVariableReference(methodCallExpressionBody, throwOnError, out result);
}
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(Argument))
&& calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertArgumentReference(methodCallExpressionBody, throwOnError, out result);
}
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(DelegateArgument))
&& calledMethod.Name == "Get" && parameters.Length == 1 && TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(ActivityContext)))
{
return TryConvertDelegateArgumentReference(methodCallExpressionBody, throwOnError, out result);
}
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedReferenceExpressionType(body.NodeType)));
}
else
{
return SR.UnsupportedReferenceExpressionType(body.NodeType);
}
}
static string TryConvertIndexerReference(MethodCallExpression methodCallExpressionBody, bool throwOnError, out Activity> result)
{
result = null;
try
{
if (methodCallExpressionBody.Object == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.InstanceMethodCallRequiresTargetObject));
}
else
{
return SR.InstanceMethodCallRequiresTargetObject;
}
}
MethodInfo specializedHandle = TryConvertIndexerReferenceHandle.MakeGenericMethod(methodCallExpressionBody.Object.Type, typeof(TResult));
object[] parameters = new object[] { methodCallExpressionBody, throwOnError, null };
string errorString = specializedHandle.Invoke(null, parameters) as string;
result = parameters[2] as Activity>;
return errorString;
}
catch (TargetInvocationException e)
{
throw FxTrace.Exception.AsError(e.InnerException);
}
}
static string TryConvertIndexerReferenceWorker(MethodCallExpression methodCallExpressionBody, bool throwOnError, out Activity> result)
{
result = null;
Fx.Assert(methodCallExpressionBody.Object != null, "Indexer must have a target object");
if (!typeof(TOperand).IsValueType)
{
Activity operand = null;
string operandError = TryConvert(methodCallExpressionBody.Object, throwOnError, out operand);
if (operandError != null)
{
return operandError;
}
IndexerReference indexerReference = new IndexerReference
{
Operand = new InArgument(operand) { EvaluationOrder = 0 },
};
string argumentError = TryConvertArguments(methodCallExpressionBody.Arguments, indexerReference.Indices, methodCallExpressionBody.GetType(), 1, null, throwOnError);
if (argumentError != null)
{
return argumentError;
}
result = indexerReference;
}
else
{
Activity> operandReference = null;
string operandError = TryConvertReference(methodCallExpressionBody.Object, throwOnError, out operandReference);
if (operandError != null)
{
return operandError;
}
ValueTypeIndexerReference indexerReference = new ValueTypeIndexerReference
{
OperandLocation = new InOutArgument(operandReference) { EvaluationOrder = 0 },
};
string argumentError = TryConvertArguments(methodCallExpressionBody.Arguments, indexerReference.Indices, methodCallExpressionBody.GetType(), 1, null, throwOnError);
if (argumentError != null)
{
return argumentError;
}
result = indexerReference;
}
return null;
}
static string TryConvertMultiDimensionalArrayItemReference(MethodCallExpression methodCallExpression, bool throwOnError, out Activity> result)
{
result = null;
Activity operand;
if (methodCallExpression.Object == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.InstanceMethodCallRequiresTargetObject));
}
else
{
return SR.InstanceMethodCallRequiresTargetObject;
}
}
string errorString = TryConvert(methodCallExpression.Object, throwOnError, out operand);
if (errorString != null)
{
return errorString;
}
MultidimensionalArrayItemReference reference = new MultidimensionalArrayItemReference
{
Array = new InArgument(operand) { EvaluationOrder = 0 },
};
Collection> arguments = reference.Indices;
string argumentError = TryConvertArguments(methodCallExpression.Arguments, reference.Indices, methodCallExpression.GetType(), 1, null, throwOnError);
if (argumentError != null)
{
return argumentError;
}
result = reference;
return null;
}
static string TryConvertVariableReference(MethodCallExpression methodCallExpression, bool throwOnError, out Activity> result)
{
result = null;
Variable variableObject = null;
//
// This is a fast path to handle a simple variable object.
//
// Linq actually generate a temp class wrapping all the local variables.
//
// The real expression object look like
// new TempClass() { A = a }.A.Get(env)
//
// A is a field
if (methodCallExpression.Object is MemberExpression)
{
MemberExpression member = methodCallExpression.Object as MemberExpression;
if (member.Expression is ConstantExpression)
{
ConstantExpression memberExpression = member.Expression as ConstantExpression;
if (member.Member is FieldInfo)
{
FieldInfo field = member.Member as FieldInfo;
variableObject = field.GetValue(memberExpression.Value) as Variable;
Fx.Assert(variableObject != null, "Linq generated expression tree should be correct");
result = new VariableReference { Variable = variableObject };
return null;
}
}
}
//This is to handle the expression whose evaluation result is a variable object.
//Limitation: The expression of variable object has to be evaludated in conversion time. It means after conversion, the variable object should not be changed any more.
//For example, the following case is not legal:
//
//Program.static_X = new Variable { Default = "Hello" };
//Activity> weRef = ExpressionServices.ConvertReference((env) => Program.static_X.Get(env));
//Program.static_X = new Variable { Default = "World" };
//Sequence sequence = new Sequence
//{
// Variables = { Program.static_X },
// Activities =
// {
// new Assign
// {
// To = new OutArgument{Expression = weRef},
// Value = "haha",
// },
// new WriteLine
// {
// Text = Program.static_X,
// }
// }
//};
//WorkflowInvoker.Invoke(sequence);
//
// The reason is that "Program.static_X = new Variable { Default = "World" }" happens after conversion.
try
{
Expression> funcExpression = Expression.Lambda>(methodCallExpression.Object);
Func func = funcExpression.Compile();
variableObject = func();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(e);
}
else
{
return e.Message;
}
}
Fx.Assert(variableObject is Variable, "Linq generated expression tree should be correct");
result = new VariableReference { Variable = variableObject };
return null;
}
static string TryConvertArrayItemReference(BinaryExpression binaryExpression, Type leftType, Type rightType, bool throwOnError, out Activity> result)
{
result = null;
//for ArrayIndex expression, Left type is always TResult[] and Right type is always int
if (!leftType.IsArray)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerOnNonArrayType(leftType)));
}
else
{
return SR.DoNotSupportArrayIndexerOnNonArrayType(leftType);
}
}
//Because co-variance for LValue requires that TResult is compatible with actual type. However, we cannot write such a lambda expression. E,g:
//Expression expr = env => a.Get(env). Here a.Get(env) returns BaseClass. So we needn't co-viariance here.
if (leftType.GetElementType() != typeof(TResult))
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerReferenceWithDifferentArrayTypeAndResultType(leftType, typeof(TResult))));
}
else
{
return SR.DoNotSupportArrayIndexerReferenceWithDifferentArrayTypeAndResultType(leftType, typeof(TResult));
}
}
if (rightType != typeof(int))
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerWithNonIntIndex(rightType)));
}
else
{
return SR.DoNotSupportArrayIndexerWithNonIntIndex(rightType);
}
}
Activity array;
string arrayError = TryConvert(binaryExpression.Left, throwOnError, out array);
if (arrayError != null)
{
return arrayError;
}
Activity index;
string indexError = TryConvert(binaryExpression.Right, throwOnError, out index);
if (indexError != null)
{
return indexError;
}
result = new ArrayItemReference
{
Array = new InArgument(array) { EvaluationOrder = 0 },
Index = new InArgument(index) { EvaluationOrder = 1 },
};
return null;
}
static string TryConvertVariableValue(MethodCallExpression methodCallExpression, bool throwOnError, out Activity result)
{
result = null;
Variable variableObject = null;
//
// This is a fast path to handle a simple variable object
//
// Linq actually generate a temp class wrapping all the local variables.
//
// The real expression object look like
// new TempClass() { A = a }.A.Get(env)
//
// A is a field
if (methodCallExpression.Object is MemberExpression)
{
MemberExpression member = methodCallExpression.Object as MemberExpression;
if (member.Expression is ConstantExpression)
{
ConstantExpression memberExpression = member.Expression as ConstantExpression;
if (member.Member is FieldInfo)
{
FieldInfo field = member.Member as FieldInfo;
variableObject = field.GetValue(memberExpression.Value) as Variable;
result = new VariableValue { Variable = variableObject };
return null;
}
}
}
//This is to handle the expression whose evaluation result is a variable object.
//Limitation: The expression of variable object has to be evaludated in conversion time. It means after conversion, the variable object should not be changed any more.
//For example, the following case is not legal:
//
// Program.static_X = new Variable { Default = "Hello" };
// Activity we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
// Program.static_X = new Variable { Default = "World" };
// Sequence sequence = new Sequence
// {
// Variables = { Program.static_X },
// Activities =
// {
// new WriteLine
// {
// Text = new InArgument{Expression = we},
// }
// }
// };
// WorkflowInvoker.Invoke(sequence);
//
// The reason is that "Program.static_X = new Variable { Default = "World" }" happens after conversion.
try
{
Expression> funcExpression = Expression.Lambda>(methodCallExpression.Object);
Func func = funcExpression.Compile();
variableObject = func();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(e);
}
else
{
return e.Message;
}
}
result = new VariableValue { Variable = variableObject };
return null;
}
static string TryConvertDelegateArgumentValue(MethodCallExpression methodCallExpression, bool throwOnError, out Activity result)
{
result = null;
DelegateArgument delegateArgument = null;
//This is to handle the expression whose evaluation result is a DelegateArgument.
//Limitation: The expression of variable object has to be evaluated in conversion time. It means after conversion, the DelegateArgument object should not be changed any more.
//For example, the following case is not legal:
//
// Program.static_X = new DelegateInArgument();
// Activity we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
// Program.static_X = new DelegateInArgument();
// ActivityAction activityAction = new ActivityAction
// {
// Argument = Program.static_X,
// Handler = new WriteLine
// {
// Text = we,
// }
// }
// };
// WorkflowInvoker.Invoke( new InvokeAction
// {
// Argument = "Hello",
// Action = activityAction,
// }
//);
//
// The reason is that "Program.static_X" is changed after conversion.
try
{
Expression> funcExpression = Expression.Lambda>(methodCallExpression.Object);
Func func = funcExpression.Compile();
delegateArgument = func();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(e);
}
else
{
return e.Message;
}
}
result = new DelegateArgumentValue(delegateArgument);
return null;
}
static string TryConvertDelegateArgumentReference(MethodCallExpression methodCallExpression, bool throwOnError, out Activity> result)
{
result = null;
DelegateArgument delegateArgument = null;
//This is to handle the expression whose evaluation result is a DelegateArgument.
//Limitation: The expression of variable object has to be evaluated in conversion time. It means after conversion, the DelegateArgument object should not be changed any more.
//For example, the following case is not legal:
//
// Program.static_X = new DelegateInArgument();
// Activity we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
// Program.static_X = new DelegateInArgument();
// ActivityAction activityAction = new ActivityAction
// {
// Argument = Program.static_X,
// Handler = new WriteLine
// {
// Text = we,
// }
// }
// };
// WorkflowInvoker.Invoke( new InvokeAction
// {
// Argument = "Hello",
// Action = activityAction,
// }
//);
//
// The reason is that "Program.static_X" is changed after conversion.
try
{
Expression> funcExpression = Expression.Lambda>(methodCallExpression.Object);
Func func = funcExpression.Compile();
delegateArgument = func();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(e);
}
else
{
return e.Message;
}
}
result = new DelegateArgumentReference(delegateArgument);
return null;
}
static string TryConvertArgumentValue(MemberExpression memberExpression, bool throwOnError, out Activity result)
{
result = null;
if (memberExpression != null && TypeHelper.AreTypesCompatible(memberExpression.Type, typeof(RuntimeArgument)))
{
RuntimeArgument ra = null;
try
{
Expression> expr = Expression.Lambda>(memberExpression, null);
Func func = expr.Compile();
ra = func();
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
return e.Message;
}
if (ra != null)
{
result = new ArgumentValue
{
ArgumentName = ra.Name,
};
return null;
}
else
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.RuntimeArgumentNotCreated));
}
else
{
return SR.RuntimeArgumentNotCreated;
}
}
}
else
{
//Assumption: Arguments must be properties of Activity object. Otherwise, it cannot be found by runtime via ArgumentValue.
if (memberExpression != null && memberExpression.Member is PropertyInfo)
{
PropertyInfo property = memberExpression.Member as PropertyInfo;
result = new ArgumentValue { ArgumentName = property.Name };
return null;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentMustbePropertyofWorkflowElement));
}
else
{
return SR.ArgumentMustbePropertyofWorkflowElement;
}
}
}
static string TryConvertArgumentReference(MethodCallExpression methodCallExpression, bool throwOnError, out Activity> result)
{
result = null;
//Assumption: Arguments must be properties of Activity object. Otherwise, it cannot be found by runtime via ArgumentReference.
if (methodCallExpression.Object is MemberExpression)
{
MemberExpression member = methodCallExpression.Object as MemberExpression;
if (member.Member is PropertyInfo)
{
PropertyInfo property = member.Member as PropertyInfo;
result = new ArgumentReference { ArgumentName = property.Name };
return null;
}
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentMustbePropertyofWorkflowElement));
}
else
{
return SR.ArgumentMustbePropertyofWorkflowElement;
}
}
static string TryConvertBinaryExpression(BinaryExpression binaryExpressionBody, Type leftType, Type rightType, bool throwOnError, out Activity result)
{
try
{
MethodInfo specializedHandle = TryConvertBinaryExpressionHandle.MakeGenericMethod(leftType, rightType, typeof(TResult));
object[] parameters = new object[] { binaryExpressionBody, throwOnError, null };
string errorString = specializedHandle.Invoke(null, parameters) as string;
result = parameters[2] as Activity;
return errorString;
}
catch (TargetInvocationException e)
{
throw FxTrace.Exception.AsError(e.InnerException);
}
}
//this method handles single dimentional array. Multiple dimentional array accessor is method call expression
static string TryConvertArrayItemValue(BinaryExpression binaryExpression, Type leftType, Type rightType, bool throwOnError, out Activity result)
{
result = null;
//for ArrayIndex expression, Left type is always TResult[] and Right type is always int
if (!leftType.IsArray)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerOnNonArrayType(leftType)));
}
else
{
return SR.DoNotSupportArrayIndexerOnNonArrayType(leftType);
}
}
if (!TypeHelper.AreTypesCompatible(leftType.GetElementType(), typeof(TResult)))
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerValueWithIncompatibleArrayTypeAndResultType(leftType, typeof(TResult))));
}
else
{
return SR.DoNotSupportArrayIndexerValueWithIncompatibleArrayTypeAndResultType(leftType, typeof(TResult));
}
}
if (rightType != typeof(int))
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.DoNotSupportArrayIndexerWithNonIntIndex(rightType)));
}
else
{
return SR.DoNotSupportArrayIndexerWithNonIntIndex(rightType);
}
}
Activity array;
string arrayError = TryConvert(binaryExpression.Left, throwOnError, out array);
if (arrayError != null)
{
return arrayError;
}
Activity index;
string indexError = TryConvert(binaryExpression.Right, throwOnError, out index);
if (indexError != null)
{
return indexError;
}
result = new ArrayItemValue
{
Array = new InArgument(array) { EvaluationOrder = 0 },
Index = new InArgument(index) { EvaluationOrder = 1 },
};
return null;
}
static string TryConvertBinaryExpressionWorker(BinaryExpression binaryExpressionBody, bool throwOnError, out Activity result)
{
result = null;
Activity left;
string leftError = TryConvert(binaryExpressionBody.Left, throwOnError, out left);
if (leftError != null)
{
return leftError;
}
Activity right;
string rightError = TryConvert(binaryExpressionBody.Right, throwOnError, out right);
if (rightError != null)
{
return rightError;
}
if (binaryExpressionBody.Method != null)
{
return TryConvertOverloadingBinaryOperator(binaryExpressionBody, left, right, throwOnError, out result);
}
InArgument leftArgument = new InArgument(left) { EvaluationOrder = 0 };
InArgument rightArgument = new InArgument(right) { EvaluationOrder = 1 };
switch (binaryExpressionBody.NodeType)
{
case ExpressionType.Add:
result = new Add() { Left = leftArgument, Right = rightArgument, Checked = false };
break;
case ExpressionType.AddChecked:
result = new Add() { Left = leftArgument, Right = rightArgument, Checked = true };
break;
case ExpressionType.Subtract:
result = new Subtract() { Left = leftArgument, Right = rightArgument, Checked = false };
break;
case ExpressionType.SubtractChecked:
result = new Subtract() { Left = leftArgument, Right = rightArgument, Checked = true };
break;
case ExpressionType.Multiply:
result = new Multiply() { Left = leftArgument, Right = rightArgument, Checked = false };
break;
case ExpressionType.MultiplyChecked:
result = new Multiply() { Left = leftArgument, Right = rightArgument, Checked = true };
break;
case ExpressionType.Divide:
result = new Divide() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.AndAlso:
Fx.Assert(typeof(TLeft) == typeof(bool), "AndAlso only accept bool.");
Fx.Assert(typeof(TRight) == typeof(bool), "AndAlso only accept bool.");
Fx.Assert(typeof(TResult) == typeof(bool), "AndAlso only accept bool.");
// Work around generic constraints
object leftObject1 = left;
object rightObject1 = right;
object resultObject1 = new AndAlso() { Left = (Activity)leftObject1, Right = (Activity)rightObject1 };
result = (Activity)resultObject1;
break;
case ExpressionType.OrElse:
Fx.Assert(typeof(TLeft) == typeof(bool), "OrElse only accept bool.");
Fx.Assert(typeof(TRight) == typeof(bool), "OrElse only accept bool.");
Fx.Assert(typeof(TResult) == typeof(bool), "OrElse only accept bool.");
// Work around generic constraints
object leftObject2 = left;
object rightObject2 = right;
object resultObject2 = new OrElse() { Left = (Activity)leftObject2, Right = (Activity)rightObject2 };
result = (Activity)resultObject2;
break;
case ExpressionType.Or:
result = new Or() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.And:
result = new And() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.LessThan:
result = new LessThan() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.LessThanOrEqual:
result = new LessThanOrEqual() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.GreaterThan:
result = new GreaterThan() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.GreaterThanOrEqual:
result = new GreaterThanOrEqual() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.Equal:
result = new Equal() { Left = leftArgument, Right = rightArgument };
break;
case ExpressionType.NotEqual:
result = new NotEqual() { Left = leftArgument, Right = rightArgument };
break;
default:
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedExpressionType(binaryExpressionBody.NodeType)));
}
else
{
return SR.UnsupportedExpressionType(binaryExpressionBody.NodeType);
}
}
return null;
}
static string TryConvertUnaryExpression(UnaryExpression unaryExpressionBody, Type operandType, bool throwOnError, out Activity result)
{
try
{
MethodInfo specializedHandle = TryConvertUnaryExpressionHandle.MakeGenericMethod(operandType, typeof(TResult));
object[] parameters = new object[] { unaryExpressionBody, throwOnError, null };
string errorString = specializedHandle.Invoke(null, parameters) as string;
result = parameters[2] as Activity;
return errorString;
}
catch (TargetInvocationException e)
{
throw FxTrace.Exception.AsError(e.InnerException);
}
}
static string TryConvertUnaryExpressionWorker(UnaryExpression unaryExpressionBody, bool throwOnError, out Activity result)
{
result = null;
Activity operand;
string operandError = TryConvert(unaryExpressionBody.Operand, throwOnError, out operand);
if (operandError != null)
{
return operandError;
}
if (unaryExpressionBody.Method != null)
{
return TryConvertOverloadingUnaryOperator(unaryExpressionBody, operand, throwOnError, out result);
}
switch (unaryExpressionBody.NodeType)
{
case ExpressionType.Not:
result = new Not { Operand = operand };
break;
case ExpressionType.Convert:
result = new Cast { Operand = operand, Checked = false };
break;
case ExpressionType.ConvertChecked:
result = new Cast { Operand = operand, Checked = true };
break;
case ExpressionType.TypeAs:
result = new As { Operand = operand };
break;
default:
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedExpressionType(unaryExpressionBody.NodeType)));
}
else
{
return SR.UnsupportedExpressionType(unaryExpressionBody.NodeType);
}
}
return null;
}
static string TryConvertMemberExpression(MemberExpression memberExpressionBody, Type operandType, bool throwOnError, out Activity result)
{
try
{
MethodInfo specializedHandle = TryConvertMemberExpressionHandle.MakeGenericMethod(operandType, typeof(TResult));
object[] parameters = new object[] { memberExpressionBody, throwOnError, null };
string errorString = specializedHandle.Invoke(null, parameters) as string;
result = parameters[2] as Activity;
return errorString;
}
catch (TargetInvocationException e)
{
throw FxTrace.Exception.AsError(e.InnerException);
}
}
static string TryConvertMemberExpressionWorker(MemberExpression memberExpressionBody, bool throwOnError, out Activity result)
{
result = null;
Activity operand = null;
if (memberExpressionBody.Expression != null)
{
// Static property might not have any expressions.
string operandError = TryConvert(memberExpressionBody.Expression, throwOnError, out operand);
if (operandError != null)
{
return operandError;
}
}
if (memberExpressionBody.Member is PropertyInfo)
{
if (operand == null)
{
result = new PropertyValue { PropertyName = memberExpressionBody.Member.Name };
}
else
{
result = new PropertyValue { Operand = operand, PropertyName = memberExpressionBody.Member.Name };
}
return null;
}
else if (memberExpressionBody.Member is FieldInfo)
{
if (operand == null)
{
result = new FieldValue { FieldName = memberExpressionBody.Member.Name };
}
else
{
result = new FieldValue { Operand = operand, FieldName = memberExpressionBody.Member.Name };
}
return null;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedMemberExpressionWithType(memberExpressionBody.Member.GetType().Name)));
}
else
{
return SR.UnsupportedMemberExpressionWithType(memberExpressionBody.Member.GetType().Name);
}
}
static string TryConvertReferenceMemberExpression(MemberExpression memberExpressionBody, Type operandType, bool throwOnError, out Activity> result)
{
try
{
MethodInfo specializedHandle = TryConvertReferenceMemberExpressionHandle.MakeGenericMethod(operandType, typeof(TResult));
object[] parameters = new object[] { memberExpressionBody, throwOnError, null };
string errorString = specializedHandle.Invoke(null, parameters) as string;
result = parameters[2] as Activity>;
return errorString;
}
catch (TargetInvocationException e)
{
throw FxTrace.Exception.AsError(e.InnerException);
}
}
static string TryConvertReferenceMemberExpressionWorker(MemberExpression memberExpressionBody, bool throwOnError, out Activity> result)
{
result = null;
Activity operand = null;
Activity> operandReference = null;
bool isValueType = typeof(TOperand).IsValueType;
if (memberExpressionBody.Expression != null)
{
// Static property might not have any expressions.
if (!isValueType)
{
string operandError = TryConvert(memberExpressionBody.Expression, throwOnError, out operand);
if (operandError != null)
{
return operandError;
}
}
else
{
string operandError = TryConvertReference(memberExpressionBody.Expression, throwOnError, out operandReference);
if (operandError != null)
{
return operandError;
}
}
}
if (memberExpressionBody.Member is PropertyInfo)
{
if (!isValueType)
{
if (operand == null)
{
result = new PropertyReference { PropertyName = memberExpressionBody.Member.Name };
}
else
{
result = new PropertyReference { Operand = operand, PropertyName = memberExpressionBody.Member.Name };
}
}
else
{
if (operandReference == null)
{
result = new ValueTypePropertyReference { PropertyName = memberExpressionBody.Member.Name };
}
else
{
result = new ValueTypePropertyReference { OperandLocation = operandReference, PropertyName = memberExpressionBody.Member.Name };
}
}
return null;
}
if (memberExpressionBody.Member is FieldInfo)
{
if (!isValueType)
{
if (operand == null)
{
result = new FieldReference { FieldName = memberExpressionBody.Member.Name };
}
else
{
result = new FieldReference { Operand = operand, FieldName = memberExpressionBody.Member.Name };
}
}
else
{
if (operandReference == null)
{
result = new ValueTypeFieldReference { FieldName = memberExpressionBody.Member.Name };
}
else
{
result = new ValueTypeFieldReference { OperandLocation = operandReference, FieldName = memberExpressionBody.Member.Name };
}
}
return null;
}
if (throwOnError)
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedMemberExpressionWithType(memberExpressionBody.Member.GetType().Name)));
}
else
{
return SR.UnsupportedMemberExpressionWithType(memberExpressionBody.Member.GetType().Name);
}
}
static string TryConvertOverloadingUnaryOperator(UnaryExpression unaryExpression, Activity operand, bool throwOnError, out Activity result)
{
result = null;
if (!unaryExpression.Method.IsStatic)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.OverloadingMethodMustBeStatic));
}
else
{
return SR.OverloadingMethodMustBeStatic;
}
}
result = new InvokeMethod
{
MethodName = unaryExpression.Method.Name,
TargetType = unaryExpression.Method.DeclaringType,
Parameters = {new InArgument { Expression = operand}},
};
return null;
}
static string TryConvertOverloadingBinaryOperator(BinaryExpression binaryExpression, Activity left, Activity right, bool throwOnError, out Activity result)
{
result = null;
if (!binaryExpression.Method.IsStatic)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.OverloadingMethodMustBeStatic));
}
else
{
return SR.OverloadingMethodMustBeStatic;
}
}
result = new InvokeMethod
{
MethodName = binaryExpression.Method.Name,
TargetType = binaryExpression.Method.DeclaringType,
Parameters = {new InArgument{Expression = left, EvaluationOrder = 0}, new InArgument{Expression = right, EvaluationOrder = 1}},
};
return null;
}
static string TryConvertMethodCallExpression(MethodCallExpression methodCallExpression, bool throwOnError, out Activity result)
{
result = null;
MethodInfo methodInfo = methodCallExpression.Method;
if (methodInfo == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.MethodInfoRequired(methodCallExpression.GetType().Name)));
}
else
{
return SR.MethodInfoRequired(methodCallExpression.GetType().Name);
}
};
if (string.IsNullOrEmpty(methodInfo.Name) || methodInfo.DeclaringType == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.MethodNameRequired(methodInfo.GetType().Name)));
}
else
{
return SR.MethodNameRequired(methodInfo.GetType().Name);
}
}
InvokeMethod invokeMethod = new InvokeMethod
{
MethodName = methodInfo.Name,
};
ParameterInfo[] parameterInfoArray = methodInfo.GetParameters();
if (methodCallExpression.Arguments.Count != parameterInfoArray.Length)//no optional argument call for LINQ expression
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentNumberRequiresTheSameAsParameterNumber(methodCallExpression.GetType().Name)));
}
else
{
return SR.ArgumentNumberRequiresTheSameAsParameterNumber(methodCallExpression.GetType().Name);
}
}
string error = TryConvertArguments(methodCallExpression.Arguments, invokeMethod.Parameters, methodCallExpression.GetType(), 1, parameterInfoArray, throwOnError);
if (error != null)
{
return error;
}
foreach (Type type in methodInfo.GetGenericArguments())
{
if (type == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.InvalidGenericTypeInfo(methodCallExpression.GetType().Name)));
}
else
{
return SR.InvalidGenericTypeInfo(methodCallExpression.GetType().Name);
}
}
invokeMethod.GenericTypeArguments.Add(type);
}
if (methodInfo.IsStatic)
{
invokeMethod.TargetType = methodInfo.DeclaringType;
}
else
{
if (methodCallExpression.Object == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.InstanceMethodCallRequiresTargetObject));
}
else
{
return SR.InstanceMethodCallRequiresTargetObject;
}
}
object[] parameters = new object[] { methodCallExpression.Object, false, throwOnError, null };
error = TryConvertArgumentExpressionHandle.MakeGenericMethod(methodCallExpression.Object.Type).Invoke(null, parameters) as string;
if (error != null)
{
return error;
}
InArgument argument = (InArgument)parameters[3];
argument.EvaluationOrder = 0;
invokeMethod.TargetObject = argument;
}
result = invokeMethod;
return null;
}
static string TryConvertInvocationExpression(InvocationExpression invocationExpression, bool throwOnError, out Activity result)
{
result = null;
if (invocationExpression.Expression == null || invocationExpression.Expression.Type == null)
{
if (throwOnError)
{
throw FxTrace.Exception.AsError(new ValidationException(SR.InvalidExpressionProperty(invocationExpression.GetType().Name)));
}
else
{
return SR.InvalidExpressionProperty(invocationExpression.GetType().Name);
}
}
InvokeMethod invokeMethod = new InvokeMethod
{
MethodName = "Invoke",
};
object[] parameters = new object[] { invocationExpression.Expression, false, throwOnError, null };
string error = TryConvertArgumentExpressionHandle.MakeGenericMethod(invocationExpression.Expression.Type).Invoke(null, parameters) as string;
if (error != null)
{
return error;
}
InArgument argument = (InArgument)parameters[3];
argument.EvaluationOrder = 0;
invokeMethod.TargetObject = argument;
//InvocationExpression can not have a by-ref parameter.
error = TryConvertArguments(invocationExpression.Arguments, invokeMethod.Parameters, invocationExpression.GetType(), 1, null, throwOnError);
if (error != null)
{
return error;
}
result = invokeMethod;
return null;
}
static string TryConvertArgumentExpressionWorker(Expression expression, bool isByRef, bool throwOnError, out System.Activities.Argument result)
{
result = null;
string error = null;
if (isByRef)
{
Activity> argument;
error = TryConvertReference(expression, throwOnError, out argument);
if (error == null)
{
result = new InOutArgument
{
Expression = argument,
};
}
}
else
{
Activity argument;
error = TryConvert(expression, throwOnError, out argument);
if (error == null)
{
result = new InArgument