2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[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 Microsoft Public License. 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  Microsoft Public License, 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 Microsoft Public License.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15 using System; using Microsoft;
16
17
18 using System.Collections.Generic;
19 using System.Collections.ObjectModel;
20 using System.Diagnostics;
21 #if CODEPLEX_40
22 using System.Dynamic.Utils;
23 #else
24 using Microsoft.Scripting.Utils;
25 #endif
26 using System.Runtime.CompilerServices;
27 #if !CODEPLEX_40
28 using Microsoft.Runtime.CompilerServices;
29 #endif
30
31
32 #if CODEPLEX_40
33 namespace System.Linq.Expressions {
34 #else
35 namespace Microsoft.Linq.Expressions {
36 #endif
37     /// <summary>
38     /// Represents calling a constructor and initializing one or more members of the new object.
39     /// </summary>
40 #if !SILVERLIGHT
41     [DebuggerTypeProxy(typeof(Expression.MemberInitExpressionProxy))]
42 #endif
43     public sealed class MemberInitExpression : Expression {
44         private readonly NewExpression _newExpression;
45         private readonly ReadOnlyCollection<MemberBinding> _bindings;
46
47         internal MemberInitExpression(NewExpression newExpression, ReadOnlyCollection<MemberBinding> bindings) {
48             _newExpression = newExpression;
49             _bindings = bindings;
50         }
51
52         /// <summary>
53         /// Gets the static type of the expression that this <see cref="Expression" /> represents.
54         /// </summary>
55         /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
56         public sealed override Type Type {
57             get { return _newExpression.Type; }
58         }
59
60         /// <summary>
61         /// Gets a value that indicates whether the expression tree node can be reduced. 
62         /// </summary>
63         public override bool CanReduce {
64             get {
65                 return true;
66             }
67         }
68
69         /// <summary>
70         /// Returns the node type of this Expression. Extension nodes should return
71         /// ExpressionType.Extension when overriding this method.
72         /// </summary>
73         /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
74         public sealed override ExpressionType NodeType {
75             get { return ExpressionType.MemberInit; }
76         }
77
78         ///<summary>Gets the expression that represents the constructor call.</summary>
79         ///<returns>A <see cref="T:Microsoft.Linq.Expressions.NewExpression" /> that represents the constructor call.</returns>
80         public NewExpression NewExpression {
81             get { return _newExpression; }
82         }
83
84         ///<summary>Gets the bindings that describe how to initialize the members of the newly created object.</summary>
85         ///<returns>A <see cref="T:System.Collections.ObjectModel.ReadOnlyCollection`1" /> of <see cref="T:Microsoft.Linq.Expressions.MemberBinding" /> objects which describe how to initialize the members.</returns>
86         public ReadOnlyCollection<MemberBinding> Bindings {
87             get { return _bindings; }
88         }
89
90         internal override Expression Accept(ExpressionVisitor visitor) {
91             return visitor.VisitMemberInit(this);
92         }
93
94         /// <summary>
95         /// Reduces the <see cref="MemberInitExpression"/> to a simpler expression. 
96         /// If CanReduce returns true, this should return a valid expression.
97         /// This method is allowed to return another node which itself 
98         /// must be reduced.
99         /// </summary>
100         /// <returns>The reduced expression.</returns>
101         public override Expression Reduce() {
102             return ReduceMemberInit(_newExpression, _bindings, true);
103         }
104
105         internal static Expression ReduceMemberInit(Expression objExpression, ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack) {
106             var objVar = Expression.Variable(objExpression.Type, null);
107             int count = bindings.Count;
108             var block = new Expression[count + 2];
109             block[0] = Expression.Assign(objVar, objExpression);
110             for (int i = 0; i < count; i++) {
111                 block[i + 1] = ReduceMemberBinding(objVar, bindings[i]);
112             }
113             block[count + 1] = keepOnStack ? (Expression)objVar : Expression.Empty();
114             return Expression.Block(new TrueReadOnlyCollection<Expression>(block));
115         }
116
117         internal static Expression ReduceListInit(Expression listExpression, ReadOnlyCollection<ElementInit> initializers, bool keepOnStack) {
118             var listVar = Expression.Variable(listExpression.Type, null);
119             int count = initializers.Count;
120             var block = new Expression[count + 2];
121             block[0] = Expression.Assign(listVar, listExpression);
122             for (int i = 0; i < count; i++) {
123                 ElementInit element = initializers[i];
124                 block[i + 1] = Expression.Call(listVar, element.AddMethod, element.Arguments);
125             }
126             block[count + 1] = keepOnStack ? (Expression)listVar : Expression.Empty();
127             return Expression.Block(new TrueReadOnlyCollection<Expression>(block));
128         }
129
130         internal static Expression ReduceMemberBinding(ParameterExpression objVar, MemberBinding binding) {
131             MemberExpression member = Expression.MakeMemberAccess(objVar, binding.Member);
132             switch (binding.BindingType) {
133                 case MemberBindingType.Assignment:
134                     return Expression.Assign(member, ((MemberAssignment)binding).Expression);
135                 case MemberBindingType.ListBinding:
136                     return ReduceListInit(member, ((MemberListBinding)binding).Initializers, false);
137                 case MemberBindingType.MemberBinding:
138                     return ReduceMemberInit(member, ((MemberMemberBinding)binding).Bindings, false);
139                 default: throw ContractUtils.Unreachable;
140             }
141         }
142     }
143
144     public partial class Expression {
145         ///<summary>Creates a <see cref="T:Microsoft.Linq.Expressions.MemberInitExpression" />.</summary>
146         ///<returns>A <see cref="T:Microsoft.Linq.Expressions.MemberInitExpression" /> that has the <see cref="P:Microsoft.Linq.Expressions.Expression.NodeType" /> property equal to <see cref="F:Microsoft.Linq.Expressions.ExpressionType.MemberInit" /> and the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.NewExpression" /> and <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.Bindings" /> properties set to the specified values.</returns>
147         ///<param name="newExpression">A <see cref="T:Microsoft.Linq.Expressions.NewExpression" /> to set the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.NewExpression" /> property equal to.</param>
148         ///<param name="bindings">An array of <see cref="T:Microsoft.Linq.Expressions.MemberBinding" /> objects to use to populate the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.Bindings" /> collection.</param>
149         ///<exception cref="T:System.ArgumentNullException">
150         ///<paramref name="newExpression" /> or <paramref name="bindings" /> is null.</exception>
151         ///<exception cref="T:System.ArgumentException">The <see cref="P:Microsoft.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>
152         public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings) {
153             return MemberInit(newExpression, (IEnumerable<MemberBinding>)bindings);
154         }
155
156         ///<summary>Creates a <see cref="T:Microsoft.Linq.Expressions.MemberInitExpression" />.</summary>
157         ///<returns>A <see cref="T:Microsoft.Linq.Expressions.MemberInitExpression" /> that has the <see cref="P:Microsoft.Linq.Expressions.Expression.NodeType" /> property equal to <see cref="F:Microsoft.Linq.Expressions.ExpressionType.MemberInit" /> and the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.NewExpression" /> and <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.Bindings" /> properties set to the specified values.</returns>
158         ///<param name="newExpression">A <see cref="T:Microsoft.Linq.Expressions.NewExpression" /> to set the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.NewExpression" /> property equal to.</param>
159         ///<param name="bindings">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> that contains <see cref="T:Microsoft.Linq.Expressions.MemberBinding" /> objects to use to populate the <see cref="P:Microsoft.Linq.Expressions.MemberInitExpression.Bindings" /> collection.</param>
160         ///<exception cref="T:System.ArgumentNullException">
161         ///<paramref name="newExpression" /> or <paramref name="bindings" /> is null.</exception>
162         ///<exception cref="T:System.ArgumentException">The <see cref="P:Microsoft.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>
163         public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings) {
164             ContractUtils.RequiresNotNull(newExpression, "newExpression");
165             ContractUtils.RequiresNotNull(bindings, "bindings");
166             var roBindings = bindings.ToReadOnly();
167             ValidateMemberInitArgs(newExpression.Type, roBindings);
168             return new MemberInitExpression(newExpression, roBindings);
169         }
170     }
171 }