/// <summary> /// Creates a <see cref="MemberExpression"/> accessing a field. /// </summary> /// <param name="expression">The containing object of the field. This can be null for static fields.</param> /// <param name="field">The field to be accessed.</param> /// <returns>The created <see cref="MemberExpression"/>.</returns> public static MemberExpression Field(Expression expression, FieldInfo field) { ContractUtils.RequiresNotNull(field, "field"); if (field.IsStatic) { if (expression != null) { throw new ArgumentException(Strings.OnlyStaticFieldsHaveNullInstance, "expression"); } } else { if (expression == null) { throw new ArgumentException(Strings.OnlyStaticFieldsHaveNullInstance, "field"); } RequiresCanRead(expression, "expression"); if (!TypeHelper.AreReferenceAssignable(field.DeclaringType, expression.Type)) { throw Error.FieldInfoNotDefinedForType(field.DeclaringType, field.Name, expression.Type); } } return(MemberExpression.Make(expression, field)); }
/// <summary> /// Creates a <see cref="BlockExpression"/> that contains the given variables and expressions. /// </summary> /// <param name="type">The result type of the block.</param> /// <param name="variables">The variables in the block.</param> /// <param name="expressions">The expressions in the block.</param> /// <returns>The created <see cref="BlockExpression"/>.</returns> public static BlockExpression Block(Type type, IEnumerable <ParameterExpression> variables, IEnumerable <Expression> expressions) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(expressions, "expressions"); var expressionList = expressions.ToReadOnly(); var variableList = variables.ToReadOnly(); ContractUtils.RequiresNotEmpty(expressionList, "expressions"); RequiresCanRead(expressionList, "expressions"); ValidateVariables(variableList, "variables"); var last = expressionList.Last(); if (type != typeof(void)) { if (!TypeHelper.AreReferenceAssignable(type, last.Type)) { throw Error.ArgumentTypesMustMatch(); } } if (type != last.Type) { return(new ScopeWithType(variableList, expressionList, type)); } if (expressionList.Count == 1) { return(new Scope1(variableList, expressionList[0])); } return(new ScopeN(variableList, expressionList)); }
internal Expression ReduceTypeEqual() { var cType = Expression.Type; // For value types (including Void, but not nullables), we can // determine the result now if (cType.IsValueType && !cType.IsNullableType()) { return(Block(Expression, Constant(cType == _typeOperand.GetNonNullableType()))); } // Can check the value right now for constants. if (Expression.NodeType == ExpressionType.Constant) { return(ReduceConstantTypeEqual()); } // If the operand type is a sealed reference type or a nullable // type, it will match if value is not null if (cType.IsSealed && (cType == _typeOperand)) { if (cType.IsNullableType()) { return(NotEqual(Expression, Constant(null, Expression.Type))); } else { return(ReferenceNotEqual(Expression, Constant(null, Expression.Type))); } } // expression is a ByVal parameter. Can safely reevaluate. var parameter = Expression as ParameterExpression; if (parameter != null && !parameter.IsByRef) { return(ByValParameterTypeEqual(parameter)); } // Create a temp so we only evaluate the left side once parameter = Parameter(typeof(object)); // Convert to object if necessary var expression = Expression; if (!TypeHelper.AreReferenceAssignable(typeof(object), expression.Type)) { expression = Convert(expression, typeof(object)); } return(Block( new[] { parameter }, Assign(parameter, expression), ByValParameterTypeEqual(parameter) )); }
private void EmitSwitchExpression(Expression expr, CompilationFlags flags) { var node = (SwitchExpression)expr; // Try to emit it as an IL switch. Works for integer types. if (TryEmitSwitchInstruction(node, flags)) { return; } // Try to emit as a hashtable lookup. Works for strings. if (TryEmitHashtableSwitch(node, flags)) { return; } // // Fall back to a series of tests. We need to IL gen instead of // transform the tree to avoid stack overflow on a big switch. // var switchValue = Expression.Parameter(node.SwitchValue.Type, "switchValue"); var testValue = Expression.Parameter(GetTestValueType(node), "testValue"); _scope.AddLocal(this, switchValue); _scope.AddLocal(this, testValue); EmitExpression(node.SwitchValue); _scope.EmitSet(switchValue); // Emit tests var labels = new Label[node.Cases.Count]; var isGoto = new bool[node.Cases.Count]; var n = node.Cases.Count; for (int i = 0; i < n; i++) { DefineSwitchCaseLabel(node.Cases[i], out labels[i], out isGoto[i]); foreach (Expression test in node.Cases[i].TestValues) { // Pull the test out into a temp so it runs on the same // stack as the switch. This simplifies spilling. EmitExpression(test); _scope.EmitSet(testValue); Debug.Assert(TypeHelper.AreReferenceAssignable(testValue.Type, test.Type)); EmitExpressionAndBranch(true, Expression.Equal(switchValue, testValue, false, node.Comparison), labels[i]); } } // Define labels var end = _ilg.DefineLabel(); var @default = (node.DefaultBody == null) ? end : _ilg.DefineLabel(); // Emit the case and default bodies EmitSwitchCases(node, labels, isGoto, @default, end, flags); }
private void EmitExpressionAddress(Expression node, Type type) { Debug.Assert(TypeHelper.AreReferenceAssignable(type, node.Type)); EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart); var tmp = GetLocal(type); _ilg.Emit(OpCodes.Stloc, tmp); _ilg.Emit(OpCodes.Ldloca, tmp); }
private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection <Expression> arguments) { if (indexes.Length > 0) { if (indexes.Length != arguments.Count) { throw Error.IncorrectNumberOfMethodCallArguments(method); } Expression[] newArgs = null; var n = indexes.Length; for (var i = 0; i < n; i++) { var arg = arguments[i]; var pi = indexes[i]; RequiresCanRead(arg, "arguments"); var pType = pi.ParameterType; if (pType.IsByRef) { throw Error.AccessorsCannotHaveByRefArgs(); } TypeHelper.ValidateType(pType); if (!TypeHelper.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); } } if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (var j = 0; j < i; j++) { newArgs[j] = arguments[j]; } } if (newArgs != null) { newArgs[i] = arg; } } if (newArgs != null) { arguments = new TrueReadOnlyCollection <Expression>(newArgs); } } else if (arguments.Count > 0) { throw Error.IncorrectNumberOfMethodCallArguments(method); } }
// For optimized Equal/NotEqual, we can eliminate reference // conversions. IL allows comparing managed pointers regardless of // type. See ECMA-335 "Binary Comparison or Branch Operations", in // Partition III, Section 1.5 Table 4. private static Expression GetEqualityOperand(Expression expression) { if (expression.NodeType == ExpressionType.Convert) { var convert = (UnaryExpression)expression; if (TypeHelper.AreReferenceAssignable(convert.Type, convert.Operand.Type)) { return(convert.Operand); } } return(expression); }
// Standard argument validation, taken from ValidateArgumentTypes private static void ValidateGotoType(Type expectedType, ref Expression value, string paramName) { RequiresCanRead(value, paramName); if (expectedType != typeof(void)) { if (!TypeHelper.AreReferenceAssignable(expectedType, value.Type)) { // C# autoquotes return values, so we'll do that here if (!TryQuote(expectedType, ref value)) { throw Error.ExpressionTypeDoesNotMatchLabel(value.Type, expectedType); } } } }
/// <summary> /// Creates a new array expression of the specified type from the provided initializers. /// </summary> /// <param name="type">A Type that represents the element type of the array.</param> /// <param name="initializers">The expressions used to create the array elements.</param> /// <returns>An instance of the <see cref="NewArrayExpression"/>.</returns> public static NewArrayExpression NewArrayInit(Type type, IEnumerable <Expression> initializers) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(initializers, "initializers"); if (type == typeof(void)) { throw Error.ArgumentCannotBeOfTypeVoid(); } var initializerList = initializers.ToReadOnly(); Expression[] newList = null; var n = initializerList.Count; for (var i = 0; i < n; i++) { var expr = initializerList[i]; RequiresCanRead(expr, "initializers"); if (!TypeHelper.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)) { throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[n]; for (var j = 0; j < i; j++) { newList[j] = initializerList[j]; } } } if (newList != null) { newList[i] = expr; } } if (newList != null) { initializerList = new TrueReadOnlyCollection <Expression>(newList); } return(NewArrayExpression.Make(ExpressionType.NewArrayInit, type.MakeArrayType(), initializerList)); }
private static bool IsCompatible(PropertyInfo pi, Expression[] args) { var mi = pi.GetGetMethod(true); ParameterInfo[] parms; if (mi != null) { parms = mi.GetParameters(); } else { mi = pi.GetSetMethod(true); //The setter has an additional parameter for the value to set, //need to remove the last type to match the arguments. parms = mi.GetParameters().RemoveLast(); } if (mi == null) { return(false); // TODO: Test coverage? } if (args == null) { return(parms.Length == 0); } if (parms.Length != args.Length) { return(false); } for (var i = 0; i < args.Length; i++) { if (args[i] == null) { return(false); } if (!TypeHelper.AreReferenceAssignable(parms[i].ParameterType, args[i].Type)) { return(false); } } return(true); }
internal static void ValidateLift(IList <ParameterExpression> variables, IList <Expression> arguments) { Debug.Assert(variables != null); Debug.Assert(arguments != null); var n = variables.Count; if (n != arguments.Count) { throw Error.IncorrectNumberOfIndexes(); } for (int i = 0; i < n; i++) { if (!TypeHelper.AreReferenceAssignable(variables[i].Type, arguments[i].Type.GetNonNullableType())) { throw Error.ArgumentTypesMustMatch(); } } }
//Validate that the body of the try expression must have the same type as the body of every try block. private static void ValidateTryAndCatchHaveSameType(Type type, Expression tryBody, ReadOnlyCollection <CatchBlock> handlers) { // Type unification ... all parts must be reference assignable to "type" if (type != null) { if (type != typeof(void)) { if (!TypeHelper.AreReferenceAssignable(type, tryBody.Type)) { throw Error.ArgumentTypesMustMatch(); } foreach (var cb in handlers) { if (!TypeHelper.AreReferenceAssignable(type, cb.Body.Type)) { throw Error.ArgumentTypesMustMatch(); } } } } else if (tryBody == null || tryBody.Type == typeof(void)) { //The body of every try block must be null or have void type. foreach (var cb in handlers) { if (cb.Body != null && cb.Body.Type != typeof(void)) { throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry(); } } } else { //Body of every catch must have the same type of body of try. type = tryBody.Type; foreach (var cb in handlers) { if (cb.Body == null || cb.Body.Type != type) { throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry(); } } } }
private static void ValidateSwitchCaseType(Expression @case, bool customType, Type resultType, string parameterName) { if (customType) { if (resultType != typeof(void)) { if (!TypeHelper.AreReferenceAssignable(resultType, @case.Type)) { throw new ArgumentException(Strings.ArgumentTypesMustMatch, parameterName); } } } else { if (resultType != @case.Type) { throw new ArgumentException(Strings.AllCaseBodiesMustHaveSameType, parameterName); } } }
private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) { if (type == typeof(void)) { EmitExpressionAsVoid(node, flags); } else { // if the node is emitted as a different type, CastClass IL is emitted at the end, // should not emit with tail calls. if (node.Type != type) { EmitExpression(node); Debug.Assert(TypeHelper.AreReferenceAssignable(type, node.Type)); _ilg.Emit(OpCodes.Castclass, type); } else { // emit the with the flags and emit emit expression start EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart)); } } }
/// <summary> /// Reduces this node to a simpler expression. If CanReduce returns /// true, this should return a valid expression. This method is /// allowed to return another node which itself must be reduced. /// </summary> /// <returns>The reduced expression.</returns> /// <remarks > /// Unlike Reduce, this method checks that the reduced node satisfies /// certain invariants. /// </remarks> public Expression ReduceAndCheck() { if (!CanReduce) { throw Error.MustBeReducible(); } var newNode = Reduce(); // 1. Reduction must return a new, non-null node // 2. Reduction must return a new node whose result type can be assigned to the type of the original node if (newNode == null || newNode == this) { throw Error.MustReduceToDifferent(); } if (!TypeHelper.AreReferenceAssignable(Type, newNode.Type)) { throw Error.ReducedNotCompatible(); } return(newNode); }
/// <summary> /// Creates a <see cref="ConditionalExpression"/>. /// </summary> /// <param name="test">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.Test"/> property equal to.</param> /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfTrue"/> property equal to.</param> /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="P:ConditionalExpression.IfFalse"/> property equal to.</param> /// <param name="type">A <see cref="Type"/> to set the <see cref="P:Expression.Type"/> property equal to.</param> /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to /// <see cref="F:ExpressionType.Conditional"/> and the <see cref="P:ConditionalExpression.Test"/>, <see cref="P:ConditionalExpression.IfTrue"/>, /// and <see cref="P:ConditionalExpression.IfFalse"/> properties set to the specified values.</returns> /// <remarks>This method allows explicitly unifying the result type of the conditional expression in cases where the types of <paramref name="ifTrue"/> /// and <paramref name="ifFalse"/> expressions are not equal. Types of both <paramref name="ifTrue"/> and <paramref name="ifFalse"/> must be implicitly /// reference assignable to the result type. The <paramref name="type"/> is allowed to be <see cref="System.Void"/>.</remarks> public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) { RequiresCanRead(test, "test"); RequiresCanRead(ifTrue, "ifTrue"); RequiresCanRead(ifFalse, "ifFalse"); ContractUtils.RequiresNotNull(type, "type"); if (test.Type != typeof(bool)) { throw Error.ArgumentMustBeBoolean(); } if (type != typeof(void)) { if (!TypeHelper.AreReferenceAssignable(type, ifTrue.Type) || !TypeHelper.AreReferenceAssignable(type, ifFalse.Type)) { throw Error.ArgumentTypesMustMatch(); } } return(ConditionalExpression.Make(test, ifTrue, ifFalse, type)); }
public static Expression ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi) { RequiresCanRead(arg, "arguments"); var pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } TypeHelper.ValidateType(pType); if (!TypeHelper.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { // Throw the right error for the node we were given switch (nodeKind) { case ExpressionType.New: throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); case ExpressionType.Invoke: throw Error.ExpressionTypeDoesNotMatchParameter(arg.Type, pType); #if !NET35 case ExpressionType.Dynamic: #endif case ExpressionType.Call: throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); default: throw ContractUtils.Unreachable; } } } return(arg); }
private static void VerifyRewrite(Result result, Expression node) { Debug.Assert(result.Node != null); // (result.Action == RewriteAction.None) if and only if (node == result.Node) Debug.Assert((result.Action == RewriteAction.None) ^ (node != result.Node), "rewrite action does not match node object identity"); // if the original node is an extension node, it should have been rewritten Debug.Assert(result.Node.NodeType != ExpressionType.Extension, "extension nodes must be rewritten"); // if we have Copy, then node type must match Debug.Assert( result.Action != RewriteAction.Copy || node.NodeType == result.Node.NodeType || node.CanReduce, "rewrite action does not match node object kind" ); // New type must be reference assignable to the old type // (our rewrites preserve type exactly, but the rules for rewriting // an extension node are more lenient, see Expression.ReduceAndCheck()) Debug.Assert( TypeHelper.AreReferenceAssignable(node.Type, result.Node.Type), "rewritten object must be reference assignable to the original type" ); }
// See if this lambda has a return label // If so, we'll create it now and mark it as allowing the "ret" opcode // This allows us to generate better IL private void AddReturnLabel(LambdaExpression lambda) { var expression = lambda.Body; while (true) { switch (expression.NodeType) { default: // Didn't find return label return; case ExpressionType.Label: // Found the label. We can directly return from this place // only if the label type is reference assignable to the lambda return type. var label = ((LabelExpression)expression).Target; _labelInfo.Add(label, new LabelInfo(_ilg, label, TypeHelper.AreReferenceAssignable(lambda.ReturnType, label.Type))); return; case ExpressionType.Block: // Look in the last significant expression of a block var body = (BlockExpression)expression; // omit empty and debuginfo at the end of the block since they // are not going to emit any IL for (int i = body.ExpressionCount - 1; i >= 0; i--) { expression = body.GetExpression(i); if (Significant(expression)) { break; } } continue; } } }
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection <Expression> arguments, ref ReadOnlyCollection <MemberInfo> members) { ParameterInfo[] pis; if ((pis = constructor.GetParameters()).Length > 0) { if (arguments.Count != pis.Length) { throw Error.IncorrectNumberOfConstructorArguments(); } if (arguments.Count != members.Count) { throw Error.IncorrectNumberOfArgumentsForMembers(); } Expression[] newArguments = null; MemberInfo[] newMembers = null; var n = arguments.Count; for (var i = 0; i < n; i++) { var arg = arguments[i]; RequiresCanRead(arg, "argument"); var member = members[i]; ContractUtils.RequiresNotNull(member, "member"); if (member.DeclaringType != constructor.DeclaringType) { throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType.Name); } Type memberType; ValidateAnonymousTypeMember(ref member, out memberType); if (!TypeHelper.AreReferenceAssignable(memberType, arg.Type)) { if (!TryQuote(memberType, ref arg)) { throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType); } } var pi = pis[i]; var pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } if (!TypeHelper.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); } } if (newArguments == null && arg != arguments[i]) { newArguments = new Expression[n]; for (var j = 0; j < i; j++) { newArguments[j] = arguments[j]; } } if (newArguments != null) { newArguments[i] = arg; } if (newMembers == null && member != members[i]) { newMembers = new MemberInfo[members.Count]; for (var j = 0; j < i; j++) { newMembers[j] = members[j]; } } if (newMembers != null) { newMembers[i] = member; } } if (newArguments != null) { arguments = new TrueReadOnlyCollection <Expression>(newArguments); } if (newMembers != null) { members = new TrueReadOnlyCollection <MemberInfo>(newMembers); } } else if (arguments != null && arguments.Count > 0) { throw Error.IncorrectNumberOfConstructorArguments(); } else if (members != null && members.Count > 0) { throw Error.IncorrectNumberOfMembersForGivenConstructor(); } }