Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / MemberInitExpression.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Runtime.CompilerServices;
22
23 #if !FEATURE_CORE_DLR
24 namespace Microsoft.Scripting.Ast {
25 #else
26 namespace System.Linq.Expressions {
27 #endif
28     /// <summary>
29     /// Represents calling a constructor and initializing one or more members of the new object.
30     /// </summary>
31     [DebuggerTypeProxy(typeof(Expression.MemberInitExpressionProxy))]
32     public sealed class MemberInitExpression : Expression {
33         private readonly NewExpression _newExpression;
34         private readonly ReadOnlyCollection<MemberBinding> _bindings;
35
36         internal MemberInitExpression(NewExpression newExpression, ReadOnlyCollection<MemberBinding> bindings) {
37             _newExpression = newExpression;
38             _bindings = bindings;
39         }
40
41         /// <summary>
42         /// Gets the static type of the expression that this <see cref="Expression" /> represents.
43         /// </summary>
44         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
45         public sealed override Type Type {
46             get { return _newExpression.Type; }
47         }
48
49         /// <summary>
50         /// Gets a value that indicates whether the expression tree node can be reduced. 
51         /// </summary>
52         public override bool CanReduce {
53             get {
54                 return true;
55             }
56         }
57
58         /// <summary>
59         /// Returns the node type of this Expression. Extension nodes should return
60         /// ExpressionType.Extension when overriding this method.
61         /// </summary>
62         /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
63         public sealed override ExpressionType NodeType {
64             get { return ExpressionType.MemberInit; }
65         }
66
67         ///<summary>Gets the expression that represents the constructor call.</summary>
68         ///<returns>A <see cref="T:System.Linq.Expressions.NewExpression" /> that represents the constructor call.</returns>
69         public NewExpression NewExpression {
70             get { return _newExpression; }
71         }
72
73         ///<summary>Gets the bindings that describe how to initialize the members of the newly created object.</summary>
74         ///<returns>A <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1" /> of <see cref="T:System.Linq.Expressions.MemberBinding" /> objects which describe how to initialize the members.</returns>
75         public ReadOnlyCollection<MemberBinding> Bindings {
76             get { return _bindings; }
77         }
78
79         /// <summary>
80         /// Dispatches to the specific visit method for this node type.
81         /// </summary>
82         protected internal override Expression Accept(ExpressionVisitor visitor) {
83             return visitor.VisitMemberInit(this);
84         }
85
86         /// <summary>
87         /// Reduces the <see cref="MemberInitExpression"/> to a simpler expression. 
88         /// If CanReduce returns true, this should return a valid expression.
89         /// This method is allowed to return another node which itself 
90         /// must be reduced.
91         /// </summary>
92         /// <returns>The reduced expression.</returns>
93         public override Expression Reduce() {
94             return ReduceMemberInit(_newExpression, _bindings, true);
95         }
96
97         internal static Expression ReduceMemberInit(Expression objExpression, ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack) {
98             var objVar = Expression.Variable(objExpression.Type, null);
99             int count = bindings.Count;
100             var block = new Expression[count + 2];
101             block[0] = Expression.Assign(objVar, objExpression);
102             for (int i = 0; i < count; i++) {
103                 block[i + 1] = ReduceMemberBinding(objVar, bindings[i]);
104             }
105             block[count + 1] = keepOnStack ? (Expression)objVar : Expression.Empty();
106             return Expression.Block(new TrueReadOnlyCollection<Expression>(block));
107         }
108
109         internal static Expression ReduceListInit(Expression listExpression, ReadOnlyCollection<ElementInit> initializers, bool keepOnStack) {
110             var listVar = Expression.Variable(listExpression.Type, null);
111             int count = initializers.Count;
112             var block = new Expression[count + 2];
113             block[0] = Expression.Assign(listVar, listExpression);
114             for (int i = 0; i < count; i++) {
115                 ElementInit element = initializers[i];
116                 block[i + 1] = Expression.Call(listVar, element.AddMethod, element.Arguments);
117             }
118             block[count + 1] = keepOnStack ? (Expression)listVar : Expression.Empty();
119             return Expression.Block(new TrueReadOnlyCollection<Expression>(block));
120         }
121
122         internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding) {
123             MemberExpression member = Expression.MakeMemberAccess(objVar, binding.Member);
124             switch (binding.BindingType) {
125                 case MemberBindingType.Assignment:
126                     return Expression.Assign(member, ((MemberAssignment)binding).Expression);
127                 case MemberBindingType.ListBinding:
128                     return ReduceListInit(member, ((MemberListBinding)binding).Initializers, false);
129                 case MemberBindingType.MemberBinding:
130                     return ReduceMemberInit(member, ((MemberMemberBinding)binding).Bindings, false);
131                 default: throw ContractUtils.Unreachable;
132             }
133         }
134
135         /// <summary>
136         /// Creates a new expression that is like this one, but using the
137         /// supplied children. If all of the children are the same, it will
138         /// return this expression.
139         /// </summary>
140         /// <param name="newExpression">The <see cref="NewExpression" /> property of the result.</param>
141         /// <param name="bindings">The <see cref="Bindings" /> property of the result.</param>
142         /// <returns>This expression if no children changed, or an expression with the updated children.</returns>
143         public MemberInitExpression Update(NewExpression newExpression, IEnumerable<MemberBinding> bindings) {
144             if (newExpression == NewExpression && bindings == Bindings) {
145                 return this;
146             }
147             return Expression.MemberInit(newExpression, bindings);
148         }
149     }
150
151     public partial class Expression {
152         ///<summary>Creates a <see cref="T:System.Linq.Expressions.MemberInitExpression" />.</summary>
153         ///<returns>A <see cref="T:System.Linq.Expressions.MemberInitExpression" /> that has the <see cref="P:System.Linq.Expressions.Expression.NodeType" /> property equal to <see cref="F:System.Linq.Expressions.ExpressionType.MemberInit" /> and the <see cref="P:System.Linq.Expressions.MemberInitExpression.NewExpression" /> and <see cref="P:System.Linq.Expressions.MemberInitExpression.Bindings" /> properties set to the specified values.</returns>
154         ///<param name="newExpression">A <see cref="T:System.Linq.Expressions.NewExpression" /> to set the <see cref="P:System.Linq.Expressions.MemberInitExpression.NewExpression" /> property equal to.</param>
155         ///<param name="bindings">An array of <see cref="T:System.Linq.Expressions.MemberBinding" /> objects to use to populate the <see cref="P:System.Linq.Expressions.MemberInitExpression.Bindings" /> collection.</param>
156         ///<exception cref="T:System.ArgumentNullException">
157         ///<paramref name="newExpression" /> or <paramref name="bindings" /> is null.</exception>
158         ///<exception cref="T:System.ArgumentException">The <see cref="P:System.Linq.Expressions.MemberBinding.Member" /> property of an element of <paramref name="bindings" /> does not represent a member of the type that <paramref name="newExpression" />.Type represents.</exception>
159         public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings) {
160             return MemberInit(newExpression, (IEnumerable<MemberBinding>)bindings);
161         }
162
163         ///<summary>Creates a <see cref="T:System.Linq.Expressions.MemberInitExpression" />.</summary>
164         ///<returns>A <see cref="T:System.Linq.Expressions.MemberInitExpression" /> that has the <see cref="P:System.Linq.Expressions.Expression.NodeType" /> property equal to <see cref="F:System.Linq.Expressions.ExpressionType.MemberInit" /> and the <see cref="P:System.Linq.Expressions.MemberInitExpression.NewExpression" /> and <see cref="P:System.Linq.Expressions.MemberInitExpression.Bindings" /> properties set to the specified values.</returns>
165         ///<param name="newExpression">A <see cref="T:System.Linq.Expressions.NewExpression" /> to set the <see cref="P:System.Linq.Expressions.MemberInitExpression.NewExpression" /> property equal to.</param>
166         ///<param name="bindings">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> that contains <see cref="T:System.Linq.Expressions.MemberBinding" /> objects to use to populate the <see cref="P:System.Linq.Expressions.MemberInitExpression.Bindings" /> collection.</param>
167         ///<exception cref="T:System.ArgumentNullException">
168         ///<paramref name="newExpression" /> or <paramref name="bindings" /> is null.</exception>
169         ///<exception cref="T:System.ArgumentException">The <see cref="P:System.Linq.Expressions.MemberBinding.Member" /> property of an element of <paramref name="bindings" /> does not represent a member of the type that <paramref name="newExpression" />.Type represents.</exception>
170         public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings) {
171             ContractUtils.RequiresNotNull(newExpression, "newExpression");
172             ContractUtils.RequiresNotNull(bindings, "bindings");
173             var roBindings = bindings.ToReadOnly();
174             ValidateMemberInitArgs(newExpression.Type, roBindings);
175             return new MemberInitExpression(newExpression, roBindings);
176         }
177     }
178 }