/// <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)); }
/// <summary> /// Creates a <see cref="ConstantExpression"/> that has the <see cref="ConstantExpression.Value"/> /// and <see cref="ConstantExpression.Type"/> properties set to the specified values. . /// </summary> /// <param name="value">An <see cref="object"/> to set the <see cref="ConstantExpression.Value"/> property equal to.</param> /// <param name="type">A <see cref="Type"/> to set the <see cref="Type"/> property equal to.</param> /// <returns> /// A <see cref="ConstantExpression"/> that has the <see cref="NodeType"/> property equal to /// <see cref="ExpressionType.Constant"/> and the <see cref="ConstantExpression.Value"/> and /// <see cref="Type"/> properties set to the specified values. /// </returns> public static ConstantExpression Constant(object?value, Type type) { ContractUtils.RequiresNotNull(type, nameof(type)); TypeUtils.ValidateType(type, nameof(type)); if (value == null) { if (type == typeof(object)) { return(new ConstantExpression(null)); } if (!type.IsValueType || type.IsNullableType()) { return(new TypedConstantExpression(null, type)); } } else { Type valueType = value.GetType(); if (type == valueType) { return(new ConstantExpression(value)); } if (type.IsAssignableFrom(valueType)) { return(new TypedConstantExpression(value, type)); } } throw Error.ArgumentTypesMustMatch(); }
private static BlockExpression BlockCore(Type type, ReadOnlyCollection <ParameterExpression> variableList, ReadOnlyCollection <Expression> expressionList) { ContractUtils.RequiresNotEmpty(expressionList, "expressions"); RequiresCanRead(expressionList, "expressions"); ValidateVariables(variableList, "variables"); Expression last = expressionList.Last(); if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { throw Error.ArgumentTypesMustMatch(); } } if (!TypeUtils.AreEquivalent(type, last.Type)) { return(new ScopeWithType(variableList, expressionList, type)); } else { if (expressionList.Count == 1) { return(new Scope1(variableList, expressionList[0])); } else { return(new ScopeN(variableList, expressionList)); } } }
/// <summary> /// Creates a <see cref="ConstantExpression"/> that has the <see cref="P:ConstantExpression.Value"/> /// and <see cref="P:ConstantExpression.Type"/> properties set to the specified values. . /// </summary> /// <param name="value">An <see cref="System.Object"/> to set the <see cref="P:ConstantExpression.Value"/> property equal to.</param> /// <param name="type">A <see cref="System.Type"/> to set the <see cref="P:Expression.Type"/> property equal to.</param> /// <returns> /// A <see cref="ConstantExpression"/> that has the <see cref="P:Expression.NodeType"/> property equal to /// <see cref="F:ExpressionType.Constant"/> and the <see cref="P:ConstantExpression.Value"/> and /// <see cref="P:Expression.Type"/> properties set to the specified values. /// </returns> public static ConstantExpression Constant(object value, Type type) { ContractUtils.RequiresNotNull(type, nameof(type)); if (value == null ? type.GetTypeInfo().IsValueType&& !TypeUtils.IsNullableType(type) : !type.IsAssignableFrom(value.GetType())) { throw Error.ArgumentTypesMustMatch(); } return(ConstantExpression.Make(value, type)); }
/// <summary> /// Creates a <see cref="MemberAssignment"/> binding the specified value to the given member. /// </summary> /// <param name="member">The <see cref="MemberInfo"/> for the member which is being assigned to.</param> /// <param name="expression">The value to be assigned to <paramref name="member"/>.</param> /// <returns>The created <see cref="MemberAssignment"/>.</returns> public static MemberAssignment Bind(MemberInfo member, Expression expression) { ContractUtils.RequiresNotNull(member, nameof(member)); ExpressionUtils.RequiresCanRead(expression, nameof(expression)); ValidateSettableFieldOrPropertyMember(member, out var memberType); if (!memberType.IsAssignableFrom(expression.Type)) { throw Error.ArgumentTypesMustMatch(); } return(new MemberAssignment(member, expression)); }
//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) { Debug.Assert(tryBody != null); // Type unification ... all parts must be reference assignable to "type" if (type != null) { if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, tryBody.Type)) { throw Error.ArgumentTypesMustMatch(); } foreach (CatchBlock cb in handlers) { if (!TypeUtils.AreReferenceAssignable(type, cb.Body.Type)) { throw Error.ArgumentTypesMustMatch(); } } } } else if (tryBody.Type == typeof(void)) { //The body of every try block must be null or have void type. foreach (CatchBlock cb in handlers) { Debug.Assert(cb.Body != null); if (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 (CatchBlock cb in handlers) { Debug.Assert(cb.Body != null); if (!TypeUtils.AreEquivalent(cb.Body.Type, type)) { throw Error.BodyOfCatchMustHaveSameTypeAsBodyOfTry(); } } } }
/// <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> /// <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> public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse) { RequiresCanRead(test, "test"); RequiresCanRead(ifTrue, "ifTrue"); RequiresCanRead(ifFalse, "ifFalse"); if (test.Type != typeof(bool)) { throw Error.ArgumentMustBeBoolean(); } if (!TypeUtils.AreEquivalent(ifTrue.Type, ifFalse.Type)) { throw Error.ArgumentTypesMustMatch(); } return(ConditionalExpression.Make(test, ifTrue, ifFalse, ifTrue.Type)); }
private static BlockExpression BlockCore(Type type, ReadOnlyCollection <ParameterExpression> variableList, ReadOnlyCollection <Expression> expressionList) { RequiresCanRead(expressionList, "expressions"); ValidateVariables(variableList, "variables"); if (type != null) { if (expressionList.Count == 0) { if (type != typeof(void)) { throw Error.ArgumentTypesMustMatch(); } return(new ScopeWithType(variableList, expressionList, type)); } Expression last = expressionList.Last(); if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, last.Type)) { throw Error.ArgumentTypesMustMatch(); } } if (!TypeUtils.AreEquivalent(type, last.Type)) { return(new ScopeWithType(variableList, expressionList, type)); } } switch (expressionList.Count) { case 0: return(new ScopeWithType(variableList, expressionList, typeof(void))); case 1: return(new Scope1(variableList, expressionList[0])); default: return(new ScopeN(variableList, expressionList)); } }
/// <summary> /// If custom type is provided, all branches must be reference assignable to the result type. /// If no custom type is provided, all branches must have the same type - resultType. /// </summary> private static void ValidateSwitchCaseType(Expression @case, bool customType, Type resultType, string parameterName) { if (customType) { if (resultType != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(resultType, @case.Type)) { throw Error.ArgumentTypesMustMatch(parameterName); } } } else { if (!TypeUtils.AreEquivalent(resultType, @case.Type)) { throw Error.AllCaseBodiesMustHaveSameType(parameterName); } } }
/// <summary> /// If custom type is provided, all branches must be reference assignable to the result type. /// If no custom type is provided, all branches must have the same type - resultType. /// </summary> private static void ValidateSwitchCaseType(Expression @case, bool customType, Type resultType, string parameterName) { if (customType) { if (resultType != typeof(void)) { if (!resultType.IsReferenceAssignableFromInternal(@case.Type)) { throw Error.ArgumentTypesMustMatch(parameterName); } } } else { if (resultType != @case.Type) { throw Error.AllCaseBodiesMustHaveSameType(parameterName); } } }
/// <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 (!TypeUtils.AreReferenceAssignable(type, ifTrue.Type) || !TypeUtils.AreReferenceAssignable(type, ifFalse.Type)) { throw Error.ArgumentTypesMustMatch(); } } return(ConditionalExpression.Make(test, ifTrue, ifFalse, type)); }
/// <summary> /// Creates a <see cref="ConditionalExpression"/>. /// </summary> /// <param name="test">An <see cref="Expression"/> to set the <see cref="ConditionalExpression.Test"/> property equal to.</param> /// <param name="ifTrue">An <see cref="Expression"/> to set the <see cref="ConditionalExpression.IfTrue"/> property equal to.</param> /// <param name="ifFalse">An <see cref="Expression"/> to set the <see cref="ConditionalExpression.IfFalse"/> property equal to.</param> /// <param name="type">A <see cref="Type"/> to set the <see cref="Type"/> property equal to.</param> /// <returns>A <see cref="ConditionalExpression"/> that has the <see cref="NodeType"/> property equal to /// <see cref="ExpressionType.Conditional"/> and the <see cref="ConditionalExpression.Test"/>, <see cref="ConditionalExpression.IfTrue"/>, /// and <see cref="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="Void"/>.</remarks> public static ConditionalExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type type) { ExpressionUtils.RequiresCanRead(test, nameof(test)); ExpressionUtils.RequiresCanRead(ifTrue, nameof(ifTrue)); ExpressionUtils.RequiresCanRead(ifFalse, nameof(ifFalse)); ArgumentNullException.ThrowIfNull(type); if (test.Type != typeof(bool)) { throw Error.ArgumentMustBeBoolean(nameof(test)); } if (type != typeof(void)) { if (!TypeUtils.AreReferenceAssignable(type, ifTrue.Type) || !TypeUtils.AreReferenceAssignable(type, ifFalse.Type)) { throw Error.ArgumentTypesMustMatch(); } } return(ConditionalExpression.Make(test, ifTrue, ifFalse, type)); }
/// <summary> /// Creates a <see cref="MemberInitializer"/> binding the specified value to the given member. /// </summary> /// <param name="member">The <see cref="MemberInfo"/> for the member which is being assigned to.</param> /// <param name="expression">The value to be assigned to <paramref name="member"/>.</param> /// <returns>The created <see cref="MemberInitializer"/>.</returns> public static MemberInitializer MemberInitializer(MemberInfo member, Expression expression) { RequiresNotNull(member, nameof(member)); // // NB: System.Linq.Expressions fails to checks for static members in ValidateSettableFieldOrPropertyMember. // // class Foo { public static int Bar { get; set; } } // // Expression.Lambda<Func<Foo>>( // Expression.MemberInit( // Expression.New(typeof(Foo)), // Expression.Bind(typeof(Foo).GetProperty("Bar"), Expression.Constant(1)) // ) // ).Compile() // // throws 'System.Security.VerificationException: Operation could destabilize the runtime.' // // We add this check here. // Type memberType; switch (member) { case FieldInfo f: memberType = f.FieldType; if (f.IsStatic) { throw Error.MemberInitializerMemberMustNotBeStatic(member.Name); } break; case PropertyInfo p: memberType = p.PropertyType; var accessor = p.GetGetMethod(nonPublic: true) ?? p.GetSetMethod(nonPublic: true); if (accessor == null) { throw LinqError.PropertyDoesNotHaveAccessor(p); } if (accessor.IsStatic) { throw Error.MemberInitializerMemberMustNotBeStatic(member.Name); } if (p.GetIndexParameters().Length > 0) { throw Error.MemberInitializerMemberMustNotBeIndexer(member.Name); } break; default: throw LinqError.ArgumentMustBeFieldInfoOrPropertInfo(); } RequiresCanRead(expression, nameof(expression)); // // NB: We don't check here for an accessible setter in order to support anonymous types where // the getter is specified instead. // if (!memberType.IsAssignableFrom(expression.Type)) { throw LinqError.ArgumentTypesMustMatch(); } if (member.DeclaringType == null) { throw Error.NotAMemberOfAnyType(member); } ValidateType(member.DeclaringType); return(new MemberInitializer(member, expression)); }