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 ActivityConvert (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