e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1601 lines
72 KiB
C#
1601 lines
72 KiB
C#
//----------------------------------------------------------------
|
|
// 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<TResult> Convert<TResult>(Expression<Func<ActivityContext, TResult>> expression)
|
|
{
|
|
Activity<TResult> result;
|
|
if (expression == null)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ArgumentNullException("expression", SR.ExpressionRequiredForConversion));
|
|
}
|
|
TryConvert<TResult>(expression.Body, true, out result);
|
|
return result;
|
|
}
|
|
|
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
|
Justification = "The parameter is restricted correctly.")]
|
|
public static bool TryConvert<TResult>(Expression<Func<ActivityContext, TResult>> expression, out Activity<TResult> result)
|
|
{
|
|
if (expression == null)
|
|
{
|
|
result = null;
|
|
return false;
|
|
}
|
|
return TryConvert<TResult>(expression.Body, false, out result) == null;
|
|
}
|
|
|
|
static string TryConvert<TResult>(Expression body, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
UnaryExpression unaryExpressionBody = body as UnaryExpression;
|
|
if (unaryExpressionBody != null)
|
|
{
|
|
Type operandType = unaryExpressionBody.Operand.Type;
|
|
Type resultType = typeof(TResult);
|
|
return TryConvertUnaryExpression<TResult>(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<TResult>(binaryExpressionBody, leftType, rightType, throwOnError, out result);
|
|
}
|
|
return TryConvertBinaryExpression<TResult>(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<TResult>(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<TResult>(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<TResult>(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<TResult>(methodCallExpressionBody, throwOnError, out result);
|
|
}
|
|
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<TResult>(memberExpression, throwOnError, out result);
|
|
}
|
|
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(ActivityContext)) && calledMethod.Name == "GetValue" && parameters.Length == 1 &&
|
|
(TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(LocationReference))))
|
|
{
|
|
return TryConvertLocationReference<TResult>(methodCallExpressionBody, throwOnError, out result);
|
|
}
|
|
else
|
|
{
|
|
return TryConvertMethodCallExpression<TResult>(methodCallExpressionBody, throwOnError, out result);
|
|
}
|
|
}
|
|
InvocationExpression invocationExpression = body as InvocationExpression;
|
|
if (invocationExpression != null)
|
|
{
|
|
return TryConvertInvocationExpression<TResult>(invocationExpression, throwOnError, out result);
|
|
}
|
|
NewExpression newExpression = body as NewExpression;
|
|
if (newExpression != null)
|
|
{
|
|
return TryConvertNewExpression<TResult>(newExpression, throwOnError, out result);
|
|
}
|
|
NewArrayExpression newArrayExpression = body as NewArrayExpression;
|
|
if (newArrayExpression != null && newArrayExpression.NodeType != ExpressionType.NewArrayInit)
|
|
{
|
|
return TryConvertNewArrayExpression<TResult>(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<TResult> { 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<Location<TResult>> ConvertReference<TResult>(Expression<Func<ActivityContext, TResult>> expression)
|
|
{
|
|
Activity<Location<TResult>> result;
|
|
if (expression == null)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ArgumentNullException("expression", SR.ExpressionRequiredForConversion));
|
|
}
|
|
|
|
TryConvertReference<TResult>(expression.Body, true, out result);
|
|
return result;
|
|
}
|
|
|
|
[SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
|
|
Justification = "The parameter is restricted correctly.")]
|
|
public static bool TryConvertReference<TResult>(Expression<Func<ActivityContext, TResult>> expression, out Activity<Location<TResult>> result)
|
|
{
|
|
if (expression == null)
|
|
{
|
|
result = null;
|
|
return false;
|
|
}
|
|
return TryConvertReference<TResult>(expression.Body, false, out result) == null;
|
|
}
|
|
|
|
static string TryConvertReference<TResult>(Expression body, bool throwOnError, out Activity<Location<TResult>> result)
|
|
{
|
|
result = null;
|
|
MemberExpression memberExpressionBody = body as MemberExpression;
|
|
if (memberExpressionBody != null)
|
|
{
|
|
Type memberType = memberExpressionBody.Expression == null ? memberExpressionBody.Member.DeclaringType : memberExpressionBody.Expression.Type;
|
|
return TryConvertReferenceMemberExpression<TResult>(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<TResult>(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<TResult>(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<TResult>(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<TResult>(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<TResult>(methodCallExpressionBody, throwOnError, out result);
|
|
}
|
|
else if (TypeHelper.AreTypesCompatible(declaringType, typeof(ActivityContext)) && calledMethod.Name == "GetValue" && parameters.Length == 1 &&
|
|
(TypeHelper.AreTypesCompatible(parameters[0].ParameterType, typeof(LocationReference))))
|
|
{
|
|
return TryConvertReferenceLocationReference<TResult>(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<TResult>(MethodCallExpression methodCallExpressionBody, bool throwOnError, out Activity<Location<TResult>> 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<Location<TResult>>;
|
|
return errorString;
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
throw FxTrace.Exception.AsError(e.InnerException);
|
|
}
|
|
}
|
|
|
|
static string TryConvertIndexerReferenceWorker<TOperand, TResult>(MethodCallExpression methodCallExpressionBody, bool throwOnError, out Activity<Location<TResult>> result)
|
|
{
|
|
result = null;
|
|
Fx.Assert(methodCallExpressionBody.Object != null, "Indexer must have a target object");
|
|
if (!typeof(TOperand).IsValueType)
|
|
{
|
|
Activity<TOperand> operand = null;
|
|
string operandError = TryConvert<TOperand>(methodCallExpressionBody.Object, throwOnError, out operand);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
IndexerReference<TOperand, TResult> indexerReference = new IndexerReference<TOperand, TResult>
|
|
{
|
|
Operand = new InArgument<TOperand>(operand) { EvaluationOrder = 0 },
|
|
};
|
|
string argumentError = TryConvertArguments(methodCallExpressionBody.Arguments, indexerReference.Indices, methodCallExpressionBody.GetType(), 1, null, throwOnError);
|
|
if (argumentError != null)
|
|
{
|
|
return argumentError;
|
|
}
|
|
result = indexerReference;
|
|
|
|
}
|
|
else
|
|
{
|
|
Activity<Location<TOperand>> operandReference = null;
|
|
string operandError = TryConvertReference<TOperand>(methodCallExpressionBody.Object, throwOnError, out operandReference);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
ValueTypeIndexerReference<TOperand, TResult> indexerReference = new ValueTypeIndexerReference<TOperand, TResult>
|
|
{
|
|
OperandLocation = new InOutArgument<TOperand>(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<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<Location<TResult>> result)
|
|
{
|
|
result = null;
|
|
Activity<Array> operand;
|
|
if (methodCallExpression.Object == null)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.InstanceMethodCallRequiresTargetObject));
|
|
}
|
|
else
|
|
{
|
|
return SR.InstanceMethodCallRequiresTargetObject;
|
|
}
|
|
}
|
|
string errorString = TryConvert<Array>(methodCallExpression.Object, throwOnError, out operand);
|
|
if (errorString != null)
|
|
{
|
|
return errorString;
|
|
}
|
|
|
|
MultidimensionalArrayItemReference<TResult> reference = new MultidimensionalArrayItemReference<TResult>
|
|
{
|
|
Array = new InArgument<Array>(operand) { EvaluationOrder = 0 },
|
|
};
|
|
|
|
Collection<InArgument<int>> 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<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<Location<TResult>> 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<TResult> { 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<string> { Default = "Hello" };
|
|
//Activity<Location<string>> weRef = ExpressionServices.ConvertReference<string>((env) => Program.static_X.Get(env));
|
|
//Program.static_X = new Variable<string> { Default = "World" };
|
|
//Sequence sequence = new Sequence
|
|
//{
|
|
// Variables = { Program.static_X },
|
|
// Activities =
|
|
// {
|
|
// new Assign<string>
|
|
// {
|
|
// To = new OutArgument<string>{Expression = weRef},
|
|
// Value = "haha",
|
|
// },
|
|
// new WriteLine
|
|
// {
|
|
// Text = Program.static_X,
|
|
// }
|
|
// }
|
|
//};
|
|
//WorkflowInvoker.Invoke(sequence);
|
|
//
|
|
// The reason is that "Program.static_X = new Variable<string> { Default = "World" }" happens after conversion.
|
|
try
|
|
{
|
|
Expression<Func<Variable>> funcExpression = Expression.Lambda<Func<Variable>>(methodCallExpression.Object);
|
|
Func<Variable> 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<TResult>, "Linq generated expression tree should be correct");
|
|
result = new VariableReference<TResult> { Variable = variableObject };
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertArrayItemReference<TResult>(BinaryExpression binaryExpression, Type leftType, Type rightType, bool throwOnError, out Activity<Location<TResult>> 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<Func<ActivityContext, DerivedClass> 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<TResult[]> array;
|
|
string arrayError = TryConvert<TResult[]>(binaryExpression.Left, throwOnError, out array);
|
|
if (arrayError != null)
|
|
{
|
|
return arrayError;
|
|
}
|
|
|
|
Activity<int> index;
|
|
string indexError = TryConvert<int>(binaryExpression.Right, throwOnError, out index);
|
|
if (indexError != null)
|
|
{
|
|
return indexError;
|
|
}
|
|
|
|
result = new ArrayItemReference<TResult>
|
|
{
|
|
Array = new InArgument<TResult[]>(array) { EvaluationOrder = 0 },
|
|
Index = new InArgument<int>(index) { EvaluationOrder = 1 },
|
|
};
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertVariableValue<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<TResult> 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<TResult> { 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<string> { Default = "Hello" };
|
|
// Activity<string> we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
|
|
// Program.static_X = new Variable<string> { Default = "World" };
|
|
// Sequence sequence = new Sequence
|
|
// {
|
|
// Variables = { Program.static_X },
|
|
// Activities =
|
|
// {
|
|
// new WriteLine
|
|
// {
|
|
// Text = new InArgument<string>{Expression = we},
|
|
// }
|
|
// }
|
|
// };
|
|
// WorkflowInvoker.Invoke(sequence);
|
|
//
|
|
// The reason is that "Program.static_X = new Variable<string> { Default = "World" }" happens after conversion.
|
|
|
|
try
|
|
{
|
|
Expression<Func<Variable>> funcExpression = Expression.Lambda<Func<Variable>>(methodCallExpression.Object);
|
|
Func<Variable> 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<TResult> { Variable = variableObject };
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertDelegateArgumentValue<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<TResult> 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<string>();
|
|
// Activity<string> we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
|
|
// Program.static_X = new DelegateInArgument<string>();
|
|
// ActivityAction<string> activityAction = new ActivityAction<string>
|
|
// {
|
|
// Argument = Program.static_X,
|
|
// Handler = new WriteLine
|
|
// {
|
|
// Text = we,
|
|
// }
|
|
// }
|
|
// };
|
|
// WorkflowInvoker.Invoke( new InvokeAction<string>
|
|
// {
|
|
// Argument = "Hello",
|
|
// Action = activityAction,
|
|
// }
|
|
//);
|
|
//
|
|
// The reason is that "Program.static_X" is changed after conversion.
|
|
|
|
try
|
|
{
|
|
Expression<Func<DelegateArgument>> funcExpression = Expression.Lambda<Func<DelegateArgument>>(methodCallExpression.Object);
|
|
Func<DelegateArgument> 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<TResult>(delegateArgument);
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertDelegateArgumentReference<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<Location<TResult>> 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<string>();
|
|
// Activity<string> we = ExpressionServices.Convert((env) => Program.static_X.Get(env));
|
|
// Program.static_X = new DelegateInArgument<string>();
|
|
// ActivityAction<string> activityAction = new ActivityAction<string>
|
|
// {
|
|
// Argument = Program.static_X,
|
|
// Handler = new WriteLine
|
|
// {
|
|
// Text = we,
|
|
// }
|
|
// }
|
|
// };
|
|
// WorkflowInvoker.Invoke( new InvokeAction<string>
|
|
// {
|
|
// Argument = "Hello",
|
|
// Action = activityAction,
|
|
// }
|
|
//);
|
|
//
|
|
// The reason is that "Program.static_X" is changed after conversion.
|
|
|
|
try
|
|
{
|
|
Expression<Func<DelegateArgument>> funcExpression = Expression.Lambda<Func<DelegateArgument>>(methodCallExpression.Object);
|
|
Func<DelegateArgument> 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<TResult>(delegateArgument);
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertArgumentValue<TResult>(MemberExpression memberExpression, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
|
|
if (memberExpression != null && TypeHelper.AreTypesCompatible(memberExpression.Type, typeof(RuntimeArgument)))
|
|
{
|
|
RuntimeArgument ra = null;
|
|
try
|
|
{
|
|
Expression<Func<RuntimeArgument>> expr = Expression.Lambda<Func<RuntimeArgument>>(memberExpression, null);
|
|
Func<RuntimeArgument> func = expr.Compile();
|
|
ra = func();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
return e.Message;
|
|
}
|
|
|
|
if (ra != null)
|
|
{
|
|
result = new ArgumentValue<TResult>
|
|
{
|
|
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<TResult> { ArgumentName = property.Name };
|
|
return null;
|
|
}
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentMustbePropertyofWorkflowElement));
|
|
}
|
|
else
|
|
{
|
|
return SR.ArgumentMustbePropertyofWorkflowElement;
|
|
}
|
|
}
|
|
}
|
|
|
|
static string TryConvertArgumentReference<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<Location<TResult>> 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<TResult> { ArgumentName = property.Name };
|
|
return null;
|
|
}
|
|
}
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentMustbePropertyofWorkflowElement));
|
|
}
|
|
else
|
|
{
|
|
return SR.ArgumentMustbePropertyofWorkflowElement;
|
|
}
|
|
}
|
|
|
|
static string TryConvertBinaryExpression<TResult>(BinaryExpression binaryExpressionBody, Type leftType, Type rightType, bool throwOnError, out Activity<TResult> 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<TResult>;
|
|
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<TResult>(BinaryExpression binaryExpression, Type leftType, Type rightType, bool throwOnError, out Activity<TResult> 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<TResult[]> array;
|
|
string arrayError = TryConvert<TResult[]>(binaryExpression.Left, throwOnError, out array);
|
|
if (arrayError != null)
|
|
{
|
|
return arrayError;
|
|
}
|
|
|
|
Activity<int> index;
|
|
string indexError = TryConvert<int>(binaryExpression.Right, throwOnError, out index);
|
|
if (indexError != null)
|
|
{
|
|
return indexError;
|
|
}
|
|
|
|
result = new ArrayItemValue<TResult>
|
|
{
|
|
Array = new InArgument<TResult[]>(array) { EvaluationOrder = 0 },
|
|
Index = new InArgument<int>(index) { EvaluationOrder = 1 },
|
|
};
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertBinaryExpressionWorker<TLeft, TRight, TResult>(BinaryExpression binaryExpressionBody, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
|
|
Activity<TLeft> left;
|
|
string leftError = TryConvert<TLeft>(binaryExpressionBody.Left, throwOnError, out left);
|
|
if (leftError != null)
|
|
{
|
|
return leftError;
|
|
}
|
|
Activity<TRight> right;
|
|
string rightError = TryConvert<TRight>(binaryExpressionBody.Right, throwOnError, out right);
|
|
if (rightError != null)
|
|
{
|
|
return rightError;
|
|
}
|
|
|
|
if (binaryExpressionBody.Method != null)
|
|
{
|
|
return TryConvertOverloadingBinaryOperator<TLeft, TRight, TResult>(binaryExpressionBody, left, right, throwOnError, out result);
|
|
}
|
|
|
|
InArgument<TLeft> leftArgument = new InArgument<TLeft>(left) { EvaluationOrder = 0 };
|
|
InArgument<TRight> rightArgument = new InArgument<TRight>(right) { EvaluationOrder = 1 };
|
|
|
|
switch (binaryExpressionBody.NodeType)
|
|
{
|
|
case ExpressionType.Add:
|
|
result = new Add<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = false };
|
|
break;
|
|
case ExpressionType.AddChecked:
|
|
result = new Add<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = true };
|
|
break;
|
|
case ExpressionType.Subtract:
|
|
result = new Subtract<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = false };
|
|
break;
|
|
case ExpressionType.SubtractChecked:
|
|
result = new Subtract<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = true };
|
|
break;
|
|
case ExpressionType.Multiply:
|
|
result = new Multiply<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = false };
|
|
break;
|
|
case ExpressionType.MultiplyChecked:
|
|
result = new Multiply<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument, Checked = true };
|
|
break;
|
|
case ExpressionType.Divide:
|
|
result = new Divide<TLeft, TRight, TResult>() { 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<bool>)leftObject1, Right = (Activity<bool>)rightObject1 };
|
|
result = (Activity<TResult>)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<bool>)leftObject2, Right = (Activity<bool>)rightObject2 };
|
|
result = (Activity<TResult>)resultObject2;
|
|
break;
|
|
case ExpressionType.Or:
|
|
result = new Or<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.And:
|
|
result = new And<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.LessThan:
|
|
result = new LessThan<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.LessThanOrEqual:
|
|
result = new LessThanOrEqual<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.GreaterThan:
|
|
result = new GreaterThan<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.GreaterThanOrEqual:
|
|
result = new GreaterThanOrEqual<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.Equal:
|
|
result = new Equal<TLeft, TRight, TResult>() { Left = leftArgument, Right = rightArgument };
|
|
break;
|
|
case ExpressionType.NotEqual:
|
|
result = new NotEqual<TLeft, TRight, TResult>() { 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<TResult>(UnaryExpression unaryExpressionBody, Type operandType, bool throwOnError, out Activity<TResult> 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<TResult>;
|
|
return errorString;
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
throw FxTrace.Exception.AsError(e.InnerException);
|
|
}
|
|
}
|
|
|
|
static string TryConvertUnaryExpressionWorker<TOperand, TResult>(UnaryExpression unaryExpressionBody, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
|
|
Activity<TOperand> operand;
|
|
string operandError = TryConvert<TOperand>(unaryExpressionBody.Operand, throwOnError, out operand);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
|
|
if (unaryExpressionBody.Method != null)
|
|
{
|
|
return TryConvertOverloadingUnaryOperator<TOperand, TResult>(unaryExpressionBody, operand, throwOnError, out result);
|
|
}
|
|
|
|
switch (unaryExpressionBody.NodeType)
|
|
{
|
|
case ExpressionType.Not:
|
|
result = new Not<TOperand, TResult> { Operand = operand };
|
|
break;
|
|
case ExpressionType.Convert:
|
|
result = new Cast<TOperand, TResult> { Operand = operand, Checked = false };
|
|
break;
|
|
case ExpressionType.ConvertChecked:
|
|
result = new Cast<TOperand, TResult> { Operand = operand, Checked = true };
|
|
break;
|
|
case ExpressionType.TypeAs:
|
|
result = new As<TOperand, TResult> { 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<TResult>(MemberExpression memberExpressionBody, Type operandType, bool throwOnError, out Activity<TResult> 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<TResult>;
|
|
return errorString;
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
throw FxTrace.Exception.AsError(e.InnerException);
|
|
}
|
|
}
|
|
|
|
static string TryConvertMemberExpressionWorker<TOperand, TResult>(MemberExpression memberExpressionBody, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
Activity<TOperand> operand = null;
|
|
if (memberExpressionBody.Expression != null)
|
|
{
|
|
// Static property might not have any expressions.
|
|
string operandError = TryConvert<TOperand>(memberExpressionBody.Expression, throwOnError, out operand);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
}
|
|
if (memberExpressionBody.Member is PropertyInfo)
|
|
{
|
|
if (operand == null)
|
|
{
|
|
result = new PropertyValue<TOperand, TResult> { PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new PropertyValue<TOperand, TResult> { Operand = operand, PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
return null;
|
|
}
|
|
else if (memberExpressionBody.Member is FieldInfo)
|
|
{
|
|
if (operand == null)
|
|
{
|
|
result = new FieldValue<TOperand, TResult> { FieldName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new FieldValue<TOperand, TResult> { 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<TResult>(MemberExpression memberExpressionBody, Type operandType, bool throwOnError, out Activity<Location<TResult>> 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<Location<TResult>>;
|
|
return errorString;
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
throw FxTrace.Exception.AsError(e.InnerException);
|
|
}
|
|
}
|
|
|
|
static string TryConvertReferenceMemberExpressionWorker<TOperand, TResult>(MemberExpression memberExpressionBody, bool throwOnError, out Activity<Location<TResult>> result)
|
|
{
|
|
result = null;
|
|
Activity<TOperand> operand = null;
|
|
Activity<Location<TOperand>> operandReference = null;
|
|
bool isValueType = typeof(TOperand).IsValueType;
|
|
if (memberExpressionBody.Expression != null)
|
|
{
|
|
// Static property might not have any expressions.
|
|
if (!isValueType)
|
|
{
|
|
string operandError = TryConvert<TOperand>(memberExpressionBody.Expression, throwOnError, out operand);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string operandError = TryConvertReference<TOperand>(memberExpressionBody.Expression, throwOnError, out operandReference);
|
|
if (operandError != null)
|
|
{
|
|
return operandError;
|
|
}
|
|
}
|
|
}
|
|
if (memberExpressionBody.Member is PropertyInfo)
|
|
{
|
|
if (!isValueType)
|
|
{
|
|
if (operand == null)
|
|
{
|
|
result = new PropertyReference<TOperand, TResult> { PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new PropertyReference<TOperand, TResult> { Operand = operand, PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (operandReference == null)
|
|
{
|
|
result = new ValueTypePropertyReference<TOperand, TResult> { PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new ValueTypePropertyReference<TOperand, TResult> { OperandLocation = operandReference, PropertyName = memberExpressionBody.Member.Name };
|
|
}
|
|
|
|
}
|
|
return null;
|
|
}
|
|
if (memberExpressionBody.Member is FieldInfo)
|
|
{
|
|
if (!isValueType)
|
|
{
|
|
if (operand == null)
|
|
{
|
|
result = new FieldReference<TOperand, TResult> { FieldName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new FieldReference<TOperand, TResult> { Operand = operand, FieldName = memberExpressionBody.Member.Name };
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (operandReference == null)
|
|
{
|
|
result = new ValueTypeFieldReference<TOperand, TResult> { FieldName = memberExpressionBody.Member.Name };
|
|
}
|
|
else
|
|
{
|
|
result = new ValueTypeFieldReference<TOperand, TResult> { 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<TOperand, TResult>(UnaryExpression unaryExpression, Activity<TOperand> operand, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
if (!unaryExpression.Method.IsStatic)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.OverloadingMethodMustBeStatic));
|
|
}
|
|
else
|
|
{
|
|
return SR.OverloadingMethodMustBeStatic;
|
|
}
|
|
}
|
|
|
|
result = new InvokeMethod<TResult>
|
|
{
|
|
MethodName = unaryExpression.Method.Name,
|
|
TargetType = unaryExpression.Method.DeclaringType,
|
|
Parameters = { new InArgument<TOperand> { Expression = operand } },
|
|
};
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertOverloadingBinaryOperator<TLeft, TRight, TResult>(BinaryExpression binaryExpression, Activity<TLeft> left, Activity<TRight> right, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
if (!binaryExpression.Method.IsStatic)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.OverloadingMethodMustBeStatic));
|
|
}
|
|
else
|
|
{
|
|
return SR.OverloadingMethodMustBeStatic;
|
|
}
|
|
}
|
|
|
|
result = new InvokeMethod<TResult>
|
|
{
|
|
MethodName = binaryExpression.Method.Name,
|
|
TargetType = binaryExpression.Method.DeclaringType,
|
|
Parameters = { new InArgument<TLeft> { Expression = left, EvaluationOrder = 0 }, new InArgument<TRight> { Expression = right, EvaluationOrder = 1 } },
|
|
};
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertMethodCallExpression<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<TResult> 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<TResult> invokeMethod = new InvokeMethod<TResult>
|
|
{
|
|
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<TResult>(InvocationExpression invocationExpression, bool throwOnError, out Activity<TResult> 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<TResult> invokeMethod = new InvokeMethod<TResult>
|
|
{
|
|
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<TArgument>(Expression expression, bool isByRef, bool throwOnError, out System.Activities.Argument result)
|
|
{
|
|
result = null;
|
|
|
|
string error = null;
|
|
|
|
if (isByRef)
|
|
{
|
|
Activity<Location<TArgument>> argument;
|
|
error = TryConvertReference<TArgument>(expression, throwOnError, out argument);
|
|
if (error == null)
|
|
{
|
|
result = new InOutArgument<TArgument>
|
|
{
|
|
Expression = argument,
|
|
};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Activity<TArgument> argument;
|
|
error = TryConvert<TArgument>(expression, throwOnError, out argument);
|
|
if (error == null)
|
|
{
|
|
result = new InArgument<TArgument>
|
|
{
|
|
Expression = argument,
|
|
};
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static string TryConvertNewExpression<TResult>(NewExpression newExpression, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
New<TResult> newActivity = new New<TResult>();
|
|
ParameterInfo[] parameterInfoArray = null;
|
|
if (newExpression.Constructor != null)
|
|
{
|
|
parameterInfoArray = newExpression.Constructor.GetParameters();
|
|
if (newExpression.Arguments.Count != parameterInfoArray.Length)//no optional argument call for LINQ expression
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.ArgumentNumberRequiresTheSameAsParameterNumber(newExpression.GetType().Name)));
|
|
}
|
|
else
|
|
{
|
|
return SR.ArgumentNumberRequiresTheSameAsParameterNumber(newExpression.GetType().Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
string error = TryConvertArguments(newExpression.Arguments, newActivity.Arguments, newExpression.GetType(), 0, parameterInfoArray, throwOnError);
|
|
if (error != null)
|
|
{
|
|
return error;
|
|
}
|
|
result = newActivity;
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertNewArrayExpression<TResult>(NewArrayExpression newArrayExpression, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
NewArray<TResult> newArrayActivity = new NewArray<TResult>();
|
|
string error = TryConvertArguments(newArrayExpression.Expressions, newArrayActivity.Bounds, newArrayExpression.GetType(), 0, null, throwOnError);
|
|
if (error != null)
|
|
{
|
|
return error;
|
|
}
|
|
result = newArrayActivity;
|
|
return null;
|
|
|
|
}
|
|
|
|
static string TryConvertArguments(ReadOnlyCollection<Expression> source, IList target, Type expressionType, int baseEvaluationOrder, ParameterInfo[] parameterInfoArray, bool throwOnError)
|
|
{
|
|
object[] parameters;
|
|
for (int i = 0; i < source.Count; i++)
|
|
{
|
|
bool isByRef = false;
|
|
Expression expression = source[i];
|
|
if (parameterInfoArray != null)
|
|
{
|
|
ParameterInfo parameterInfo = parameterInfoArray[i];
|
|
|
|
if (parameterInfo == null || parameterInfo.ParameterType == null)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(SR.InvalidParameterInfo(i, expressionType.Name)));
|
|
}
|
|
else
|
|
{
|
|
return SR.InvalidParameterInfo(i, expressionType.Name);
|
|
}
|
|
}
|
|
isByRef = parameterInfo.ParameterType.IsByRef;
|
|
}
|
|
parameters = new object[] { expression, isByRef, throwOnError, null };
|
|
string error = TryConvertArgumentExpressionHandle.MakeGenericMethod(expression.Type).Invoke(null, parameters) as string;
|
|
if (error != null)
|
|
{
|
|
return error;
|
|
}
|
|
Argument argument = (Argument)parameters[3];
|
|
argument.EvaluationOrder = i + baseEvaluationOrder;
|
|
target.Add(argument);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertLocationReference<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<TResult> result)
|
|
{
|
|
result = null;
|
|
|
|
Expression expression = methodCallExpression.Arguments[0];
|
|
if (expression.NodeType != ExpressionType.Constant)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(
|
|
SR.UnexpectedExpressionNodeType(ExpressionType.Constant.ToString(), expression.NodeType.ToString())));
|
|
}
|
|
else
|
|
{
|
|
return SR.UnexpectedExpressionNodeType(ExpressionType.Constant.ToString(), expression.NodeType.ToString());
|
|
}
|
|
}
|
|
|
|
object value = ((ConstantExpression)expression).Value;
|
|
Type valueType = value.GetType();
|
|
|
|
if (typeof(RuntimeArgument).IsAssignableFrom(valueType))
|
|
{
|
|
RuntimeArgument runtimeArgument = (RuntimeArgument)value;
|
|
result = new ArgumentValue<TResult>
|
|
{
|
|
ArgumentName = runtimeArgument.Name,
|
|
};
|
|
}
|
|
else if (typeof(Variable).IsAssignableFrom(valueType))
|
|
{
|
|
Variable variable = (Variable)value;
|
|
result = new VariableValue<TResult> { Variable = variable };
|
|
}
|
|
else if (typeof(DelegateArgument).IsAssignableFrom(valueType))
|
|
{
|
|
DelegateArgument delegateArgument = (DelegateArgument)value;
|
|
result = new DelegateArgumentValue<TResult>
|
|
{
|
|
DelegateArgument = delegateArgument
|
|
};
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedLocationReferenceValue));
|
|
}
|
|
else
|
|
{
|
|
return SR.UnsupportedLocationReferenceValue;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static string TryConvertReferenceLocationReference<TResult>(MethodCallExpression methodCallExpression, bool throwOnError, out Activity<Location<TResult>> result)
|
|
{
|
|
result = null;
|
|
|
|
Expression expression = methodCallExpression.Arguments[0];
|
|
if (expression.NodeType != ExpressionType.Constant)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new ValidationException(
|
|
SR.UnexpectedExpressionNodeType(ExpressionType.Constant.ToString(), expression.NodeType.ToString())));
|
|
}
|
|
else
|
|
{
|
|
return SR.UnexpectedExpressionNodeType(ExpressionType.Constant.ToString(), expression.NodeType.ToString());
|
|
}
|
|
}
|
|
|
|
object value = ((ConstantExpression)expression).Value;
|
|
Type valueType = value.GetType();
|
|
|
|
if (typeof(RuntimeArgument).IsAssignableFrom(valueType))
|
|
{
|
|
RuntimeArgument runtimeArgument = (RuntimeArgument)value;
|
|
result = new ArgumentReference<TResult>
|
|
{
|
|
ArgumentName = runtimeArgument.Name,
|
|
};
|
|
}
|
|
else if (typeof(Variable).IsAssignableFrom(valueType))
|
|
{
|
|
Variable variable = (Variable)value;
|
|
result = new VariableReference<TResult> { Variable = variable };
|
|
}
|
|
else if (typeof(DelegateArgument).IsAssignableFrom(valueType))
|
|
{
|
|
DelegateArgument delegateArgument = (DelegateArgument)value;
|
|
result = new DelegateArgumentReference<TResult>
|
|
{
|
|
DelegateArgument = delegateArgument
|
|
};
|
|
}
|
|
|
|
if (result == null && throwOnError)
|
|
{
|
|
if (throwOnError)
|
|
{
|
|
throw FxTrace.Exception.AsError(new NotSupportedException(SR.UnsupportedLocationReferenceValue));
|
|
}
|
|
else
|
|
{
|
|
return SR.UnsupportedLocationReferenceValue;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|