private static void ValidateIndexer(Type instanceType, PropertyInfo indexer) { // NB: We rely on the LINQ API to do validation of the indexer, including the setter (if any). We // have to validate the setter as well because this node could reduce into an assignment when // used in combination with C# assignment expressions. Note that the LINQ API won't treat our // node as assignable when used with LINQ assignment expressions. Our reduction of assignment // will properly call the setter, thus we have to make sure things are well-typed. var parameters = indexer.GetIndexParameters(); var n = parameters.Length; var args = new Expression[n]; for (var i = 0; i < n; i++) { // NB: This is just a trick to be compatible with the signature of ValidateIndexeProperty but // we could change the LINQ APIs to have a variant that just takes in types. Shouldn't be // a big deal though, given that most indexers only have a few parameters. args[i] = Expression.Default(parameters[i].ParameterType); } var original = new TrueReadOnlyCollection <Expression>(args); var argList = (ReadOnlyCollection <Expression>)original; ValidateIndexedProperty(Expression.Default(instanceType), indexer, ref argList); // NB: We don't expect mutations because all expressions match the corresponding indexer parameter // type. As such, we shouldn't end up quoting any argument. Debug.Assert(argList == original); }
internal NewExpression(ConstructorInfo constructor, Expression[] arguments, ReadOnlyCollection <MemberInfo> members) { Constructor = constructor; _arguments = arguments; Members = members; _argumentsAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(_arguments); }
private static MethodCallCSharpExpression MakeAccess(ConditionalReceiver receiver, MethodInfo method, ReadOnlyCollection <ParameterAssignment> arguments) { if (method.IsStatic && method.IsDefined(typeof(ExtensionAttribute))) { var thisPar = method.GetParametersCached()[0]; var thisArg = CSharpExpression.Bind(thisPar, receiver); var newArgs = new ParameterAssignment[arguments.Count + 1]; newArgs[0] = thisArg; var i = 1; foreach (var arg in arguments) { newArgs[i++] = arg; } var newArguments = new TrueReadOnlyCollection <ParameterAssignment>(newArgs); return(CSharpExpression.Call(null, method, newArguments)); // TODO: call ctor directly } else { return(CSharpExpression.Call(receiver, method, arguments)); // TODO: call ctor directly } }
internal static ReadOnlyCollection <T> ToReadOnly <T>(this IEnumerable <T> enumerable) { if (enumerable == null) { return(EmptyReadOnlyCollection <T> .Instance); } TrueReadOnlyCollection <T> onlys = enumerable as TrueReadOnlyCollection <T>; if (onlys != null) { return(onlys); } ReadOnlyCollectionBuilder <T> builder = enumerable as ReadOnlyCollectionBuilder <T>; if (builder != null) { return(builder.ToReadOnlyCollection()); } ICollection <T> is2 = enumerable as ICollection <T>; if (is2 == null) { return(new TrueReadOnlyCollection <T>(new List <T>(enumerable).ToArray())); } int count = is2.Count; if (count == 0) { return(EmptyReadOnlyCollection <T> .Instance); } T[] array = new T[count]; is2.CopyTo(array, 0); return(new TrueReadOnlyCollection <T>(array)); }
public static void ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection <Expression> arguments) { Debug.Assert(nodeKind == ExpressionType.Invoke || nodeKind == ExpressionType.Call || nodeKind == ExpressionType.Dynamic || nodeKind == ExpressionType.New); ParameterInfo[] pis = GetParametersForValidation(method, nodeKind); ValidateArgumentCount(method, nodeKind, arguments.Count, pis); Expression[] newArgs = null; for (int i = 0, n = pis.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = pis[i]; arg = ValidateOneArgument(method, nodeKind, arg, pi); if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int j = 0; j < i; j++) { newArgs[j] = arguments[j]; } } if (newArgs != null) { newArgs[i] = arg; } } if (newArgs != null) { arguments = new TrueReadOnlyCollection <Expression>(newArgs); } }
internal BlockN(Expression[] expressions) { Debug.Assert(expressions.Length != 0); _expressions = expressions; _expressionsAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(_expressions); }
public override TypeSlim ToType(DeserializationDomain domain, TypeSlim[] genericArguments) { if (_type == null) { var def = domain.GetType(_genericTypeDefinition).ToType(domain, genericArguments); GenericDefinitionTypeSlim genDef; switch (def.Kind) { case TypeSlimKind.Simple: var simple = (SimpleTypeSlim)def; genDef = TypeSlim.GenericDefinition(simple.Assembly, simple.Name); break; default: throw new InvalidOperationException("Expected either simple type slim discriminator for generic definition type."); } var n = _genericTypeArguments.Length; var argsList = new TypeSlim[n]; for (var i = 0; i < n; i++) { var arg = _genericTypeArguments[i]; var argType = domain.GetType(arg).ToType(domain, genericArguments); argsList[i] = argType; } var args = new TrueReadOnlyCollection <TypeSlim>(/* transfer ownership */ argsList); _type = TypeSlim.Generic(genDef, args); } return(_type); }
internal MemberMemberBinding(MemberInfo member, MemberBinding[] bindings) #pragma warning disable 618 : base(MemberBindingType.MemberBinding, member) { #pragma warning restore 618 _bindings = bindings; _bindingsAsReadOnlyCollection = new TrueReadOnlyCollection <MemberBinding>(_bindings); }
internal TryExpression(Type type, Expression body, Expression @finally, Expression fault, CatchBlock[] handlers) { Type = type; Body = body; _handlers = handlers; Finally = @finally; Fault = fault; _handlersAsReadOnlyCollection = new TrueReadOnlyCollection <CatchBlock>(_handlers); }
internal SwitchExpression(Type type, Expression switchValue, Expression defaultBody, MethodInfo comparison, SwitchCase[] cases) { Type = type; SwitchValue = switchValue; DefaultBody = defaultBody; Comparison = comparison; _cases = cases; _casesAsReadOnlyCollection = new TrueReadOnlyCollection <SwitchCase>(_cases); }
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; for (int i = 0, n = indexes.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = indexes[i]; RequiresCanRead(arg, "arguments"); Type pType = pi.ParameterType; if (pType.IsByRef) { throw Error.AccessorsCannotHaveByRefArgs(); } TypeUtils.ValidateType(pType); if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), pType) && pType.IsAssignableFrom(arg.GetType())) { arg = Expression.Quote(arg); } else { throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); } } if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int 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); } }
/// <summary> /// Creates a <see cref="NewArrayExpression"/> 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>A <see cref="NewArrayExpression"/> that has the <see cref="NodeType"/> property equal to <see cref="ExpressionType.NewArrayInit"/> and the <see cref="NewArrayExpression.Expressions"/> property set to the specified value.</returns> public static NewArrayExpression NewArrayInit(Type type, IEnumerable <Expression> initializers) { ContractUtils.RequiresNotNull(type, nameof(type)); ContractUtils.RequiresNotNull(initializers, nameof(initializers)); if (type == typeof(void)) { throw Error.ArgumentCannotBeOfTypeVoid(nameof(type)); } TypeUtils.ValidateType(type, nameof(type)); if (type.IsByRef) { throw Error.TypeMustNotBeByRef(nameof(type)); } if (type.IsPointer) { throw Error.TypeMustNotBePointer(nameof(type)); } ReadOnlyCollection <Expression> initializerList = initializers.ToReadOnly(); Expression[] newList = null; for (int i = 0, n = initializerList.Count; i < n; i++) { Expression expr = initializerList[i]; RequiresCanRead(expr, nameof(initializers), i); if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)) { throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int 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 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); } }
/// <summary> /// Reduces the expression node to a simpler expression. /// </summary> /// <returns>The reduced expression.</returns> public override Expression Reduce() { var statements = default(ReadOnlyCollection <Expression>); if (ReturnLabel == null) { if (Statements.Count == 0) { // NB: Can ignore variables; there's no expression that can use them anyway, and they don't have side-effects. return(Expression.Empty()); } else { statements = Statements; } } else { var returnLabel = default(LabelExpression); if (ReturnLabel.Type != typeof(void)) { returnLabel = Expression.Label(ReturnLabel, Expression.Default(ReturnLabel.Type)); } else { returnLabel = Expression.Label(ReturnLabel); } if (Statements.Count == 0) { // NB: Can ignore variables; there's no expression that can use them anyway, and they don't have side-effects. return(returnLabel); } else { statements = new TrueReadOnlyCollection <Expression>(Statements.AddLast(returnLabel)); } } if (Variables.Count == 0) { return(Expression.Block(Type, statements)); } else { // REVIEW: Should we ensure all variables get assigned default values? Cf. when it's used // in a loop and the locals don't get reinitialized. Or should we assume there's // definite assignment (or enforce it)? return(Expression.Block(Type, Variables, statements)); } }
private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection <Expression> arguments, string?paramName) { if (indexes.Length > 0) { if (indexes.Length != arguments.Count) { throw Error.IncorrectNumberOfMethodCallArguments(method, paramName); } Expression[]? newArgs = null; for (int i = 0, n = indexes.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = indexes[i]; ExpressionUtils.RequiresCanRead(arg, nameof(arguments), i); Type pType = pi.ParameterType; if (pType.IsByRef) { throw Error.AccessorsCannotHaveByRefArgs(nameof(indexes), i); } TypeUtils.ValidateType(pType, nameof(indexes), i); if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method, nameof(arguments), i); } } if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int 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, paramName); } }
/// <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.Equals(typeof(void))) { throw Error.ArgumentCannotBeOfTypeVoid(); } ReadOnlyCollection <Expression> initializerList = initializers.ToReadOnly(); Expression[] newList = null; for (int i = 0, n = initializerList.Count; i < n; i++) { Expression expr = initializerList[i]; RequiresCanRead(expr, "initializers"); if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), type) && type.IsAssignableFrom(expr.GetType())) { expr = Expression.Quote(expr); } else { throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int 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)); }
internal IndexExpression( Expression instance, PropertyInfo indexer, Expression[] arguments) { if (indexer == null) { Debug.Assert(instance != null && instance.Type.IsArray); Debug.Assert(instance.Type.GetArrayRank() == arguments.Length); } Object = instance; Indexer = indexer; _arguments = arguments; _argumentsAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(_arguments); }
internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection<ParameterExpression> vars) { if (parent != null) { vars = new TrueReadOnlyCollection<ParameterExpression>(vars.AddFirst<ParameterExpression>(parent.SelfVariable)); } Dictionary<Expression, int> dict = new Dictionary<Expression, int>(vars.Count); for (int i = 0; i < vars.Count; i++) { dict.Add(vars[i], i); } this.SelfVariable = Expression.Variable(typeof(object[]), null); this.Parent = parent; this.Variables = vars; this.Indexes = new ReadOnlyDictionary<Expression, int>(dict); }
/// <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)); }
internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection<ParameterExpression> vars) { if (parent != null) { // Add the parent locals array as the 0th element in the array vars = new TrueReadOnlyCollection<ParameterExpression>(vars.AddFirst(parent.SelfVariable)); } Dictionary<Expression, int> indexes = new Dictionary<Expression, int>(vars.Count); for (int i = 0; i < vars.Count; i++) { indexes.Add(vars[i], i); } SelfVariable = Expression.Variable(typeof(object[]), null); Parent = parent; Variables = vars; Indexes = new ReadOnlyDictionary<Expression, int>(indexes); }
internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection <ParameterExpression> vars) { if (parent != null) { vars = new TrueReadOnlyCollection <ParameterExpression>(vars.AddFirst <ParameterExpression>(parent.SelfVariable)); } Dictionary <Expression, int> dict = new Dictionary <Expression, int>(vars.Count); for (int i = 0; i < vars.Count; i++) { dict.Add(vars[i], i); } this.SelfVariable = Expression.Variable(typeof(object[]), null); this.Parent = parent; this.Variables = vars; this.Indexes = new ReadOnlyDictionary <Expression, int>(dict); }
public override MemberInfoSlim ToMember(DeserializationDomain domain) { // PERF: This is a heavy allocator; should we support caching the result? var def = ((GenericDefinitionMethodInfoSlim)domain.GetMember(_genericMethodDefinition)); var count = _genericArguments.Length; var argsList = new TypeSlim[count]; for (var i = 0; i < count; i++) { argsList[i] = domain.GetType(_genericArguments[i]).ToType(domain); } var args = new TrueReadOnlyCollection <TypeSlim>(/* transfer ownership */ argsList); return(def.DeclaringType.GetGenericMethod(def, args)); }
internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection <ParameterExpression> vars) { if (parent != null) { // Add the parent locals array as the 0th element in the array vars = new TrueReadOnlyCollection <ParameterExpression>(vars.AddFirst(parent.SelfVariable)); } Dictionary <Expression, int> indexes = new Dictionary <Expression, int>(vars.Count); for (int i = 0; i < vars.Count; i++) { indexes.Add(vars[i], i); } SelfVariable = Expression.Variable(typeof(object[]), null); Parent = parent; Variables = vars; Indexes = new ReadOnlyDictionary <Expression, int>(indexes); }
public void EnsureLonelyDefault() { if (DefaultCase != null && !IsDefaultLonely) { // We have a default case but it's mingled up with other cases, e.g. // // switch (x) // { // case 1: // case 2: // default: // ... // break; // } // // We can simply drop the other test values for compilation purposes, e.g. // // switch (x) // { // default: // ... // break; // } var roDefaultTestValues = new TrueReadOnlyCollection <object>(new[] { SwitchCaseDefaultValue }); var newDefaultCase = new CSharpSwitchCase(roDefaultTestValues, DefaultCase.Statements); if (DefaultCase == NullCase) { NullCase = null; IsNullLonely = false; } DefaultCase = newDefaultCase; IsDefaultLonely = true; } }
public InvocationExpressionN(Expression lambda, Expression[] arguments, Type returnType) : base(lambda, returnType) { _arguments = arguments; _argumentsAsReadOnly = new TrueReadOnlyCollection <Expression>(_arguments); }
internal ScopeExpression(ParameterExpression[] variables) { _variables = variables; _variablesAsReadOnlyCollection = new TrueReadOnlyCollection <ParameterExpression>(_variables); }
internal ScopeN(ParameterExpression[] variables, Expression[] body) : base(variables) { _body = body; _bodyAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(_body); }
internal ElementInit(MethodInfo addMethod, Expression[] arguments) { AddMethod = addMethod; _argumentsAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(arguments); }
/// <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.Equals(typeof(void))) { throw Error.ArgumentCannotBeOfTypeVoid(); } ReadOnlyCollection<Expression> initializerList = initializers.ToReadOnly(); Expression[] newList = null; for (int i = 0, n = initializerList.Count; i < n; i++) { Expression expr = initializerList[i]; RequiresCanRead(expr, "initializers"); if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)){ throw Error.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int 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 void ValidateIndexer(Type instanceType, PropertyInfo indexer) { // NB: We rely on the LINQ API to do validation of the indexer, including the setter (if any). We // have to validate the setter as well because this node could reduce into an assignment when // used in combination with C# assignment expressions. Note that the LINQ API won't treat our // node as assignable when used with LINQ assignment expressions. Our reduction of assignment // will properly call the setter, thus we have to make sure things are well-typed. var parameters = indexer.GetIndexParameters(); var n = parameters.Length; var args = new Expression[n]; for (var i = 0; i < n; i++) { // NB: This is just a trick to be compatible with the signature of ValidateIndexeProperty but // we could change the LINQ APIs to have a variant that just takes in types. Shouldn't be // a big deal though, given that most indexers only have a few parameters. args[i] = Expression.Default(parameters[i].ParameterType); } var original = new TrueReadOnlyCollection<Expression>(args); var argList = (ReadOnlyCollection<Expression>)original; ValidateIndexedProperty(Expression.Default(instanceType), indexer, ref argList); // NB: We don't expect mutations because all expressions match the corresponding indexer parameter // type. As such, we shouldn't end up quoting any argument. Debug.Assert(argList == original); }
public void EnsureLonelyDefault() { if (DefaultCase != null && !IsDefaultLonely) { // We have a default case but it's mingled up with other cases, e.g. // // switch (x) // { // case 1: // case 2: // case default: // ... // break; // } // // We can simply drop the other test values for compilation purposes, e.g. // // switch (x) // { // case default: // ... // break; // } var roDefaultTestValues = new TrueReadOnlyCollection<object>(new[] { SwitchCaseDefaultValue }); var newDefaultCase = new CSharpSwitchCase(roDefaultTestValues, DefaultCase.Statements); if (DefaultCase == NullCase) { NullCase = null; IsNullLonely = false; } DefaultCase = newDefaultCase; IsDefaultLonely = true; } }
/// <summary> /// Reduces the expression node to a simpler expression. /// </summary> /// <returns>The reduced expression.</returns> public override Expression Reduce() { // NB: Unlike the C# compiler, we don't optimize for the case where all elements are constants of a // blittable type. In such a case, the C# compiler will emit a field with the binary representation // of the whole array and emit a call to RuntimeHelpers.InitializeArray. To achieve this, we'd need // to have access to a ModuleBuilder, also tying the reduction path to the compiler and requiring // separate treatment for the interpreter. // // This optimization would matter most if many copies of the array are initialized or the array is // really big, and it only contains constants. In the case of a big array, we could argue that the // current expression API has many ineffiencies already, e.g. when emitting closures including lots // of captured variables, which does not create a display class but an array of StrongBox<T> objects. // // An alternative, which could be useful if the expression is evaluated many times (creating many // copies), would be to prepare an instance of the array, cache it, and use Array.Clone to create // a copy each time the expression is evaluated. Effectively, the reduced expression would become a // Constant node containing the fully materialized array. This would only be worth the effort if the // cost of creating a clone is sufficiently less compared to element-by-element initialization which // is likely true given it does a memberwise clone underneath. One drawback is that the expression // would root the "prototype" of the array, but this is comparable to the module image containing a // blob containing the array elements from which copies are created through InitializeArray (unless // it employs a copy-on-write approach, haven't checked). // // Note that the optimization sketched above could also be applied to ArrayInit nodes in LINQ, by // changing the LambdaCompiler (see ILGen.EmitArray which generates a sequence of stelems). It // doesn't seem like it was considered there, so maybe we can get away without it here as well. // // Finally, not that the reduction approach below is likely more expensive than EmitArray used by // the LambdaCompiler which can use dup instructions where we'll have a ldloc instruction for each // element being initialized. // // A quick experiment with an optimizer for NewArrayInit nodes with only constants yields the // following result (see Experiment.cs, method ArrayInitOptimization, in Playground): // // [RAW] new int[10] x 100000 = 2ms // [OPT] new int[10] x 100000 = 10ms // [RAW] new int[100] x 100000 = 6ms // [OPT] new int[100] x 100000 = 15ms // [RAW] new int[400] x 100000 = 28ms << // [OPT] new int[400] x 100000 = 23ms << // [RAW] new int[500] x 100000 = 33ms // [OPT] new int[500] x 100000 = 28ms // [RAW] new int[1000] x 100000 = 69ms // [OPT] new int[1000] x 100000 = 43ms // // With 100K iterations of evaluating the expression, it takes an array of 400+ elements for the // optimization to pay off. If we run into the need to optimize this, we should likely take such // a measurement into account and only apply the optimization when the expression is contained in // some loop construct and the element count is sufficiently large. var n = Expressions.Count; var rank = Bounds.Count; var res = Expression.Parameter(Type, "__array"); var exprs = new Expression[n + 2]; // NB: We need the bounds to NewArrayBounds and all values from 0 to each bound for ArrayAccess. var consts = Enumerable.Range(0, Bounds.Max() + 1).Select(CreateConstantInt32).ToArray(); exprs[0] = Expression.Assign(res, Expression.NewArrayBounds(Type.GetElementType(), Bounds.Map(i => consts[i]))); var indexValues = new int[rank]; for (var i = 1; i <= n; i++) { var idx = i - 1; var value = Expressions[idx]; for (var j = rank - 1; j >= 0; j--) { var bound = Bounds[j]; indexValues[j] = idx % bound; idx /= bound; } var indexes = new TrueReadOnlyCollection <Expression>(indexValues.Map(j => consts[j])); var element = Expression.ArrayAccess(res, indexes); exprs[i] = Expression.Assign(element, value); } exprs[n + 1] = res; return(Expression.Block(new[] { res }, exprs)); }
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection<Expression> arguments, ref ReadOnlyCollection<MemberInfo> members) { ParameterInfo[] pis; if ((pis = constructor.GetParametersCached()).Length > 0) { if (arguments.Count != pis.Length) { throw Error.IncorrectNumberOfConstructorArguments(); } if (arguments.Count != members.Count) { throw Error.IncorrectNumberOfArgumentsForMembers(); } Expression[] newArguments = null; MemberInfo[] newMembers = null; for (int i = 0, n = arguments.Count; i < n; i++) { Expression arg = arguments[i]; RequiresCanRead(arg, "argument"); MemberInfo member = members[i]; ContractUtils.RequiresNotNull(member, nameof(member)); if (!TypeUtils.AreEquivalent(member.DeclaringType, constructor.DeclaringType)) { throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType.Name); } Type memberType; ValidateAnonymousTypeMember(ref member, out memberType); if (!TypeUtils.AreReferenceAssignable(memberType, arg.Type)) { if (!TryQuote(memberType, ref arg)) { throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType); } } ParameterInfo pi = pis[i]; Type pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType(); } if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType); } } if (newArguments == null && arg != arguments[i]) { newArguments = new Expression[arguments.Count]; for (int 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 (int 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(); } }
public static NewMultidimensionalArrayInitCSharpExpression NewMultidimensionalArrayInit(Type type, IEnumerable <int> bounds, IEnumerable <Expression> initializers) { ContractUtils.RequiresNotNull(type, nameof(type)); ContractUtils.RequiresNotNull(bounds, nameof(bounds)); ContractUtils.RequiresNotNull(initializers, nameof(initializers)); if (type.Equals(typeof(void))) { throw LinqError.ArgumentCannotBeOfTypeVoid(); } var boundsList = bounds.ToReadOnly(); int dimensions = boundsList.Count; if (dimensions <= 0) { throw LinqError.BoundsCannotBeLessThanOne(); } var length = 1; foreach (var bound in boundsList) { if (bound < 0) { throw Error.BoundCannotBeLessThanZero(); } checked { length *= bound; } } var initializerList = initializers.ToReadOnly(); if (initializerList.Count != length) { throw Error.ArrayBoundsElementCountMismatch(); } var newList = default(Expression[]); for (int i = 0, n = initializerList.Count; i < n; i++) { var expr = initializerList[i]; RequiresCanRead(expr, nameof(initializers)); if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)) { throw LinqError.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int j = 0; j < i; j++) { newList[j] = initializerList[j]; } } } if (newList != null) { newList[i] = expr; } } if (newList != null) { initializerList = new TrueReadOnlyCollection <Expression>(newList); } return(new NewMultidimensionalArrayInitCSharpExpression(type.MakeArrayType(boundsList.Count), boundsList, initializerList)); }
public static NewMultidimensionalArrayInitCSharpExpression NewMultidimensionalArrayInit(Type type, IEnumerable<int> bounds, IEnumerable<Expression> initializers) { ContractUtils.RequiresNotNull(type, nameof(type)); ContractUtils.RequiresNotNull(bounds, nameof(bounds)); ContractUtils.RequiresNotNull(initializers, nameof(initializers)); if (type.Equals(typeof(void))) { throw LinqError.ArgumentCannotBeOfTypeVoid(); } var boundsList = bounds.ToReadOnly(); int dimensions = boundsList.Count; if (dimensions <= 0) { throw LinqError.BoundsCannotBeLessThanOne(); } var length = 1; foreach (var bound in boundsList) { if (bound < 0) { throw Error.BoundCannotBeLessThanZero(); } checked { length *= bound; } } var initializerList = initializers.ToReadOnly(); if (initializerList.Count != length) { throw Error.ArrayBoundsElementCountMismatch(); } var newList = default(Expression[]); for (int i = 0, n = initializerList.Count; i < n; i++) { var expr = initializerList[i]; RequiresCanRead(expr, nameof(initializers)); if (!TypeUtils.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)) { throw LinqError.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int j = 0; j < i; j++) { newList[j] = initializerList[j]; } } } if (newList != null) { newList[i] = expr; } } if (newList != null) { initializerList = new TrueReadOnlyCollection<Expression>(newList); } return new NewMultidimensionalArrayInitCSharpExpression(type.MakeArrayType(boundsList.Count), boundsList, initializerList); }
internal NewArrayExpression(Type type, Expression[] expressions) { _expressions = expressions; Type = type; _expressionsAsReadOnlyCollection = new TrueReadOnlyCollection <Expression>(_expressions); }
private static void ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ref ReadOnlyCollection<Expression> arguments) { Debug.Assert(nodeKind == ExpressionType.Invoke || nodeKind == ExpressionType.Call || nodeKind == ExpressionType.Dynamic || nodeKind == ExpressionType.New); ParameterInfo[] pis = GetParametersForValidation(method, nodeKind); ValidateArgumentCount(method, nodeKind, arguments.Count, pis); Expression[] newArgs = null; for (int i = 0, n = pis.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = pis[i]; arg = ValidateOneArgument(method, nodeKind, arg, pi); if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int j = 0; j < i; j++) { newArgs[j] = arguments[j]; } } if (newArgs != null) { newArgs[i] = arg; } } if (newArgs != null) { arguments = new TrueReadOnlyCollection<Expression>(newArgs); } }
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; for (int i = 0, n = indexes.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = indexes[i]; RequiresCanRead(arg, nameof(arguments)); Type pType = pi.ParameterType; if (pType.IsByRef) throw Error.AccessorsCannotHaveByRefArgs(); TypeUtils.ValidateType(pType); if (!TypeUtils.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 (int 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); } }
/// <summary> /// Reduces the expression node to a simpler expression. /// </summary> /// <returns>The reduced expression.</returns> public override Expression Reduce() { // NB: Unlike the C# compiler, we don't optimize for the case where all elements are constants of a // blittable type. In such a case, the C# compiler will emit a field with the binary representation // of the whole array and emit a call to RuntimeHelpers.InitializeArray. To achieve this, we'd need // to have access to a ModuleBuilder, also tying the reduction path to the compiler and requiring // separate treatment for the interpreter. // // This optimization would matter most if many copies of the array are initialized or the array is // really big, and it only contains constants. In the case of a big array, we could argue that the // current expression API has many ineffiencies already, e.g. when emitting closures including lots // of captured variables, which does not create a display class but an array of StrongBox<T> objects. // // An alternative, which could be useful if the expression is evaluated many times (creating many // copies), would be to prepare an instance of the array, cache it, and use Array.Clone to create // a copy each time the expression is evaluated. Effectively, the reduced expression would become a // Constant node containing the fully materialized array. This would only be worth the effort if the // cost of creating a clone is sufficiently less compared to element-by-element initialization which // is likely true given it does a memberwise clone underneath. One drawback is that the expression // would root the "prototype" of the array, but this is comparable to the module image containing a // blob containing the array elements from which copies are created through InitializeArray (unless // it employs a copy-on-write approach, haven't checked). // // Note that the optimization sketched above could also be applied to ArrayInit nodes in LINQ, by // changing the LambdaCompiler (see ILGen.EmitArray which generates a sequence of stelems). It // doesn't seem like it was considered there, so maybe we can get away without it here as well. // // Finally, not that the reduction approach below is likely more expensive than EmitArray used by // the LambdaCompiler which can use dup instructions where we'll have a ldloc instruction for each // element being initialized. // // A quick experiment with an optimizer for NewArrayInit nodes with only constants yields the // following result (see Experiment.cs, method ArrayInitOptimization, in Playground): // // [RAW] new int[10] x 100000 = 2ms // [OPT] new int[10] x 100000 = 10ms // [RAW] new int[100] x 100000 = 6ms // [OPT] new int[100] x 100000 = 15ms // [RAW] new int[400] x 100000 = 28ms << // [OPT] new int[400] x 100000 = 23ms << // [RAW] new int[500] x 100000 = 33ms // [OPT] new int[500] x 100000 = 28ms // [RAW] new int[1000] x 100000 = 69ms // [OPT] new int[1000] x 100000 = 43ms // // With 100K iterations of evaluating the expression, it takes an array of 400+ elements for the // optimization to pay off. If we run into the need to optimize this, we should likely take such // a measurement into account and only apply the optimization when the expression is contained in // some loop construct and the element count is sufficiently large. var n = Expressions.Count; var rank = Bounds.Count; var res = Expression.Parameter(Type, "__array"); var exprs = new Expression[n + 2]; // NB: We need the bounds to NewArrayBounds and all values from 0 to each bound for ArrayAccess. var consts = Enumerable.Range(0, Bounds.Max() + 1).Select(CreateConstantInt32).ToArray(); exprs[0] = Expression.Assign(res, Expression.NewArrayBounds(Type.GetElementType(), Bounds.Map(i => consts[i]))); var indexValues = new int[rank]; for (var i = 1; i <= n; i++) { var idx = i - 1; var value = Expressions[idx]; for (var j = rank - 1; j >= 0; j--) { var bound = Bounds[j]; indexValues[j] = idx % bound; idx /= bound; } var indexes = new TrueReadOnlyCollection<Expression>(indexValues.Map(j => consts[j])); var element = Expression.ArrayAccess(res, indexes); exprs[i] = Expression.Assign(element, value); } exprs[n + 1] = res; return Expression.Block(new[] { res }, exprs); }
/// <summary> /// Reduces the expression node to a simpler expression. /// </summary> /// <returns>The reduced expression.</returns> public override Expression Reduce() { var statements = default(ReadOnlyCollection<Expression>); if (ReturnLabel == null) { if (Statements.Count == 0) { // NB: Can ignore variables; there's no expression that can use them anyway, and they don't have side-effects. return Expression.Empty(); } else { statements = Statements; } } else { var returnLabel = default(LabelExpression); if (ReturnLabel.Type != typeof(void)) { returnLabel = Expression.Label(ReturnLabel, Expression.Default(ReturnLabel.Type)); } else { returnLabel = Expression.Label(ReturnLabel); } if (Statements.Count == 0) { // NB: Can ignore variables; there's no expression that can use them anyway, and they don't have side-effects. return returnLabel; } else { statements = new TrueReadOnlyCollection<Expression>(Statements.AddLast(returnLabel)); } } if (Variables.Count == 0) { return Expression.Block(Type, statements); } else { // REVIEW: Should we ensure all variables get assigned default values? Cf. when it's used // in a loop and the locals don't get reinitialized. Or should we assume there's // definite assignment (or enforce it)? return Expression.Block(Type, Variables, statements); } }
private static void ValidateNewArgs(ConstructorInfo constructor, ref ReadOnlyCollection <Expression> arguments, ref ReadOnlyCollection <MemberInfo> members) { ParameterInfo[] pis; if ((pis = constructor.GetParametersCached()).Length > 0) { if (arguments.Count != pis.Length) { throw Error.IncorrectNumberOfConstructorArguments(); } if (arguments.Count != members.Count) { throw Error.IncorrectNumberOfArgumentsForMembers(); } Expression[]? newArguments = null; MemberInfo[]? newMembers = null; for (int i = 0, n = arguments.Count; i < n; i++) { Expression arg = arguments[i]; ExpressionUtils.RequiresCanRead(arg, nameof(arguments), i); MemberInfo member = members[i]; ContractUtils.RequiresNotNull(member, nameof(members), i); if (!TypeUtils.AreEquivalent(member.DeclaringType, constructor.DeclaringType)) { throw Error.ArgumentMemberNotDeclOnType(member.Name, constructor.DeclaringType !.Name, nameof(members), i); } Type memberType; ValidateAnonymousTypeMember(ref member, out memberType, nameof(members), i); if (!TypeUtils.AreReferenceAssignable(memberType, arg.Type)) { if (!TryQuote(memberType, ref arg)) { throw Error.ArgumentTypeDoesNotMatchMember(arg.Type, memberType, nameof(arguments), i); } } ParameterInfo pi = pis[i]; Type pType = pi.ParameterType; if (pType.IsByRef) { pType = pType.GetElementType() !; } if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (!TryQuote(pType, ref arg)) { throw Error.ExpressionTypeDoesNotMatchConstructorParameter(arg.Type, pType, nameof(arguments), i); } } if (newArguments == null && arg != arguments[i]) { newArguments = new Expression[arguments.Count]; for (int 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 (int 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(); } }
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; for (int i = 0, n = indexes.Length; i < n; i++) { Expression arg = arguments[i]; ParameterInfo pi = indexes[i]; RequiresCanRead(arg, "arguments"); Type pType = pi.ParameterType; ContractUtils.Requires(!pType.IsByRef, "indexes", Strings.AccessorsCannotHaveByRefArgs); TypeUtils.ValidateType(pType); if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) { if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), pType) && pType.IsAssignableFrom(arg.GetType())) { arg = Expression.Quote(arg); } else { throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); } } if (newArgs == null && arg != arguments[i]) { newArgs = new Expression[arguments.Count]; for (int 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); } }