You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
3521 lines
164 KiB
C#
3521 lines
164 KiB
C#
// ---------------------------------------------------------------------------
|
|
// Copyright (C) 2006 Microsoft Corporation All Rights Reserved
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define CODE_ANALYSIS
|
|
using System.CodeDom;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.ComponentModel.Compiler;
|
|
using System.Workflow.Activities.Common;
|
|
|
|
namespace System.Workflow.Activities.Rules
|
|
{
|
|
public interface IRuleExpression
|
|
{
|
|
RuleExpressionInfo Validate(RuleValidation validation, bool isWritten);
|
|
RuleExpressionResult Evaluate(RuleExecution execution);
|
|
void AnalyzeUsage(RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
|
|
[SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", MessageId = "0#")]
|
|
void Decompile(StringBuilder stringBuilder, CodeExpression parentExpression);
|
|
bool Match(CodeExpression expression);
|
|
CodeExpression Clone();
|
|
}
|
|
|
|
internal abstract class RuleExpressionInternal
|
|
{
|
|
internal abstract RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten);
|
|
internal abstract RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution);
|
|
internal abstract void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier);
|
|
internal abstract void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression);
|
|
internal abstract bool Match(CodeExpression leftExpression, CodeExpression rightExpression);
|
|
internal abstract CodeExpression Clone(CodeExpression expression);
|
|
}
|
|
|
|
|
|
#region "this" expression
|
|
|
|
// CodeThisReferenceExpression
|
|
internal class ThisExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
if (isWritten)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeThisReferenceExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = expression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
return new RuleExpressionInfo(validation.ThisType);
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
if (analysis.ForWrites && !isWritten) // If we're tracking writes, then ignore things that aren't written.
|
|
return;
|
|
else if (!analysis.ForWrites && !isRead) // ... and vice-versa
|
|
return;
|
|
|
|
StringBuilder sb = new StringBuilder("this/");
|
|
for (RulePathQualifier q = qualifier; q != null; q = q.Next)
|
|
{
|
|
sb.Append(q.Name);
|
|
if (q.Name == "*")
|
|
{
|
|
if (q.Next != null)
|
|
throw new NotSupportedException(Messages.InvalidWildCardInPathQualifier);
|
|
}
|
|
else
|
|
{
|
|
sb.Append("/");
|
|
}
|
|
}
|
|
|
|
// Add the symbol to our set.
|
|
analysis.AddSymbol(sb.ToString());
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
return execution.ThisLiteralResult;
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
stringBuilder.Append("this");
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
return new CodeThisReferenceExpression();
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
// We already verified their types match.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Primitive expression
|
|
|
|
// CodePrimitiveExpression
|
|
internal class PrimitiveExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
if (isWritten)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodePrimitiveExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = expression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
|
|
Type resultType = (primitiveExpr.Value != null) ? primitiveExpr.Value.GetType() : typeof(NullLiteral);
|
|
return new RuleExpressionInfo(resultType);
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
// Literal values have no interesting dependencies or side-effects.
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
|
|
return new RuleLiteralResult(primitiveExpr.Value);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
|
|
RuleDecompiler.DecompileObjectLiteral(stringBuilder, primitiveExpr.Value);
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
|
|
object clonedValue = ConditionHelper.CloneObject(primitiveExpr.Value);
|
|
return new CodePrimitiveExpression(clonedValue);
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodePrimitiveExpression primitiveExpr = (CodePrimitiveExpression)expression;
|
|
CodePrimitiveExpression comperandPrimitive = (CodePrimitiveExpression)comperand;
|
|
|
|
if (primitiveExpr.Value == comperandPrimitive.Value)
|
|
return true;
|
|
|
|
if (primitiveExpr.Value == null || comperandPrimitive.Value == null)
|
|
return false;
|
|
|
|
return primitiveExpr.Value.Equals(comperandPrimitive.Value);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Binary expression
|
|
|
|
// CodeBinaryOperatorExpression
|
|
internal class BinaryExpression : RuleExpressionInternal
|
|
{
|
|
#region Validate
|
|
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
ValidationError error;
|
|
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(binaryExpr))
|
|
return null;
|
|
|
|
if (isWritten)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeBinaryOperatorExpression).ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
|
|
RuleExpressionInfo lhsExprInfo = null;
|
|
RuleExpressionInfo rhsExprInfo = null;
|
|
|
|
if (binaryExpr.Left == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_LeftOperandMissing);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
else
|
|
{
|
|
if (binaryExpr.Left is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Left.GetType().FullName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Left;
|
|
validation.AddError(error);
|
|
return null;
|
|
}
|
|
|
|
lhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Left, false);
|
|
}
|
|
|
|
if (binaryExpr.Right == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_RightOperandMissing);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
else
|
|
{
|
|
if (binaryExpr.Right is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, binaryExpr.Right.GetType().FullName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr.Right;
|
|
validation.AddError(error);
|
|
return null;
|
|
}
|
|
|
|
rhsExprInfo = RuleExpressionWalker.Validate(validation, binaryExpr.Right, false);
|
|
}
|
|
|
|
validation.PopParentExpression();
|
|
|
|
RuleBinaryExpressionInfo resultExprInfo = null;
|
|
|
|
if (lhsExprInfo != null && rhsExprInfo != null)
|
|
{
|
|
Type lhsType = lhsExprInfo.ExpressionType;
|
|
Type rhsType = rhsExprInfo.ExpressionType;
|
|
|
|
switch (binaryExpr.Operator)
|
|
{
|
|
case CodeBinaryOperatorType.Add:
|
|
case CodeBinaryOperatorType.Subtract:
|
|
case CodeBinaryOperatorType.Multiply:
|
|
case CodeBinaryOperatorType.Divide:
|
|
case CodeBinaryOperatorType.Modulus:
|
|
case CodeBinaryOperatorType.BitwiseAnd:
|
|
case CodeBinaryOperatorType.BitwiseOr:
|
|
resultExprInfo = ArithmeticLiteral.ResultType(binaryExpr.Operator, lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, validation, out error);
|
|
if (resultExprInfo == null)
|
|
{
|
|
// check if constants are used with ulongs, as we should do some extra "conversions"
|
|
if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
|
|
|| ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
|
|
{
|
|
resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(ulong));
|
|
}
|
|
else
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.IdentityEquality:
|
|
case CodeBinaryOperatorType.IdentityInequality:
|
|
resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.ValueEquality:
|
|
resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
|
|
if (resultExprInfo == null)
|
|
{
|
|
// check if constants are used with ulongs, as we should do some extra "conversions"
|
|
if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
|
|
|| ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
|
|
{
|
|
resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
|
|
}
|
|
else
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.LessThan:
|
|
case CodeBinaryOperatorType.LessThanOrEqual:
|
|
case CodeBinaryOperatorType.GreaterThan:
|
|
case CodeBinaryOperatorType.GreaterThanOrEqual:
|
|
resultExprInfo = Literal.AllowedComparison(lhsType, binaryExpr.Left, rhsType, binaryExpr.Right, binaryExpr.Operator, validation, out error);
|
|
if (resultExprInfo == null)
|
|
{
|
|
// check if constants are used with ulongs, as we should do some extra "conversions"
|
|
if (((lhsType == typeof(ulong)) && (PromotionPossible(rhsType, binaryExpr.Right)))
|
|
|| ((rhsType == typeof(ulong)) && (PromotionPossible(lhsType, binaryExpr.Left))))
|
|
{
|
|
resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
|
|
}
|
|
else
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.BooleanAnd:
|
|
case CodeBinaryOperatorType.BooleanOr:
|
|
resultExprInfo = new RuleBinaryExpressionInfo(lhsType, rhsType, typeof(bool));
|
|
if (lhsType != typeof(bool))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeLHS, binaryExpr.Operator.ToString(),
|
|
(lhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(lhsType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_LeftOperandInvalidType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
resultExprInfo = null;
|
|
}
|
|
if (rhsType != typeof(bool))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.LogicalOpBadTypeRHS, binaryExpr.Operator.ToString(),
|
|
(rhsType == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(rhsType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_RightOperandInvalidType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
resultExprInfo = null;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
validation.Errors.Add(error);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Validate any RuleAttributes, if present.
|
|
if (resultExprInfo != null)
|
|
{
|
|
MethodInfo method = resultExprInfo.MethodInfo;
|
|
if (method != null)
|
|
{
|
|
object[] attrs = method.GetCustomAttributes(typeof(RuleAttribute), true);
|
|
if (attrs != null && attrs.Length > 0)
|
|
{
|
|
Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
|
|
methodStack.Push(method);
|
|
|
|
bool allAttributesValid = true;
|
|
foreach (RuleAttribute ruleAttr in attrs)
|
|
{
|
|
if (!ruleAttr.Validate(validation, method, method.DeclaringType, method.GetParameters()))
|
|
allAttributesValid = false;
|
|
}
|
|
|
|
methodStack.Pop();
|
|
|
|
if (!allAttributesValid)
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return resultExprInfo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check that the expression is a constant, and is promotable to type ULONG.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <param name="expression"></param>
|
|
/// <returns></returns>
|
|
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
|
|
private static bool PromotionPossible(Type type, CodeExpression expression)
|
|
{
|
|
// C# 2.0, section 6.1.6, int/long constants can be promoted to ulong as long as in range
|
|
if (type == typeof(int))
|
|
{
|
|
CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
|
|
if (primitive != null)
|
|
{
|
|
int i = (int)primitive.Value;
|
|
return (i >= 0);
|
|
}
|
|
}
|
|
else if (type == typeof(long))
|
|
{
|
|
CodePrimitiveExpression primitive = expression as CodePrimitiveExpression;
|
|
if (primitive != null)
|
|
{
|
|
long l = (long)primitive.Value;
|
|
return (l >= 0);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
// Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
|
|
RuleBinaryExpressionInfo expressionInfo = analysis.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
|
|
if (expressionInfo != null)
|
|
{
|
|
// we may be calling a method, not a default operator
|
|
MethodInfo method = expressionInfo.MethodInfo;
|
|
if (method != null)
|
|
{
|
|
List<CodeExpression> attributedExprs = new List<CodeExpression>();
|
|
CodeExpressionCollection arguments = new CodeExpressionCollection();
|
|
arguments.Add(binaryExpr.Left);
|
|
arguments.Add(binaryExpr.Right);
|
|
CodeExpression targetObject = new CodeTypeReferenceExpression(method.DeclaringType);
|
|
analysis.AnalyzeRuleAttributes(method, targetObject, qualifier, arguments, method.GetParameters(), attributedExprs);
|
|
}
|
|
}
|
|
|
|
// Analyze the left & right children.
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Left, true, false, null);
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, binaryExpr.Right, true, false, null);
|
|
}
|
|
|
|
#region Evaluate
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
object lhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Left).Value;
|
|
CodeBinaryOperatorType operation = binaryExpr.Operator;
|
|
// short-circuit ANDs and ORs
|
|
if (operation == CodeBinaryOperatorType.BooleanAnd)
|
|
{
|
|
if ((bool)lhsValue)
|
|
{
|
|
// LHS is true, need to look at RHS
|
|
object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
|
|
return new RuleLiteralResult(rhsValue);
|
|
}
|
|
else
|
|
// LHS is false, so result is false
|
|
return new RuleLiteralResult(false);
|
|
}
|
|
else if (operation == CodeBinaryOperatorType.BooleanOr)
|
|
{
|
|
if ((bool)lhsValue)
|
|
// LHS is true, so result is true
|
|
return new RuleLiteralResult(true);
|
|
else
|
|
{
|
|
// LHS is false, so need to look at RHS
|
|
object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
|
|
return new RuleLiteralResult(rhsValue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
object resultValue;
|
|
object rhsValue = RuleExpressionWalker.Evaluate(execution, binaryExpr.Right).Value;
|
|
RuleBinaryExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(binaryExpr) as RuleBinaryExpressionInfo;
|
|
if (expressionInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
MethodInfo methodInfo = expressionInfo.MethodInfo;
|
|
if (methodInfo != null)
|
|
{
|
|
if (methodInfo == Literal.ObjectEquality)
|
|
{
|
|
resultValue = (lhsValue == rhsValue);
|
|
}
|
|
else
|
|
{
|
|
ParameterInfo[] existingParameters = methodInfo.GetParameters();
|
|
object[] parameters = new object[2];
|
|
parameters[0] = Executor.AdjustType(expressionInfo.LeftType, lhsValue, existingParameters[0].ParameterType);
|
|
parameters[1] = Executor.AdjustType(expressionInfo.RightType, rhsValue, existingParameters[1].ParameterType);
|
|
resultValue = methodInfo.Invoke(null, parameters);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resultValue = EvaluateBinaryOperation(binaryExpr, expressionInfo.LeftType, lhsValue, operation, expressionInfo.RightType, rhsValue);
|
|
}
|
|
return new RuleLiteralResult(resultValue);
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
private static object EvaluateBinaryOperation(CodeBinaryOperatorExpression binaryExpr, Type lhsType, object lhsValue, CodeBinaryOperatorType operation, Type rhsType, object rhsValue)
|
|
{
|
|
Literal leftLiteral;
|
|
Literal rightLiteral;
|
|
ArithmeticLiteral leftArithmetic;
|
|
ArithmeticLiteral rightArithmetic;
|
|
string message;
|
|
RuleEvaluationException exception;
|
|
|
|
switch (operation)
|
|
{
|
|
case CodeBinaryOperatorType.Add:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.Add(rightArithmetic);
|
|
case CodeBinaryOperatorType.Subtract:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.Subtract(rightArithmetic);
|
|
case CodeBinaryOperatorType.Multiply:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.Multiply(rightArithmetic);
|
|
case CodeBinaryOperatorType.Divide:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.Divide(rightArithmetic);
|
|
case CodeBinaryOperatorType.Modulus:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.Modulus(rightArithmetic);
|
|
case CodeBinaryOperatorType.BitwiseAnd:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.BitAnd(rightArithmetic);
|
|
case CodeBinaryOperatorType.BitwiseOr:
|
|
leftArithmetic = ArithmeticLiteral.MakeLiteral(lhsType, lhsValue);
|
|
if (leftArithmetic == null)
|
|
break;
|
|
rightArithmetic = ArithmeticLiteral.MakeLiteral(rhsType, rhsValue);
|
|
if (rightArithmetic == null)
|
|
break;
|
|
return leftArithmetic.BitOr(rightArithmetic);
|
|
|
|
case CodeBinaryOperatorType.ValueEquality:
|
|
leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
|
|
if (leftLiteral == null)
|
|
break;
|
|
rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
|
|
if (rightLiteral == null)
|
|
break;
|
|
return leftLiteral.Equal(rightLiteral);
|
|
case CodeBinaryOperatorType.IdentityEquality:
|
|
return lhsValue == rhsValue;
|
|
case CodeBinaryOperatorType.IdentityInequality:
|
|
return lhsValue != rhsValue;
|
|
|
|
case CodeBinaryOperatorType.LessThan:
|
|
leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
|
|
if (leftLiteral == null)
|
|
break;
|
|
rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
|
|
if (rightLiteral == null)
|
|
break;
|
|
return leftLiteral.LessThan(rightLiteral);
|
|
case CodeBinaryOperatorType.LessThanOrEqual:
|
|
leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
|
|
if (leftLiteral == null)
|
|
break;
|
|
rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
|
|
if (rightLiteral == null)
|
|
break;
|
|
return leftLiteral.LessThanOrEqual(rightLiteral);
|
|
case CodeBinaryOperatorType.GreaterThan:
|
|
leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
|
|
if (leftLiteral == null)
|
|
break;
|
|
rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
|
|
if (rightLiteral == null)
|
|
break;
|
|
return leftLiteral.GreaterThan(rightLiteral);
|
|
case CodeBinaryOperatorType.GreaterThanOrEqual:
|
|
leftLiteral = Literal.MakeLiteral(lhsType, lhsValue);
|
|
if (leftLiteral == null)
|
|
break;
|
|
rightLiteral = Literal.MakeLiteral(rhsType, rhsValue);
|
|
if (rightLiteral == null)
|
|
break;
|
|
return leftLiteral.GreaterThanOrEqual(rightLiteral);
|
|
|
|
default:
|
|
// should never happen
|
|
// BooleanAnd & BooleanOr short-circuited before call
|
|
// Assign disallowed at validation time
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, operation.ToString());
|
|
exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
|
|
message = string.Format(CultureInfo.CurrentCulture,
|
|
Messages.BinaryOpFails,
|
|
operation.ToString(),
|
|
RuleDecompiler.DecompileType(lhsType),
|
|
RuleDecompiler.DecompileType(rhsType));
|
|
exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
#endregion
|
|
|
|
#region Decompile
|
|
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
bool mustParenthesize = false;
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
if (binaryExpr.Left == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpLHS, binaryExpr.Operator.ToString());
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
if (binaryExpr.Right == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullBinaryOpRHS, binaryExpr.Operator.ToString());
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
|
|
string opString;
|
|
|
|
switch (binaryExpr.Operator)
|
|
{
|
|
case CodeBinaryOperatorType.Modulus:
|
|
opString = " % ";
|
|
break;
|
|
case CodeBinaryOperatorType.Multiply:
|
|
opString = " * ";
|
|
break;
|
|
case CodeBinaryOperatorType.Divide:
|
|
opString = " / ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.Subtract:
|
|
opString = " - ";
|
|
break;
|
|
case CodeBinaryOperatorType.Add:
|
|
opString = " + ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.LessThan:
|
|
opString = " < ";
|
|
break;
|
|
case CodeBinaryOperatorType.LessThanOrEqual:
|
|
opString = " <= ";
|
|
break;
|
|
case CodeBinaryOperatorType.GreaterThan:
|
|
opString = " > ";
|
|
break;
|
|
case CodeBinaryOperatorType.GreaterThanOrEqual:
|
|
opString = " >= ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.IdentityEquality:
|
|
case CodeBinaryOperatorType.ValueEquality:
|
|
opString = " == ";
|
|
break;
|
|
case CodeBinaryOperatorType.IdentityInequality:
|
|
opString = " != ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.BitwiseAnd:
|
|
opString = " & ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.BitwiseOr:
|
|
opString = " | ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.BooleanAnd:
|
|
opString = " && ";
|
|
break;
|
|
|
|
case CodeBinaryOperatorType.BooleanOr:
|
|
opString = " || ";
|
|
break;
|
|
|
|
default:
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, binaryExpr.Operator.ToString());
|
|
NotSupportedException exception = new NotSupportedException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = binaryExpr;
|
|
throw exception;
|
|
}
|
|
|
|
CodeExpression leftExpr = binaryExpr.Left;
|
|
CodeExpression rightExpr = binaryExpr.Right;
|
|
|
|
|
|
if (binaryExpr.Operator == CodeBinaryOperatorType.ValueEquality)
|
|
{
|
|
// Look for special cases:
|
|
// LHS == false --> ! LHS
|
|
// or
|
|
// (LHS == expr) == false --> LHS != expr
|
|
|
|
CodePrimitiveExpression rhsPrimitive = rightExpr as CodePrimitiveExpression;
|
|
if (rhsPrimitive != null)
|
|
{
|
|
object rhsValue = rhsPrimitive.Value;
|
|
if (rhsValue != null)
|
|
{
|
|
// we don't have the comparison "==null"
|
|
if (rhsValue.GetType() == typeof(bool) && (bool)rhsValue == false)
|
|
{
|
|
// We have comparison "== false".
|
|
|
|
CodeBinaryOperatorExpression lhsBinary = leftExpr as CodeBinaryOperatorExpression;
|
|
if (lhsBinary != null && lhsBinary.Operator == CodeBinaryOperatorType.ValueEquality)
|
|
{
|
|
// We have the pattern
|
|
// (expr1 == expr2) == false
|
|
// Treat this as:
|
|
// expr1 != expr2
|
|
|
|
opString = " != ";
|
|
|
|
leftExpr = lhsBinary.Left;
|
|
rightExpr = lhsBinary.Right;
|
|
}
|
|
else
|
|
{
|
|
// We have the pattern
|
|
// LHS == false
|
|
// Treat this as:
|
|
// ! LHS
|
|
|
|
mustParenthesize = RuleDecompiler.MustParenthesize(leftExpr, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
// Note the "parentExpression" passed to the child decompile... cast is the only
|
|
// built-in operation that has "unary" precedence, so pass that as the parent
|
|
// to get the parenthesization right. .
|
|
stringBuilder.Append("!");
|
|
RuleExpressionWalker.Decompile(stringBuilder, leftExpr, new CodeCastExpression());
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (binaryExpr.Operator == CodeBinaryOperatorType.Subtract)
|
|
{
|
|
// Look for the special case:
|
|
// 0 - RHS --> - RHS
|
|
|
|
CodePrimitiveExpression lhsPrimitive = leftExpr as CodePrimitiveExpression;
|
|
if (lhsPrimitive != null && lhsPrimitive.Value != null)
|
|
{
|
|
object lhsValue = lhsPrimitive.Value;
|
|
|
|
// Check if the LHS is zero. We'll only check a few types (decimal,
|
|
// double, float, int, long), since these occur most often (and the
|
|
// unsigned types are all illegal).
|
|
TypeCode tc = Type.GetTypeCode(lhsValue.GetType());
|
|
bool isZero = false;
|
|
switch (tc)
|
|
{
|
|
case TypeCode.Decimal:
|
|
isZero = ((decimal)lhsValue) == 0;
|
|
break;
|
|
|
|
case TypeCode.Double:
|
|
isZero = ((double)lhsValue) == 0;
|
|
break;
|
|
|
|
case TypeCode.Single:
|
|
isZero = ((float)lhsValue) == 0;
|
|
break;
|
|
|
|
case TypeCode.Int32:
|
|
isZero = ((int)lhsValue) == 0;
|
|
break;
|
|
|
|
case TypeCode.Int64:
|
|
isZero = ((long)lhsValue) == 0;
|
|
break;
|
|
}
|
|
|
|
if (isZero)
|
|
{
|
|
mustParenthesize = RuleDecompiler.MustParenthesize(rightExpr, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
// Note the "parentExpression" passed to the child decompile... cast is the only
|
|
// built-in operation that has "unary" precedence, so pass that as the parent
|
|
// to get the parenthesization right.
|
|
stringBuilder.Append("-");
|
|
RuleExpressionWalker.Decompile(stringBuilder, rightExpr, new CodeCastExpression());
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
mustParenthesize = RuleDecompiler.MustParenthesize(binaryExpr, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, leftExpr, binaryExpr);
|
|
stringBuilder.Append(opString);
|
|
RuleExpressionWalker.Decompile(stringBuilder, rightExpr, binaryExpr);
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
}
|
|
#endregion
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
CodeBinaryOperatorExpression newOp = new CodeBinaryOperatorExpression();
|
|
newOp.Operator = binaryExpr.Operator;
|
|
newOp.Left = RuleExpressionWalker.Clone(binaryExpr.Left);
|
|
newOp.Right = RuleExpressionWalker.Clone(binaryExpr.Right);
|
|
return newOp;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeBinaryOperatorExpression binaryExpr = (CodeBinaryOperatorExpression)expression;
|
|
|
|
CodeBinaryOperatorExpression comperandBinary = (CodeBinaryOperatorExpression)comperand;
|
|
return (binaryExpr.Operator == comperandBinary.Operator
|
|
&& RuleExpressionWalker.Match(binaryExpr.Left, comperandBinary.Left)
|
|
&& RuleExpressionWalker.Match(binaryExpr.Right, comperandBinary.Right));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Field ref expression
|
|
|
|
// CodeFieldReferenceExpression
|
|
internal class FieldReferenceExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
|
|
if (fieldRefExpr.TargetObject == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(fieldRefExpr))
|
|
return null;
|
|
|
|
RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, fieldRefExpr.TargetObject, false);
|
|
|
|
validation.PopParentExpression();
|
|
|
|
if (targetExprInfo == null) // error occurred, so simply return
|
|
return null;
|
|
|
|
Type targetType = targetExprInfo.ExpressionType;
|
|
if (targetType == null) // no type, so must have been an error already
|
|
return null;
|
|
|
|
if (targetType == typeof(NullLiteral))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
BindingFlags bindingFlags = BindingFlags.Public;
|
|
if (fieldRefExpr.TargetObject is CodeTypeReferenceExpression)
|
|
bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
|
else
|
|
bindingFlags |= BindingFlags.Instance;
|
|
if (validation.AllowInternalMembers(targetType))
|
|
bindingFlags |= BindingFlags.NonPublic;
|
|
|
|
FieldInfo fi = targetType.GetField(fieldRefExpr.FieldName, bindingFlags);
|
|
if (fi == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownField, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (fi.FieldType == null)
|
|
{
|
|
// This can only happen with a design-time type.
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, fieldRefExpr.FieldName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (isWritten && fi.IsLiteral)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.FieldSetNotAllowed, fieldRefExpr.FieldName, RuleDecompiler.DecompileType(targetType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (!validation.ValidateMemberAccess(fieldRefExpr.TargetObject, targetType, fi, fi.Name, fieldRefExpr))
|
|
return null;
|
|
|
|
// Is it possible to set fi by validation.ResolveFieldOrProperty(targetType, fieldExpr.FieldName)?
|
|
validation.IsAuthorized(fi.FieldType);
|
|
return new RuleFieldExpressionInfo(fi);
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
CodeExpression targetObject = fieldRefExpr.TargetObject;
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(fieldRefExpr.FieldName, qualifier));
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
object target = RuleExpressionWalker.Evaluate(execution, fieldRefExpr.TargetObject).Value;
|
|
|
|
RuleFieldExpressionInfo fieldExprInfo = execution.Validation.ExpressionInfo(fieldRefExpr) as RuleFieldExpressionInfo;
|
|
if (fieldExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
throw exception;
|
|
}
|
|
|
|
FieldInfo fi = fieldExprInfo.FieldInfo;
|
|
|
|
return new RuleFieldResult(target, fi);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
|
|
CodeExpression targetObject = fieldRefExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullFieldTarget, fieldRefExpr.FieldName);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = fieldRefExpr;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, fieldRefExpr);
|
|
stringBuilder.Append('.');
|
|
stringBuilder.Append(fieldRefExpr.FieldName);
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
|
|
CodeFieldReferenceExpression newField = new CodeFieldReferenceExpression();
|
|
newField.FieldName = fieldRefExpr.FieldName;
|
|
newField.TargetObject = RuleExpressionWalker.Clone(fieldRefExpr.TargetObject);
|
|
return newField;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeFieldReferenceExpression fieldRefExpr = (CodeFieldReferenceExpression)expression;
|
|
|
|
CodeFieldReferenceExpression newField = (CodeFieldReferenceExpression)comperand;
|
|
return (fieldRefExpr.FieldName == newField.FieldName
|
|
&& RuleExpressionWalker.Match(fieldRefExpr.TargetObject, newField.TargetObject));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Property ref expression
|
|
|
|
// CodePropertyReferenceExpression
|
|
internal class PropertyReferenceExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
if (propGetExpr.TargetObject == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(propGetExpr))
|
|
return null;
|
|
|
|
RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, propGetExpr.TargetObject, false);
|
|
|
|
validation.PopParentExpression();
|
|
|
|
if (targetExprInfo == null) // error occurred, so simply return
|
|
return null;
|
|
|
|
Type targetType = targetExprInfo.ExpressionType;
|
|
if (targetType == null) // no type, so must have been an error already
|
|
return null;
|
|
|
|
if (targetType == typeof(NullLiteral))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
bool includeNonPublic = false;
|
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
|
if (validation.AllowInternalMembers(targetType))
|
|
{
|
|
bindingFlags |= BindingFlags.NonPublic;
|
|
includeNonPublic = true;
|
|
}
|
|
PropertyInfo pi = validation.ResolveProperty(targetType, propGetExpr.PropertyName, bindingFlags);
|
|
|
|
if (pi == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownProperty, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (pi.PropertyType == null)
|
|
{
|
|
// This can only happen with a design-time type.
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, propGetExpr.PropertyName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
|
|
if (accessorMethod == null)
|
|
{
|
|
string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
|
|
message = string.Format(CultureInfo.CurrentCulture, baseMessage, propGetExpr.PropertyName, RuleDecompiler.DecompileType(targetType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (!validation.ValidateMemberAccess(propGetExpr.TargetObject, targetType, accessorMethod, propGetExpr.PropertyName, propGetExpr))
|
|
return null;
|
|
|
|
// Validate any RuleAttributes, if present.
|
|
object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
|
|
if (attrs != null && attrs.Length > 0)
|
|
{
|
|
Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
|
|
methodStack.Push(pi);
|
|
|
|
bool allAttributesValid = true;
|
|
foreach (RuleAttribute ruleAttr in attrs)
|
|
{
|
|
if (!ruleAttr.Validate(validation, pi, targetType, null))
|
|
allAttributesValid = false;
|
|
}
|
|
|
|
methodStack.Pop();
|
|
|
|
if (!allAttributesValid)
|
|
return null;
|
|
}
|
|
|
|
return new RulePropertyExpressionInfo(pi, pi.PropertyType, false);
|
|
}
|
|
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
string message;
|
|
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
// Evaluate the target object and get its type.
|
|
CodeExpression targetObject = propGetExpr.TargetObject;
|
|
RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
|
|
if (targetExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
|
|
throw exception;
|
|
}
|
|
|
|
// Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
|
|
RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
|
|
if (propExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
throw exception;
|
|
}
|
|
|
|
PropertyInfo pi = propExprInfo.PropertyInfo;
|
|
|
|
// Look for RuleAttribute's on the invoked property.
|
|
List<CodeExpression> attributedExprs = new List<CodeExpression>();
|
|
analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, null, null, attributedExprs);
|
|
|
|
// See if the target object needs default analysis.
|
|
if (!attributedExprs.Contains(targetObject))
|
|
{
|
|
// The property had no [RuleRead] or [RuleWrite] attributes. Just qualify the target object with
|
|
// the property name and proceed with the analysis.
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, new RulePathQualifier(pi.Name, qualifier));
|
|
}
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
object target = RuleExpressionWalker.Evaluate(execution, propGetExpr.TargetObject).Value;
|
|
|
|
RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(propGetExpr) as RulePropertyExpressionInfo;
|
|
if (propExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
throw exception;
|
|
}
|
|
|
|
PropertyInfo pi = propExprInfo.PropertyInfo;
|
|
return new RulePropertyResult(pi, target, null);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
CodeExpression targetObject = propGetExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullPropertyTarget, propGetExpr.PropertyName);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = propGetExpr;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, propGetExpr);
|
|
stringBuilder.Append('.');
|
|
stringBuilder.Append(propGetExpr.PropertyName);
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
CodePropertyReferenceExpression newProperty = new CodePropertyReferenceExpression();
|
|
newProperty.PropertyName = propGetExpr.PropertyName;
|
|
newProperty.TargetObject = RuleExpressionWalker.Clone(propGetExpr.TargetObject);
|
|
return newProperty;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodePropertyReferenceExpression propGetExpr = (CodePropertyReferenceExpression)expression;
|
|
|
|
CodePropertyReferenceExpression newProperty = (CodePropertyReferenceExpression)comperand;
|
|
return (propGetExpr.PropertyName == newProperty.PropertyName
|
|
&& RuleExpressionWalker.Match(propGetExpr.TargetObject, newProperty.TargetObject));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Method invoke expression
|
|
|
|
// CodeMethodInvokeExpression
|
|
internal class MethodInvokeExpression : RuleExpressionInternal
|
|
{
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
Type targetType = null;
|
|
RuleMethodInvokeExpressionInfo methodInvokeInfo = null;
|
|
string message;
|
|
ValidationError error = null;
|
|
BindingFlags bindingFlags = BindingFlags.Public;
|
|
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
if (isWritten)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeMethodInvokeExpression).ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
return null; // Fatal error; discontinue validation of this object.
|
|
}
|
|
|
|
if ((invokeExpr.Method.TypeArguments != null) && (invokeExpr.Method.TypeArguments.Count > 0))
|
|
{
|
|
error = new ValidationError(Messages.GenericMethodsNotSupported, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(invokeExpr))
|
|
return null;
|
|
|
|
RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, invokeExpr.Method.TargetObject, false);
|
|
if (targetExprInfo == null) // error occurred, so simply return
|
|
return null;
|
|
|
|
targetType = targetExprInfo.ExpressionType;
|
|
if (targetType == null)
|
|
return null;
|
|
|
|
// if an error occurred (targetType == null), continue on to validate the arguments
|
|
if (targetType == typeof(NullLiteral))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_BindingTypeMissing);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
targetType = null; // force exit after validating the arguments
|
|
}
|
|
|
|
List<CodeExpression> argExprs = new List<CodeExpression>();
|
|
|
|
bool hasInvalidArgument = false;
|
|
if (invokeExpr.Parameters != null)
|
|
{
|
|
for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
|
|
{
|
|
CodeExpression argExpr = invokeExpr.Parameters[i];
|
|
if (argExpr == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
targetType = null; // force exit after validating the rest of the arguments
|
|
}
|
|
else
|
|
{
|
|
if (argExpr is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
|
|
validation.AddError(error);
|
|
|
|
hasInvalidArgument = true;
|
|
}
|
|
|
|
// Validate the argument.
|
|
RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
|
|
if (argExprInfo == null)
|
|
hasInvalidArgument = true;
|
|
argExprs.Add(argExpr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop further validation if there was a problem with the target expression.
|
|
if (targetType == null)
|
|
return null;
|
|
|
|
// Stop further validation if there was a problem with any of the arguments.
|
|
if (hasInvalidArgument)
|
|
return null;
|
|
|
|
if (invokeExpr.Method.TargetObject is CodeTypeReferenceExpression)
|
|
bindingFlags |= BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
|
else
|
|
bindingFlags |= BindingFlags.Instance;
|
|
if (validation.AllowInternalMembers(targetType))
|
|
bindingFlags |= BindingFlags.NonPublic;
|
|
|
|
// Everything okay so far, try to resolve the method.
|
|
methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
|
|
if ((methodInvokeInfo == null) && (invokeExpr.UserData.Contains(RuleUserDataKeys.QualifiedName)))
|
|
{
|
|
// failed to resolve the method, but a fully qualified type name is around
|
|
// load the type, add it to the assemblies, and try again
|
|
string qualifiedName = invokeExpr.UserData[RuleUserDataKeys.QualifiedName] as string;
|
|
Type containingClassType = validation.ResolveType(qualifiedName);
|
|
if (containingClassType != null)
|
|
{
|
|
validation.DetermineExtensionMethods(containingClassType.Assembly);
|
|
methodInvokeInfo = validation.ResolveMethod(targetType, invokeExpr.Method.MethodName, bindingFlags, argExprs, out error);
|
|
}
|
|
}
|
|
if (methodInvokeInfo == null)
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
validation.PopParentExpression();
|
|
}
|
|
|
|
|
|
MethodInfo mi = methodInvokeInfo.MethodInfo;
|
|
|
|
if (mi.ReturnType == null)
|
|
{
|
|
// This can only happen with a design-time type.
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CouldNotDetermineMemberType, invokeExpr.Method.MethodName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CouldNotDetermineMemberType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (!validation.ValidateMemberAccess(invokeExpr.Method.TargetObject, targetType, mi, invokeExpr.Method.MethodName, invokeExpr))
|
|
return null;
|
|
|
|
// Validate any RuleAttributes, if present.
|
|
object[] attrs = mi.GetCustomAttributes(typeof(RuleAttribute), true);
|
|
if (attrs != null && attrs.Length > 0)
|
|
{
|
|
Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
|
|
methodStack.Push(mi);
|
|
|
|
bool allAttributesValid = true;
|
|
foreach (RuleAttribute ruleAttr in attrs)
|
|
{
|
|
if (!ruleAttr.Validate(validation, mi, targetType, mi.GetParameters()))
|
|
allAttributesValid = false;
|
|
}
|
|
|
|
methodStack.Pop();
|
|
|
|
if (!allAttributesValid)
|
|
return null;
|
|
}
|
|
|
|
// if this is an extension method, save the type information
|
|
if (mi is ExtensionMethodInfo)
|
|
{
|
|
invokeExpr.UserData[RuleUserDataKeys.QualifiedName] = mi.DeclaringType.AssemblyQualifiedName;
|
|
}
|
|
|
|
return methodInvokeInfo;
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
string message;
|
|
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
// Get the target object's type.
|
|
CodeExpression targetObject = invokeExpr.Method.TargetObject;
|
|
RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
|
|
if (targetExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
|
|
throw exception;
|
|
}
|
|
|
|
// Get the method info from the validation so we can look for [RuleRead] and [RuleWrite] attributes.
|
|
RuleMethodInvokeExpressionInfo methodExprInfo = analysis.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
|
|
if (methodExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
throw exception;
|
|
}
|
|
|
|
MethodInfo mi = methodExprInfo.MethodInfo;
|
|
|
|
// Look for RuleAttribute's on the invoked method.
|
|
List<CodeExpression> attributedExprs = new List<CodeExpression>();
|
|
analysis.AnalyzeRuleAttributes(mi, targetObject, qualifier, invokeExpr.Parameters, mi.GetParameters(), attributedExprs);
|
|
|
|
// See if the target object needs default analysis.
|
|
if (!attributedExprs.Contains(targetObject))
|
|
{
|
|
// No applicable [RuleRead] or [RuleWrite] attributes were found on the target object.
|
|
|
|
// If we're analyzing for dependencies, assume that this method uses the
|
|
// value of the target object, but nothing beneath it.
|
|
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, true, false, null);
|
|
}
|
|
|
|
// See if any of the arguments need default analysis.
|
|
for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
|
|
{
|
|
CodeExpression argExpr = invokeExpr.Parameters[i];
|
|
|
|
if (!attributedExprs.Contains(argExpr))
|
|
{
|
|
// Similar to the target object, we assume that this method can reads the value
|
|
// of the parameter, but none of its members.
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
string message;
|
|
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
object target = RuleExpressionWalker.Evaluate(execution, invokeExpr.Method.TargetObject).Value;
|
|
|
|
RuleMethodInvokeExpressionInfo invokeExprInfo = execution.Validation.ExpressionInfo(invokeExpr) as RuleMethodInvokeExpressionInfo;
|
|
if (invokeExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
throw exception;
|
|
}
|
|
|
|
MethodInfo mi = invokeExprInfo.MethodInfo;
|
|
|
|
if (!mi.IsStatic && target == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullMethod, invokeExpr.Method.MethodName);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
throw exception;
|
|
}
|
|
|
|
object[] arguments = null;
|
|
RuleExpressionResult[] outArgumentResults = null;
|
|
|
|
if (invokeExpr.Parameters != null && invokeExpr.Parameters.Count > 0)
|
|
{
|
|
int actualArgCount = invokeExpr.Parameters.Count;
|
|
|
|
ParameterInfo[] parmInfos = mi.GetParameters();
|
|
|
|
arguments = new object[parmInfos.Length];
|
|
|
|
int numFixedParameters = parmInfos.Length;
|
|
if (invokeExprInfo.NeedsParamsExpansion)
|
|
numFixedParameters -= 1;
|
|
|
|
int i;
|
|
|
|
// Evaluate the fixed portion of the parameter list.
|
|
for (i = 0; i < numFixedParameters; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
|
|
|
|
// Special procesing of direction expressions to keep track of out arguments (& ref).
|
|
CodeDirectionExpression direction = invokeExpr.Parameters[i] as CodeDirectionExpression;
|
|
if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
|
|
{
|
|
// lazy creation of fieldsToSet
|
|
if (outArgumentResults == null)
|
|
outArgumentResults = new RuleExpressionResult[invokeExpr.Parameters.Count];
|
|
// keep track of this out expression so we can set it later
|
|
outArgumentResults[i] = argResult;
|
|
|
|
// don't evaluate out arguments
|
|
if (direction.Direction != FieldDirection.Out)
|
|
arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
|
|
}
|
|
else
|
|
{
|
|
// treat as in
|
|
arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
|
|
}
|
|
}
|
|
|
|
if (numFixedParameters < actualArgCount)
|
|
{
|
|
// This target method had a params array, and we are calling it with an
|
|
// expanded parameter list. E.g.,
|
|
// void foo(int x, params string[] y)
|
|
// with the invocation:
|
|
// foo(5, "crud", "kreeble", "glorp")
|
|
// We need to translate this to:
|
|
// foo(5, new string[] { "crud", "kreeble", "glorp" })
|
|
|
|
ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
|
|
|
|
Type arrayType = lastParamInfo.ParameterType;
|
|
System.Diagnostics.Debug.Assert(arrayType.IsArray);
|
|
Type elementType = arrayType.GetElementType();
|
|
|
|
Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
|
|
for (; i < actualArgCount; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(invokeExpr.Parameters[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, invokeExpr.Parameters[i]);
|
|
paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
|
|
}
|
|
|
|
arguments[numFixedParameters] = paramsArray;
|
|
}
|
|
}
|
|
|
|
object result;
|
|
try
|
|
{
|
|
result = mi.Invoke(target, arguments);
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
// if there is no inner exception, leave it untouched
|
|
if (e.InnerException == null)
|
|
throw;
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.Error_MethodInvoke,
|
|
RuleDecompiler.DecompileType(mi.ReflectedType), mi.Name, e.InnerException.Message);
|
|
throw new TargetInvocationException(message, e.InnerException);
|
|
}
|
|
|
|
// any out/ref parameters that need to be assigned?
|
|
if (outArgumentResults != null)
|
|
{
|
|
for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
|
|
{
|
|
if (outArgumentResults[i] != null)
|
|
outArgumentResults[i].Value = arguments[i];
|
|
}
|
|
}
|
|
|
|
return new RuleLiteralResult(result);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
if ((invokeExpr.Method == null) || (invokeExpr.Method.TargetObject == null))
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTarget, invokeExpr.Method.MethodName);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
throw exception;
|
|
}
|
|
|
|
// Decompile the target expression.
|
|
CodeExpression targetObject = invokeExpr.Method.TargetObject;
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, invokeExpr);
|
|
|
|
stringBuilder.Append('.');
|
|
stringBuilder.Append(invokeExpr.Method.MethodName);
|
|
|
|
// Decompile the arguments
|
|
stringBuilder.Append('(');
|
|
|
|
if (invokeExpr.Parameters != null)
|
|
{
|
|
for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
|
|
{
|
|
CodeExpression paramExpr = invokeExpr.Parameters[i];
|
|
if (paramExpr == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullMethodTypeParameter, i.ToString(CultureInfo.CurrentCulture), invokeExpr.Method.MethodName);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = invokeExpr;
|
|
throw exception;
|
|
}
|
|
|
|
if (i > 0)
|
|
stringBuilder.Append(", ");
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
|
|
}
|
|
}
|
|
|
|
stringBuilder.Append(')');
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
CodeMethodInvokeExpression newMethod = new CodeMethodInvokeExpression();
|
|
newMethod.Method = CloneMethodReference(invokeExpr.Method);
|
|
foreach (CodeExpression argument in invokeExpr.Parameters)
|
|
newMethod.Parameters.Add(RuleExpressionWalker.Clone(argument));
|
|
return newMethod;
|
|
}
|
|
|
|
private static CodeMethodReferenceExpression CloneMethodReference(CodeMethodReferenceExpression oldReference)
|
|
{
|
|
CodeMethodReferenceExpression newReference = new CodeMethodReferenceExpression();
|
|
newReference.MethodName = oldReference.MethodName;
|
|
newReference.TargetObject = RuleExpressionWalker.Clone(oldReference.TargetObject);
|
|
foreach (CodeTypeReference typeReference in oldReference.TypeArguments)
|
|
newReference.TypeArguments.Add(TypeReferenceExpression.CloneType(typeReference));
|
|
ConditionHelper.CloneUserData(oldReference, newReference);
|
|
return newReference;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeMethodInvokeExpression invokeExpr = (CodeMethodInvokeExpression)expression;
|
|
|
|
CodeMethodInvokeExpression newMethod = (CodeMethodInvokeExpression)comperand;
|
|
if (invokeExpr.Method.MethodName != newMethod.Method.MethodName)
|
|
return false;
|
|
if (!RuleExpressionWalker.Match(invokeExpr.Method.TargetObject, newMethod.Method.TargetObject))
|
|
return false;
|
|
if (invokeExpr.Parameters.Count != newMethod.Parameters.Count)
|
|
return false;
|
|
for (int i = 0; i < invokeExpr.Parameters.Count; ++i)
|
|
{
|
|
if (!RuleExpressionWalker.Match(invokeExpr.Parameters[i], newMethod.Parameters[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Direction expression (in/out/ref)
|
|
|
|
// CodeDirectionExpression
|
|
internal class DirectionExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
|
|
if (isWritten)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeDirectionExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// direction specified, make sure that something is specified
|
|
if (directionExpr.Expression == null)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.NullDirectionTarget, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (directionExpr.Expression is CodeTypeReferenceExpression)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, directionExpr.Expression.GetType().FullName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = directionExpr.Expression;
|
|
validation.AddError(error);
|
|
return null;
|
|
}
|
|
|
|
// validate the parameter
|
|
RuleExpressionInfo paramExprInfo;
|
|
bool isRef;
|
|
if (directionExpr.Direction == FieldDirection.Ref)
|
|
{
|
|
// ref parameters require that we both read and write the value
|
|
isRef = true;
|
|
paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
|
|
if (paramExprInfo == null)
|
|
return null;
|
|
paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
|
|
}
|
|
else if (directionExpr.Direction == FieldDirection.Out)
|
|
{
|
|
// out parameters mean that we only write to it
|
|
isRef = true;
|
|
paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, true);
|
|
}
|
|
else
|
|
{
|
|
// other parameters are treated as in, so we need to be able to read them
|
|
isRef = false;
|
|
paramExprInfo = RuleExpressionWalker.Validate(validation, directionExpr.Expression, false);
|
|
}
|
|
if (paramExprInfo == null)
|
|
return null;
|
|
|
|
// determine it's type
|
|
Type parameterType = paramExprInfo.ExpressionType;
|
|
if (parameterType == null)
|
|
return null;
|
|
|
|
if (parameterType != typeof(NullLiteral))
|
|
{
|
|
// adjust type if necessary
|
|
if (isRef && !parameterType.IsByRef)
|
|
parameterType = parameterType.MakeByRefType();
|
|
}
|
|
return new RuleExpressionInfo(parameterType);
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
CodeExpression paramExpr = directionExpr.Expression;
|
|
|
|
bool argIsWritten = false;
|
|
bool argIsRead = true;
|
|
RulePathQualifier argQualifier = null;
|
|
switch (directionExpr.Direction)
|
|
{
|
|
case FieldDirection.In:
|
|
// We assume that all children (* suffix) of this argument can be read.
|
|
argIsWritten = false;
|
|
argIsRead = true;
|
|
argQualifier = new RulePathQualifier("*", null);
|
|
break;
|
|
|
|
case FieldDirection.Ref:
|
|
// When this happens in a condition, we treat this like an "in" (above): all
|
|
// children (* suffix) of this argument are read. When this happens in an
|
|
// action, we treat this like an "out" (below): we assume this argument is
|
|
// modified (no suffix).
|
|
argIsWritten = true;
|
|
argIsRead = true;
|
|
argQualifier = analysis.ForWrites ? null : new RulePathQualifier("*", null);
|
|
break;
|
|
|
|
case FieldDirection.Out:
|
|
// We assume that this argument is modified (no suffix).
|
|
argIsWritten = true;
|
|
argIsRead = false;
|
|
argQualifier = null;
|
|
break;
|
|
}
|
|
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, paramExpr, argIsRead, argIsWritten, argQualifier);
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
// For evaluation purposes, ignore the direction. It is handled specifically in the
|
|
// method invoke Evaluate method.
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
return RuleExpressionWalker.Evaluate(execution, directionExpr.Expression);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
|
|
string direction = null;
|
|
if (directionExpr.Direction == FieldDirection.Out)
|
|
direction = "out ";
|
|
else if (directionExpr.Direction == FieldDirection.Ref)
|
|
direction = "ref ";
|
|
|
|
if (direction != null)
|
|
stringBuilder.Append(direction);
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, directionExpr.Expression, directionExpr);
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
CodeDirectionExpression newDirection = new CodeDirectionExpression();
|
|
newDirection.Direction = directionExpr.Direction;
|
|
newDirection.Expression = RuleExpressionWalker.Clone(directionExpr.Expression);
|
|
return newDirection;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeDirectionExpression directionExpr = (CodeDirectionExpression)expression;
|
|
CodeDirectionExpression newDirection = (CodeDirectionExpression)comperand;
|
|
return (directionExpr.Direction == newDirection.Direction &&
|
|
RuleExpressionWalker.Match(directionExpr.Expression, newDirection.Expression));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Type Reference expression
|
|
|
|
// CodeTypeReferenceExpression
|
|
internal class TypeReferenceExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
|
|
|
|
if (typeRefExpr.Type == null)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (isWritten)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeTypeReferenceExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = typeRefExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
Type resultType = validation.ResolveType(typeRefExpr.Type);
|
|
|
|
return new RuleExpressionInfo(resultType);
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
// These introduce no interesting dependencies or side-effects.
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
// Type references don't evaluate to any value.
|
|
return new RuleLiteralResult(null);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
|
|
RuleDecompiler.DecompileType(stringBuilder, typeRefExpr.Type);
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
|
|
CodeTypeReferenceExpression newType = new CodeTypeReferenceExpression(CloneType(typeRefExpr.Type));
|
|
return newType;
|
|
}
|
|
|
|
static internal CodeTypeReference CloneType(CodeTypeReference oldType)
|
|
{
|
|
if (oldType == null)
|
|
return null;
|
|
|
|
CodeTypeReference newType = new CodeTypeReference();
|
|
newType.ArrayElementType = CloneType(oldType.ArrayElementType);
|
|
newType.ArrayRank = oldType.ArrayRank;
|
|
newType.BaseType = oldType.BaseType;
|
|
foreach (CodeTypeReference typeReference in oldType.TypeArguments)
|
|
newType.TypeArguments.Add(CloneType(typeReference));
|
|
|
|
ConditionHelper.CloneUserData(oldType, newType);
|
|
|
|
return newType;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeTypeReferenceExpression typeRefExpr = (CodeTypeReferenceExpression)expression;
|
|
CodeTypeReferenceExpression newType = (CodeTypeReferenceExpression)comperand;
|
|
return MatchType(typeRefExpr.Type, newType.Type);
|
|
}
|
|
|
|
static internal bool MatchType(CodeTypeReference typeRef1, CodeTypeReference typeRef2)
|
|
{
|
|
if (typeRef1.BaseType != typeRef2.BaseType)
|
|
return false;
|
|
|
|
if (typeRef1.TypeArguments.Count != typeRef2.TypeArguments.Count)
|
|
return false;
|
|
for (int i = 0; i < typeRef1.TypeArguments.Count; ++i)
|
|
{
|
|
CodeTypeReference trArg1 = typeRef1.TypeArguments[i];
|
|
CodeTypeReference trArg2 = typeRef2.TypeArguments[i];
|
|
|
|
if (!MatchType(trArg1, trArg2))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cast expression
|
|
|
|
// CodeCastExpression
|
|
internal class CastExpression : RuleExpressionInternal
|
|
{
|
|
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
|
|
if (isWritten)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeCastExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (castExpr.Expression == null)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.NullCastExpr, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (castExpr.Expression is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, castExpr.Expression.GetType().FullName);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr.Expression;
|
|
validation.AddError(error);
|
|
return null;
|
|
}
|
|
|
|
if (castExpr.TargetType == null)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.NullCastType, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// Figure out the operand type.
|
|
RuleExpressionInfo operandInfo = RuleExpressionWalker.Validate(validation, castExpr.Expression, false);
|
|
if (operandInfo == null)
|
|
return null;
|
|
Type fromType = operandInfo.ExpressionType;
|
|
|
|
Type toType = validation.ResolveType(castExpr.TargetType);
|
|
if (toType == null)
|
|
return null;
|
|
|
|
if (fromType == typeof(NullLiteral))
|
|
{
|
|
// Casting from null value.
|
|
if (ConditionHelper.IsNonNullableValueType(toType))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CastOfNullInvalid, RuleDecompiler.DecompileType(toType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unwrap nullables to make life easy.
|
|
Type fromType2 = fromType;
|
|
if (ConditionHelper.IsNullableValueType(fromType2))
|
|
fromType2 = fromType2.GetGenericArguments()[0];
|
|
|
|
Type toType2 = toType;
|
|
if (ConditionHelper.IsNullableValueType(toType2))
|
|
toType2 = toType2.GetGenericArguments()[0];
|
|
|
|
bool canConvert = false;
|
|
if (fromType2.IsValueType && toType2.IsValueType)
|
|
{
|
|
// Convert.ChangeType doesn't handle enum <--> numeric
|
|
// and float/double/decimal <--> char, which are allowed
|
|
if (fromType2.IsEnum)
|
|
{
|
|
canConvert = (toType2.IsEnum) || IsNumeric(toType2);
|
|
}
|
|
else if (toType2.IsEnum)
|
|
{
|
|
// don't need to check fromType for enum since it's handled above
|
|
canConvert = IsNumeric(fromType2);
|
|
}
|
|
else if (fromType2 == typeof(char))
|
|
{
|
|
canConvert = IsNumeric(toType2);
|
|
}
|
|
else if (toType2 == typeof(char))
|
|
{
|
|
canConvert = IsNumeric(fromType2);
|
|
}
|
|
else if (fromType2.IsPrimitive && toType2.IsPrimitive)
|
|
{
|
|
try
|
|
{
|
|
// note: this also allows bool <--> numeric conversions
|
|
object fromValueDefault = Activator.CreateInstance(fromType2);
|
|
Convert.ChangeType(fromValueDefault, toType2, CultureInfo.CurrentCulture);
|
|
canConvert = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
canConvert = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!canConvert)
|
|
{
|
|
// We can cast up or down an inheritence hierarchy,
|
|
// as well as support explicit and implicit overrides
|
|
ValidationError error;
|
|
canConvert = RuleValidation.ExplicitConversionSpecified(fromType, toType, out error);
|
|
if (error != null)
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (!canConvert)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, RuleDecompiler.DecompileType(fromType), RuleDecompiler.DecompileType(toType));
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return new RuleExpressionInfo(toType);
|
|
}
|
|
|
|
private static bool IsNumeric(Type type)
|
|
{
|
|
switch (Type.GetTypeCode(type))
|
|
{
|
|
case TypeCode.SByte:
|
|
case TypeCode.Byte:
|
|
case TypeCode.Int16:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.Int32:
|
|
case TypeCode.UInt32:
|
|
case TypeCode.Int64:
|
|
case TypeCode.UInt64:
|
|
case TypeCode.Char:
|
|
case TypeCode.Single:
|
|
case TypeCode.Double:
|
|
case TypeCode.Decimal:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
// Just analyze the child.
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, castExpr.Expression, true, false, null);
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
string message;
|
|
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
|
|
// Evaluate the operand.
|
|
object operandValue = RuleExpressionWalker.Evaluate(execution, castExpr.Expression).Value;
|
|
|
|
// Get the cast-to type.
|
|
RuleExpressionInfo castExprInfo = execution.Validation.ExpressionInfo(castExpr);
|
|
if (castExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
throw exception;
|
|
}
|
|
Type toType = castExprInfo.ExpressionType;
|
|
|
|
// Handle null operand result.
|
|
if (operandValue == null)
|
|
{
|
|
// Here we are casting null to something. If it is a value type we can't do it.
|
|
if (ConditionHelper.IsNonNullableValueType(toType))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CastIncompatibleTypes, Messages.NullValue, RuleDecompiler.DecompileType(toType));
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
throw exception;
|
|
}
|
|
// If it's not a value type, null is good.
|
|
}
|
|
else
|
|
{
|
|
Type operandType = execution.Validation.ExpressionInfo(castExpr.Expression).ExpressionType;
|
|
operandValue = Executor.AdjustTypeWithCast(operandType, operandValue, toType);
|
|
}
|
|
|
|
return new RuleLiteralResult(operandValue);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
|
|
CodeExpression targetObject = castExpr.Expression;
|
|
if (targetObject == null)
|
|
{
|
|
RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastExpr);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
throw exception;
|
|
}
|
|
|
|
if (castExpr.TargetType == null)
|
|
{
|
|
RuleEvaluationException exception = new RuleEvaluationException(Messages.NullCastType);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = castExpr;
|
|
throw exception;
|
|
}
|
|
|
|
bool mustParenthesize = RuleDecompiler.MustParenthesize(castExpr, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
stringBuilder.Append("(");
|
|
RuleDecompiler.DecompileType(stringBuilder, castExpr.TargetType);
|
|
stringBuilder.Append(")");
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, castExpr);
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
CodeCastExpression newCast = new CodeCastExpression();
|
|
newCast.TargetType = TypeReferenceExpression.CloneType(castExpr.TargetType);
|
|
newCast.Expression = RuleExpressionWalker.Clone(castExpr.Expression);
|
|
return newCast;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeCastExpression castExpr = (CodeCastExpression)expression;
|
|
CodeCastExpression castComperand = (CodeCastExpression)comperand;
|
|
|
|
return TypeReferenceExpression.MatchType(castExpr.TargetType, castComperand.TargetType) &&
|
|
RuleExpressionWalker.Match(castExpr.Expression, castComperand.Expression);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Indexer Expression (indexer properties)
|
|
|
|
// CodeIndexerExpression
|
|
internal class IndexerPropertyExpression : RuleExpressionInternal
|
|
{
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
ValidationError error = null;
|
|
RulePropertyExpressionInfo propExprInfo = null;
|
|
bool includeNonPublic = false;
|
|
Type targetType = null;
|
|
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = indexerExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (targetObject is CodeTypeReferenceExpression)
|
|
{
|
|
error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
|
|
{
|
|
error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(indexerExpr))
|
|
return null;
|
|
|
|
RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, indexerExpr.TargetObject, false);
|
|
if (targetExprInfo == null) // error occurred, so simply return
|
|
return null;
|
|
|
|
targetType = targetExprInfo.ExpressionType;
|
|
if (targetType == null)
|
|
return null;
|
|
|
|
// if an error occurred (targetType == null), continue on to validate the arguments
|
|
if (targetType == typeof(NullLiteral))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
|
|
error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
targetType = null; // force exit after validating the arguments
|
|
}
|
|
|
|
List<CodeExpression> argExprs = new List<CodeExpression>();
|
|
|
|
bool hasInvalidArgument = false;
|
|
for (int i = 0; i < indexerExpr.Indices.Count; ++i)
|
|
{
|
|
CodeExpression argExpr = indexerExpr.Indices[i];
|
|
if (argExpr == null)
|
|
{
|
|
error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
else
|
|
{
|
|
CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
|
|
if (argDirection != null && argDirection.Direction != FieldDirection.In)
|
|
{
|
|
// No "ref" or "out" arguments are allowed on indexer arguments.
|
|
error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
|
|
if (argExpr is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
|
|
validation.AddError(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
|
|
// Validate the argument.
|
|
RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
|
|
if (argExprInfo == null)
|
|
hasInvalidArgument = true;
|
|
else
|
|
argExprs.Add(argExpr);
|
|
}
|
|
}
|
|
|
|
// Stop further validation if there was a problem with the target expression.
|
|
if (targetType == null)
|
|
return null;
|
|
|
|
// Stop further validation if there was a problem with any of the arguments.
|
|
if (hasInvalidArgument)
|
|
return null;
|
|
|
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
|
|
if (validation.AllowInternalMembers(targetType))
|
|
{
|
|
bindingFlags |= BindingFlags.NonPublic;
|
|
includeNonPublic = true;
|
|
}
|
|
|
|
// Everything okay so far, try to resolve the method.
|
|
propExprInfo = validation.ResolveIndexerProperty(targetType, bindingFlags, argExprs, out error);
|
|
if (propExprInfo == null)
|
|
{
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
validation.PopParentExpression();
|
|
}
|
|
|
|
PropertyInfo pi = propExprInfo.PropertyInfo;
|
|
|
|
MethodInfo accessorMethod = isWritten ? pi.GetSetMethod(includeNonPublic) : pi.GetGetMethod(includeNonPublic);
|
|
if (accessorMethod == null)
|
|
{
|
|
string baseMessage = isWritten ? Messages.UnknownPropertySet : Messages.UnknownPropertyGet;
|
|
message = string.Format(CultureInfo.CurrentCulture, baseMessage, pi.Name, RuleDecompiler.DecompileType(targetType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_CannotResolveMember);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (!validation.ValidateMemberAccess(targetObject, targetType, accessorMethod, pi.Name, indexerExpr))
|
|
return null;
|
|
|
|
// Validate any RuleAttributes, if present.
|
|
object[] attrs = pi.GetCustomAttributes(typeof(RuleAttribute), true);
|
|
if (attrs != null && attrs.Length > 0)
|
|
{
|
|
Stack<MemberInfo> methodStack = new Stack<MemberInfo>();
|
|
methodStack.Push(pi);
|
|
|
|
bool allAttributesValid = true;
|
|
foreach (RuleAttribute ruleAttr in attrs)
|
|
{
|
|
if (!ruleAttr.Validate(validation, pi, targetType, pi.GetIndexParameters()))
|
|
allAttributesValid = false;
|
|
}
|
|
|
|
methodStack.Pop();
|
|
|
|
if (!allAttributesValid)
|
|
return null;
|
|
}
|
|
|
|
return propExprInfo;
|
|
}
|
|
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
string message;
|
|
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
// Evaluate the target object and get its type.
|
|
CodeExpression targetObject = indexerExpr.TargetObject;
|
|
RuleExpressionInfo targetExprInfo = analysis.Validation.ExpressionInfo(targetObject);
|
|
if (targetExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = targetObject;
|
|
throw exception;
|
|
}
|
|
|
|
// Get the property info from the validator so we can look for [RuleRead] and [RuleWrite] attributes.
|
|
RulePropertyExpressionInfo propExprInfo = analysis.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
|
|
if (propExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
PropertyInfo pi = propExprInfo.PropertyInfo;
|
|
|
|
// Look for RuleAttribute's on the invoked indexer property.
|
|
List<CodeExpression> attributedExprs = new List<CodeExpression>();
|
|
analysis.AnalyzeRuleAttributes(pi, targetObject, qualifier, indexerExpr.Indices, pi.GetIndexParameters(), attributedExprs);
|
|
|
|
// See if the target object needs default analysis.
|
|
if (!attributedExprs.Contains(targetObject))
|
|
{
|
|
// The property had no [RuleRead] or [RuleWrite] attributes. The target object is read or
|
|
// written (as the case may be).
|
|
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, targetObject, isRead, isWritten, qualifier);
|
|
}
|
|
|
|
// See if any of the arguments need default analysis.
|
|
for (int i = 0; i < indexerExpr.Indices.Count; ++i)
|
|
{
|
|
CodeExpression argExpr = indexerExpr.Indices[i];
|
|
|
|
if (!attributedExprs.Contains(argExpr))
|
|
{
|
|
// Similar to the target object, we assume that this method can reads the value
|
|
// of the parameter, but none of its members.
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, argExpr, true, false, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
string message;
|
|
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
RulePropertyExpressionInfo propExprInfo = execution.Validation.ExpressionInfo(indexerExpr) as RulePropertyExpressionInfo;
|
|
if (propExprInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ExpressionNotValidated);
|
|
InvalidOperationException exception = new InvalidOperationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
PropertyInfo pi = propExprInfo.PropertyInfo;
|
|
|
|
// Evaluate the target...
|
|
object target = RuleExpressionWalker.Evaluate(execution, indexerExpr.TargetObject).Value;
|
|
|
|
if (target == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
// Evaluate the index arguments.
|
|
int actualArgCount = indexerExpr.Indices.Count;
|
|
ParameterInfo[] parmInfos = pi.GetIndexParameters();
|
|
object[] indexArgs = new object[parmInfos.Length];
|
|
|
|
int numFixedParameters = parmInfos.Length;
|
|
if (propExprInfo.NeedsParamsExpansion)
|
|
numFixedParameters -= 1;
|
|
|
|
int i;
|
|
for (i = 0; i < numFixedParameters; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
|
|
indexArgs[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
|
|
}
|
|
|
|
if (numFixedParameters < actualArgCount)
|
|
{
|
|
// This target indexer had a params array, and we are calling it with an
|
|
// expanded parameter list. E.g.,
|
|
// int this[int x, params string[] y]
|
|
// with the invocation:
|
|
// x.y[5, "crud", "kreeble", "glorp"]
|
|
// We need to translate this to:
|
|
// x.y[5, new string[] { "crud", "kreeble", "glorp" }]
|
|
|
|
ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
|
|
|
|
Type arrayType = lastParamInfo.ParameterType;
|
|
System.Diagnostics.Debug.Assert(arrayType.IsArray);
|
|
Type elementType = arrayType.GetElementType();
|
|
|
|
Array paramsArray = (Array)arrayType.InvokeMember(arrayType.Name, BindingFlags.CreateInstance, null, null, new object[] { actualArgCount - i }, CultureInfo.CurrentCulture);
|
|
for (; i < actualArgCount; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(indexerExpr.Indices[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, indexerExpr.Indices[i]);
|
|
paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
|
|
}
|
|
|
|
indexArgs[numFixedParameters] = paramsArray;
|
|
}
|
|
|
|
RulePropertyResult result = new RulePropertyResult(pi, target, indexArgs);
|
|
return result;
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
string message;
|
|
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = indexerExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
if (indexerExpr.Indices == null || indexerExpr.Indices.Count == 0)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = indexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, indexerExpr);
|
|
stringBuilder.Append('[');
|
|
RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[0], null);
|
|
for (int i = 1; i < indexerExpr.Indices.Count; ++i)
|
|
{
|
|
stringBuilder.Append(", ");
|
|
RuleExpressionWalker.Decompile(stringBuilder, indexerExpr.Indices[i], null);
|
|
}
|
|
stringBuilder.Append(']');
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = RuleExpressionWalker.Clone(indexerExpr.TargetObject);
|
|
|
|
CodeExpression[] indices = new CodeExpression[indexerExpr.Indices.Count];
|
|
for (int i = 0; i < indices.Length; ++i)
|
|
indices[i] = RuleExpressionWalker.Clone(indexerExpr.Indices[i]);
|
|
|
|
CodeIndexerExpression newIndexer = new CodeIndexerExpression(targetObject, indices);
|
|
return newIndexer;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeIndexerExpression indexerExpr = (CodeIndexerExpression)expression;
|
|
|
|
CodeIndexerExpression indexerComperand = (CodeIndexerExpression)comperand;
|
|
if (!RuleExpressionWalker.Match(indexerExpr.TargetObject, indexerComperand.TargetObject))
|
|
return false;
|
|
|
|
if (indexerExpr.Indices.Count != indexerComperand.Indices.Count)
|
|
return false;
|
|
|
|
for (int i = 0; i < indexerExpr.Indices.Count; ++i)
|
|
{
|
|
if (!RuleExpressionWalker.Match(indexerExpr.Indices[i], indexerComperand.Indices[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Array Indexer Expression (indexer properties)
|
|
|
|
// CodeArrayIndexerExpression
|
|
internal class ArrayIndexerExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
ValidationError error = null;
|
|
Type targetType = null;
|
|
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = arrayIndexerExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (targetObject is CodeTypeReferenceExpression)
|
|
{
|
|
error = new ValidationError(Messages.IndexersCannotBeStatic, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
|
|
{
|
|
error = new ValidationError(Messages.MissingIndexExpressions, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(arrayIndexerExpr))
|
|
return null;
|
|
|
|
RuleExpressionInfo targetExprInfo = RuleExpressionWalker.Validate(validation, arrayIndexerExpr.TargetObject, false);
|
|
if (targetExprInfo == null) // error occurred, so simply return
|
|
return null;
|
|
|
|
targetType = targetExprInfo.ExpressionType;
|
|
if (targetType == null)
|
|
return null;
|
|
|
|
// if an error occurred (targetType == null), continue on to validate the arguments
|
|
if (targetType == typeof(NullLiteral))
|
|
{
|
|
error = new ValidationError(Messages.NullIndexerTarget, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// The target type better be an array.
|
|
if (!targetType.IsArray)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotIndexType, RuleDecompiler.DecompileType(targetType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_CannotIndexType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
int rank = targetType.GetArrayRank();
|
|
if (arrayIndexerExpr.Indices.Count != rank)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadRank, rank);
|
|
error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadRank);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
bool hasInvalidArgument = false;
|
|
for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
|
|
{
|
|
CodeExpression argExpr = arrayIndexerExpr.Indices[i];
|
|
if (argExpr == null)
|
|
{
|
|
error = new ValidationError(Messages.NullIndexExpression, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
else
|
|
{
|
|
CodeDirectionExpression argDirection = argExpr as CodeDirectionExpression;
|
|
if (argDirection != null)
|
|
{
|
|
// No "ref" or "out" arguments are allowed on indexer arguments.
|
|
error = new ValidationError(Messages.IndexerArgCannotBeRefOrOut, ErrorNumbers.Error_IndexerArgCannotBeRefOrOut);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
|
|
if (argExpr is CodeTypeReferenceExpression)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, argExpr.GetType().FullName);
|
|
error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
|
|
validation.AddError(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
|
|
// Validate the argument.
|
|
RuleExpressionInfo argExprInfo = RuleExpressionWalker.Validate(validation, argExpr, false);
|
|
if (argExprInfo != null)
|
|
{
|
|
Type argType = argExprInfo.ExpressionType;
|
|
TypeCode argTypeCode = Type.GetTypeCode(argType);
|
|
|
|
// Any type that is, or can be converted to: int or long.
|
|
switch (argTypeCode)
|
|
{
|
|
case TypeCode.Byte:
|
|
case TypeCode.Char:
|
|
case TypeCode.Int16:
|
|
case TypeCode.Int32:
|
|
case TypeCode.Int64:
|
|
case TypeCode.SByte:
|
|
case TypeCode.UInt16:
|
|
break;
|
|
|
|
default:
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayIndexBadType, RuleDecompiler.DecompileType(argType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_ArrayIndexBadType);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = argExpr;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hasInvalidArgument = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop further validation if there was a problem with any of the arguments.
|
|
if (hasInvalidArgument)
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
validation.PopParentExpression();
|
|
}
|
|
|
|
// The result type is this array's element type.
|
|
return new RuleExpressionInfo(targetType.GetElementType());
|
|
}
|
|
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
// Analyze the target object, flowing down the qualifier from above. An expression
|
|
// like:
|
|
// this.a.b[2,3].c[4].d[5] = 99;
|
|
// should produce a path similar to:
|
|
// this/a/b/c/d
|
|
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.TargetObject, isRead, isWritten, qualifier);
|
|
|
|
// Analyze the indexer arguments. They are read.
|
|
for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, arrayIndexerExpr.Indices[i], true, false, null);
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
|
|
// Evaluate the target...
|
|
object target = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.TargetObject).Value;
|
|
|
|
if (target == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.TargetEvaluatedNullIndexer);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
// Evaluate the index arguments (converting them to "longs")
|
|
int actualArgCount = arrayIndexerExpr.Indices.Count;
|
|
long[] indexArgs = new long[actualArgCount];
|
|
|
|
for (int i = 0; i < actualArgCount; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(arrayIndexerExpr.Indices[i]).ExpressionType;
|
|
object argValue = RuleExpressionWalker.Evaluate(execution, arrayIndexerExpr.Indices[i]).Value;
|
|
indexArgs[i] = (long)Executor.AdjustType(argType, argValue, typeof(long));
|
|
}
|
|
|
|
RuleArrayElementResult result = new RuleArrayElementResult((Array)target, indexArgs);
|
|
return result;
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
string message;
|
|
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = arrayIndexerExpr.TargetObject;
|
|
if (targetObject == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullIndexerTarget);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
if (arrayIndexerExpr.Indices == null || arrayIndexerExpr.Indices.Count == 0)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.MissingIndexExpressions);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = arrayIndexerExpr;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, targetObject, arrayIndexerExpr);
|
|
stringBuilder.Append('[');
|
|
RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[0], null);
|
|
for (int i = 1; i < arrayIndexerExpr.Indices.Count; ++i)
|
|
{
|
|
stringBuilder.Append(", ");
|
|
RuleExpressionWalker.Decompile(stringBuilder, arrayIndexerExpr.Indices[i], null);
|
|
}
|
|
stringBuilder.Append(']');
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
|
|
CodeExpression targetObject = RuleExpressionWalker.Clone(arrayIndexerExpr.TargetObject);
|
|
|
|
CodeExpression[] indices = new CodeExpression[arrayIndexerExpr.Indices.Count];
|
|
for (int i = 0; i < indices.Length; ++i)
|
|
indices[i] = RuleExpressionWalker.Clone(arrayIndexerExpr.Indices[i]);
|
|
|
|
CodeArrayIndexerExpression newIndexer = new CodeArrayIndexerExpression(targetObject, indices);
|
|
return newIndexer;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeArrayIndexerExpression arrayIndexerExpr = (CodeArrayIndexerExpression)expression;
|
|
|
|
CodeArrayIndexerExpression indexerComperand = (CodeArrayIndexerExpression)comperand;
|
|
if (!RuleExpressionWalker.Match(arrayIndexerExpr.TargetObject, indexerComperand.TargetObject))
|
|
return false;
|
|
|
|
if (arrayIndexerExpr.Indices.Count != indexerComperand.Indices.Count)
|
|
return false;
|
|
|
|
for (int i = 0; i < arrayIndexerExpr.Indices.Count; ++i)
|
|
{
|
|
if (!RuleExpressionWalker.Match(arrayIndexerExpr.Indices[i], indexerComperand.Indices[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Object Create expression
|
|
internal class ObjectCreateExpression : RuleExpressionInternal
|
|
{
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
ValidationError error;
|
|
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
if (isWritten)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
|
|
error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (createExpression.CreateType == null)
|
|
{
|
|
error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
Type resultType = validation.ResolveType(createExpression.CreateType);
|
|
if (resultType == null)
|
|
return null;
|
|
|
|
// look up parameters
|
|
List<CodeExpression> parameters = new List<CodeExpression>();
|
|
try
|
|
{
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(createExpression))
|
|
return null;
|
|
|
|
bool hasInvalidArgument = false;
|
|
for (int i = 0; i < createExpression.Parameters.Count; ++i)
|
|
{
|
|
CodeExpression parameter = createExpression.Parameters[i];
|
|
if (parameter == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorParameter, i.ToString(CultureInfo.CurrentCulture), RuleDecompiler.DecompileType(resultType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
hasInvalidArgument = true;
|
|
}
|
|
else
|
|
{
|
|
RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, parameter, false);
|
|
if (parameterInfo == null)
|
|
hasInvalidArgument = true;
|
|
parameters.Add(parameter);
|
|
}
|
|
}
|
|
// quit if parameters not valid
|
|
if (hasInvalidArgument)
|
|
return null;
|
|
}
|
|
finally
|
|
{
|
|
validation.PopParentExpression();
|
|
}
|
|
|
|
// see if we can find the matching constructor
|
|
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;
|
|
if (validation.AllowInternalMembers(resultType))
|
|
bindingFlags |= BindingFlags.NonPublic;
|
|
|
|
// creating a value-type object with no parameters can always be done
|
|
if ((resultType.IsValueType) && (parameters.Count == 0))
|
|
return new RuleExpressionInfo(resultType);
|
|
|
|
// error if type is an abstract type
|
|
if (resultType.IsAbstract)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
RuleConstructorExpressionInfo constructorInvokeInfo = validation.ResolveConstructor(resultType, bindingFlags, parameters, out error);
|
|
if (constructorInvokeInfo == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.UnknownConstructor, RuleDecompiler.DecompileType(resultType));
|
|
error = new ValidationError(message, ErrorNumbers.Error_MethodNotExists);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
return constructorInvokeInfo;
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
// check each parameter
|
|
foreach (CodeExpression p in createExpression.Parameters)
|
|
{
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
|
|
}
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
if (createExpression.CreateType == null)
|
|
{
|
|
RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionInfo expressionInfo = execution.Validation.ExpressionInfo(createExpression);
|
|
if (expressionInfo == null) // Oops, someone forgot to validate.
|
|
{
|
|
InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
RuleConstructorExpressionInfo createExpressionInfo = expressionInfo as RuleConstructorExpressionInfo;
|
|
if (createExpressionInfo == null)
|
|
{
|
|
// it's just a regular RuleExpressionInfo, which means this is a value-type with no parameters
|
|
return new RuleLiteralResult(Activator.CreateInstance(expressionInfo.ExpressionType));
|
|
}
|
|
|
|
ConstructorInfo constructor = createExpressionInfo.ConstructorInfo;
|
|
object[] arguments = null;
|
|
RuleExpressionResult[] outArgumentResults = null;
|
|
|
|
if (createExpression.Parameters != null && createExpression.Parameters.Count > 0)
|
|
{
|
|
int actualArgCount = createExpression.Parameters.Count;
|
|
ParameterInfo[] parmInfos = constructor.GetParameters();
|
|
|
|
arguments = new object[parmInfos.Length];
|
|
|
|
int numFixedParameters = parmInfos.Length;
|
|
if (createExpressionInfo.NeedsParamsExpansion)
|
|
numFixedParameters -= 1;
|
|
|
|
int i;
|
|
|
|
// Evaluate the fixed portion of the parameter list.
|
|
for (i = 0; i < numFixedParameters; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
|
|
|
|
// Special procesing of direction expressions to keep track of out arguments (& ref).
|
|
CodeDirectionExpression direction = createExpression.Parameters[i] as CodeDirectionExpression;
|
|
if (direction != null && (direction.Direction == FieldDirection.Ref || direction.Direction == FieldDirection.Out))
|
|
{
|
|
// lazy creation of fieldsToSet
|
|
if (outArgumentResults == null)
|
|
outArgumentResults = new RuleExpressionResult[actualArgCount];
|
|
// keep track of this out expression so we can set it later
|
|
outArgumentResults[i] = argResult;
|
|
}
|
|
|
|
arguments[i] = Executor.AdjustType(argType, argResult.Value, parmInfos[i].ParameterType);
|
|
}
|
|
|
|
if (numFixedParameters < actualArgCount)
|
|
{
|
|
// This target method had a params array, and we are calling it with an
|
|
// expanded parameter list. E.g.,
|
|
// void foo(int x, params string[] y)
|
|
// with the invocation:
|
|
// foo(5, "crud", "kreeble", "glorp")
|
|
// We need to translate this to:
|
|
// foo(5, new string[] { "crud", "kreeble", "glorp" })
|
|
|
|
ParameterInfo lastParamInfo = parmInfos[numFixedParameters];
|
|
|
|
Type arrayType = lastParamInfo.ParameterType;
|
|
System.Diagnostics.Debug.Assert(arrayType.IsArray);
|
|
Type elementType = arrayType.GetElementType();
|
|
|
|
Array paramsArray = Array.CreateInstance(elementType, actualArgCount - i);
|
|
for (; i < actualArgCount; ++i)
|
|
{
|
|
Type argType = execution.Validation.ExpressionInfo(createExpression.Parameters[i]).ExpressionType;
|
|
RuleExpressionResult argResult = RuleExpressionWalker.Evaluate(execution, createExpression.Parameters[i]);
|
|
paramsArray.SetValue(Executor.AdjustType(argType, argResult.Value, elementType), i - numFixedParameters);
|
|
}
|
|
|
|
arguments[numFixedParameters] = paramsArray;
|
|
}
|
|
}
|
|
|
|
object result;
|
|
try
|
|
{
|
|
result = constructor.Invoke(arguments);
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
// if there is no inner exception, leave it untouched
|
|
if (e.InnerException == null)
|
|
throw;
|
|
string message = string.Format(CultureInfo.CurrentCulture,
|
|
Messages.Error_ConstructorInvoke,
|
|
RuleDecompiler.DecompileType(createExpressionInfo.ExpressionType),
|
|
e.InnerException.Message);
|
|
throw new TargetInvocationException(message, e.InnerException);
|
|
}
|
|
|
|
// any out/ref parameters that need to be assigned?
|
|
if (outArgumentResults != null)
|
|
{
|
|
for (int i = 0; i < createExpression.Parameters.Count; ++i)
|
|
{
|
|
if (outArgumentResults[i] != null)
|
|
outArgumentResults[i].Value = arguments[i];
|
|
}
|
|
}
|
|
return new RuleLiteralResult(result);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
stringBuilder.Append("new ");
|
|
RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
|
|
|
|
// Decompile the arguments
|
|
stringBuilder.Append('(');
|
|
for (int i = 0; i < createExpression.Parameters.Count; ++i)
|
|
{
|
|
CodeExpression paramExpr = createExpression.Parameters[i];
|
|
if (paramExpr == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
if (i > 0)
|
|
stringBuilder.Append(", ");
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
|
|
}
|
|
stringBuilder.Append(')');
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
CodeObjectCreateExpression newCreate = new CodeObjectCreateExpression();
|
|
newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
|
|
foreach (CodeExpression p in createExpression.Parameters)
|
|
{
|
|
newCreate.Parameters.Add(RuleExpressionWalker.Clone(p));
|
|
}
|
|
return newCreate;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeObjectCreateExpression createExpression = (CodeObjectCreateExpression)expression;
|
|
|
|
CodeObjectCreateExpression createComperand = comperand as CodeObjectCreateExpression;
|
|
if (createComperand == null)
|
|
return false;
|
|
// check types
|
|
if (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType))
|
|
return false;
|
|
// check parameters
|
|
if (createExpression.Parameters.Count != createComperand.Parameters.Count)
|
|
return false;
|
|
for (int i = 0; i < createExpression.Parameters.Count; ++i)
|
|
if (!RuleExpressionWalker.Match(createExpression.Parameters[i], createComperand.Parameters[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Array Create expression
|
|
internal class ArrayCreateExpression : RuleExpressionInternal
|
|
{
|
|
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
|
internal override RuleExpressionInfo Validate(CodeExpression expression, RuleValidation validation, bool isWritten)
|
|
{
|
|
string message;
|
|
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
if (isWritten)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.CannotWriteToExpression, typeof(CodeObjectCreateExpression).ToString());
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_InvalidAssignTarget);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
if (createExpression.CreateType == null)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.NullTypeType, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
Type resultType = validation.ResolveType(createExpression.CreateType);
|
|
if (resultType == null)
|
|
return null;
|
|
|
|
// size CodeDom has limited support for arrays (only 1 dimensional, not more)
|
|
// we limit CodeArrayCreateExpression to a single dimension
|
|
// (i.e. CodeArrayCreateExpression cannot define int[5,3] or int[5][3],
|
|
// but it is possible to define int[][3] and then use initializers to
|
|
// fill it in. But we only support int[] or int[3].)
|
|
if (resultType.IsArray)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ArrayTypeInvalid, resultType.Name);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Early exit from this if a cycle is detected.
|
|
if (!validation.PushParentExpression(createExpression))
|
|
return null;
|
|
|
|
if (createExpression.Size < 0)
|
|
{
|
|
ValidationError error = new ValidationError(Messages.ArraySizeInvalid, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
|
|
// look up size (if specified)
|
|
if (createExpression.SizeExpression != null)
|
|
{
|
|
RuleExpressionInfo sizeInfo = RuleExpressionWalker.Validate(validation, createExpression.SizeExpression, false);
|
|
if (sizeInfo == null)
|
|
return null;
|
|
if ((sizeInfo.ExpressionType != typeof(int))
|
|
&& (sizeInfo.ExpressionType != typeof(uint))
|
|
&& (sizeInfo.ExpressionType != typeof(long))
|
|
&& (sizeInfo.ExpressionType != typeof(ulong)))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.ArraySizeTypeInvalid, sizeInfo.ExpressionType.Name);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
bool parameterInvalid = false;
|
|
for (int i = 0; i < createExpression.Initializers.Count; ++i)
|
|
{
|
|
CodeExpression init = createExpression.Initializers[i];
|
|
if (init == null)
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.MissingInitializer, resultType.Name);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
RuleExpressionInfo parameterInfo = RuleExpressionWalker.Validate(validation, init, false);
|
|
if (parameterInfo == null)
|
|
{
|
|
parameterInvalid = true;
|
|
}
|
|
else
|
|
{
|
|
// can we convert the result type to the array type?
|
|
ValidationError error;
|
|
if (!RuleValidation.StandardImplicitConversion(parameterInfo.ExpressionType, resultType, init, out error))
|
|
{
|
|
// types must match
|
|
if (error != null)
|
|
{
|
|
// we got an error from the conversion, so give it back as well as a new error
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
}
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerMismatch, i, resultType.Name);
|
|
error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
// if any errors get out
|
|
if (parameterInvalid)
|
|
return null;
|
|
|
|
// now it gets tricky. CodeArrayCreateExpression constructors allow:
|
|
// 1) size as int
|
|
// 2) size as CodeExpression
|
|
// 3) initializers as params array
|
|
// However, we allow a size and initializers, so try to verify size >= #initializers
|
|
// size can be an int, uint, long, or ulong
|
|
double size = -1;
|
|
if (createExpression.SizeExpression != null)
|
|
{
|
|
CodePrimitiveExpression prim = createExpression.SizeExpression as CodePrimitiveExpression;
|
|
if ((prim != null) && (prim.Value != null))
|
|
size = (double)Executor.AdjustType(prim.Value.GetType(), prim.Value, typeof(double));
|
|
if (createExpression.Size > 0)
|
|
{
|
|
// both size and SizeExpression specified, complain
|
|
ValidationError error = new ValidationError(Messages.ArraySizeBoth, ErrorNumbers.Error_ParameterNotSet);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
else if (createExpression.Size > 0)
|
|
size = createExpression.Size;
|
|
|
|
if ((size >= 0) && (createExpression.Initializers.Count > size))
|
|
{
|
|
message = string.Format(CultureInfo.CurrentCulture, Messages.InitializerCountMismatch, createExpression.Initializers.Count, size);
|
|
ValidationError error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
|
|
error.UserData[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
validation.Errors.Add(error);
|
|
return null;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
validation.PopParentExpression();
|
|
}
|
|
return new RuleExpressionInfo(resultType.MakeArrayType());
|
|
}
|
|
|
|
internal override void AnalyzeUsage(CodeExpression expression, RuleAnalysis analysis, bool isRead, bool isWritten, RulePathQualifier qualifier)
|
|
{
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
if (createExpression.SizeExpression != null)
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, createExpression.SizeExpression, true, false, null);
|
|
foreach (CodeExpression p in createExpression.Initializers)
|
|
RuleExpressionWalker.AnalyzeUsage(analysis, p, true, false, null);
|
|
}
|
|
|
|
internal override RuleExpressionResult Evaluate(CodeExpression expression, RuleExecution execution)
|
|
{
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
if (createExpression.CreateType == null)
|
|
{
|
|
RuleEvaluationException exception = new RuleEvaluationException(Messages.NullTypeType);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
RuleExpressionInfo createExpressionInfo = execution.Validation.ExpressionInfo(createExpression);
|
|
if (createExpression == null) // Oops, someone forgot to validate.
|
|
{
|
|
InvalidOperationException exception = new InvalidOperationException(Messages.ExpressionNotValidated);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
// type should be an array type already
|
|
Type type = createExpressionInfo.ExpressionType;
|
|
Type elementType = type.GetElementType();
|
|
|
|
// assume this has been validated, so only 1 size specified
|
|
int size = 0;
|
|
if (createExpression.SizeExpression != null)
|
|
{
|
|
Type sizeType = execution.Validation.ExpressionInfo(createExpression.SizeExpression).ExpressionType;
|
|
RuleExpressionResult sizeResult = RuleExpressionWalker.Evaluate(execution, createExpression.SizeExpression);
|
|
if (sizeType == typeof(int))
|
|
size = (int)sizeResult.Value;
|
|
else if (sizeType == typeof(long))
|
|
size = (int)((long)sizeResult.Value);
|
|
else if (sizeType == typeof(uint))
|
|
size = (int)((uint)sizeResult.Value);
|
|
else if (sizeType == typeof(ulong))
|
|
size = (int)((ulong)sizeResult.Value);
|
|
}
|
|
else if (createExpression.Size != 0)
|
|
size = createExpression.Size;
|
|
else
|
|
size = createExpression.Initializers.Count;
|
|
|
|
Array result = Array.CreateInstance(elementType, size);
|
|
if (createExpression.Initializers != null)
|
|
for (int i = 0; i < createExpression.Initializers.Count; ++i)
|
|
{
|
|
CodeExpression initializer = createExpression.Initializers[i];
|
|
Type initializerType = execution.Validation.ExpressionInfo(initializer).ExpressionType;
|
|
RuleExpressionResult initializerResult = RuleExpressionWalker.Evaluate(execution, initializer);
|
|
result.SetValue(Executor.AdjustType(initializerType, initializerResult.Value, elementType), i);
|
|
}
|
|
return new RuleLiteralResult(result);
|
|
}
|
|
|
|
internal override void Decompile(CodeExpression expression, StringBuilder stringBuilder, CodeExpression parentExpression)
|
|
{
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
bool mustParenthesize = RuleDecompiler.MustParenthesize(createExpression, parentExpression);
|
|
if (mustParenthesize)
|
|
stringBuilder.Append("(");
|
|
|
|
stringBuilder.Append("new ");
|
|
RuleDecompiler.DecompileType(stringBuilder, createExpression.CreateType);
|
|
stringBuilder.Append('[');
|
|
if (createExpression.SizeExpression != null)
|
|
RuleExpressionWalker.Decompile(stringBuilder, createExpression.SizeExpression, null);
|
|
else if ((createExpression.Size != 0) || (createExpression.Initializers.Count == 0))
|
|
stringBuilder.Append(createExpression.Size);
|
|
stringBuilder.Append(']');
|
|
if (createExpression.Initializers.Count > 0)
|
|
{
|
|
stringBuilder.Append(" { ");
|
|
for (int i = 0; i < createExpression.Initializers.Count; ++i)
|
|
{
|
|
CodeExpression paramExpr = createExpression.Initializers[i];
|
|
if (paramExpr == null)
|
|
{
|
|
string message = string.Format(CultureInfo.CurrentCulture, Messages.NullConstructorTypeParameter, i.ToString(CultureInfo.CurrentCulture), createExpression.CreateType);
|
|
RuleEvaluationException exception = new RuleEvaluationException(message);
|
|
exception.Data[RuleUserDataKeys.ErrorObject] = createExpression;
|
|
throw exception;
|
|
}
|
|
|
|
if (i > 0)
|
|
stringBuilder.Append(", ");
|
|
|
|
RuleExpressionWalker.Decompile(stringBuilder, paramExpr, null);
|
|
}
|
|
stringBuilder.Append('}');
|
|
}
|
|
|
|
if (mustParenthesize)
|
|
stringBuilder.Append(")");
|
|
}
|
|
|
|
internal override CodeExpression Clone(CodeExpression expression)
|
|
{
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
CodeArrayCreateExpression newCreate = new CodeArrayCreateExpression();
|
|
newCreate.CreateType = TypeReferenceExpression.CloneType(createExpression.CreateType);
|
|
newCreate.Size = createExpression.Size;
|
|
if (createExpression.SizeExpression != null)
|
|
newCreate.SizeExpression = RuleExpressionWalker.Clone(createExpression.SizeExpression);
|
|
foreach (CodeExpression p in createExpression.Initializers)
|
|
newCreate.Initializers.Add(RuleExpressionWalker.Clone(p));
|
|
return newCreate;
|
|
}
|
|
|
|
internal override bool Match(CodeExpression expression, CodeExpression comperand)
|
|
{
|
|
CodeArrayCreateExpression createExpression = (CodeArrayCreateExpression)expression;
|
|
|
|
CodeArrayCreateExpression createComperand = comperand as CodeArrayCreateExpression;
|
|
if ((createComperand == null)
|
|
|| (createExpression.Size != createComperand.Size)
|
|
|| (!TypeReferenceExpression.MatchType(createExpression.CreateType, createComperand.CreateType)))
|
|
return false;
|
|
// check SizeExpression, if it exists
|
|
if (createExpression.SizeExpression != null)
|
|
{
|
|
if (createComperand.SizeExpression == null)
|
|
return false;
|
|
if (!RuleExpressionWalker.Match(createExpression.SizeExpression, createComperand.SizeExpression))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (createComperand.SizeExpression != null)
|
|
return false;
|
|
}
|
|
// check initializers
|
|
if (createExpression.Initializers.Count != createComperand.Initializers.Count)
|
|
return false;
|
|
for (int i = 0; i < createExpression.Initializers.Count; ++i)
|
|
if (!RuleExpressionWalker.Match(createExpression.Initializers[i], createComperand.Initializers[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|