Bug fixes and a couple of optimizations (used a nice profiler to find
[mono.git] / mcs / mcs / assign.cs
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9 using System;
10 using System.Reflection;
11 using System.Reflection.Emit;
12
13 namespace Mono.CSharp {
14
15         /// <summary>
16         ///   This interface is implemented by expressions that can be assigned to.
17         /// </summary>
18         /// <remarks>
19         ///   This interface is implemented by Expressions whose values can not
20         ///   store the result on the top of the stack.
21         ///
22         ///   Expressions implementing this (Properties, Indexers and Arrays) would
23         ///   perform an assignment of the Expression "source" into its final
24         ///   location.
25         ///
26         ///   No values on the top of the stack are expected to be left by
27         ///   invoking this method.
28         /// </remarks>
29         public interface IAssignMethod {
30                 void EmitAssign (EmitContext ec, Expression source);
31         }
32
33         /// <summary>
34         ///   An Expression to hold a temporary value.
35         /// </summary>
36         /// <remarks>
37         ///   The LocalTemporary class is used to hold temporary values of a given
38         ///   type to "simulate" the expression semantics on property and indexer
39         ///   access whose return values are void.
40         ///
41         ///   The local temporary is used to alter the normal flow of code generation
42         ///   basically it creates a local variable, and its emit instruction generates
43         ///   code to access this value, return its address or save its value.
44         /// </remarks>
45         public class LocalTemporary : Expression, IMemoryLocation {
46                 LocalBuilder builder;
47                 
48                 public LocalTemporary (EmitContext ec, Type t)
49                 {
50                         type = t;
51                         eclass = ExprClass.Value;
52                         builder = ec.GetTemporaryStorage (t);
53                 }
54
55                 public void Release (EmitContext ec)
56                 {
57                         ec.FreeTemporaryStorage (builder);
58                         builder = null;
59                 }
60                 
61                 public LocalTemporary (LocalBuilder b, Type t)
62                 {
63                         type = t;
64                         eclass = ExprClass.Value;
65                         builder = b;
66                 }
67                 
68                 public override Expression DoResolve (EmitContext ec)
69                 {
70                         return this;
71                 }
72
73                 public override void Emit (EmitContext ec)
74                 {
75                         ec.ig.Emit (OpCodes.Ldloc, builder); 
76                 }
77
78                 public void Store (EmitContext ec)
79                 {
80                         ec.ig.Emit (OpCodes.Stloc, builder);
81                 }
82
83                 public void AddressOf (EmitContext ec, AddressOp mode)
84                 {
85                         ec.ig.Emit (OpCodes.Ldloca, builder);
86                 }
87         }
88
89         /// <summary>
90         ///   The Assign node takes care of assigning the value of source into
91         ///   the expression represented by target. 
92         /// </summary>
93         public class Assign : ExpressionStatement {
94                 Expression target, source;
95                 Location l;
96
97                 public Assign (Expression target, Expression source, Location l)
98                 {
99                         this.target = target;
100                         this.source = source;
101                         this.l = l;
102                 }
103
104                 public Expression Target {
105                         get {
106                                 return target;
107                         }
108
109                         set {
110                                 target = value;
111                         }
112                 }
113
114                 public Expression Source {
115                         get {
116                                 return source;
117                         }
118
119                         set {
120                                 source = value;
121                         }
122                 }
123
124                 public static void error70 (EventInfo ei, Location l)
125                 {
126                         Report.Error (70, l, "The event '" + ei.Name +
127                                       "' can only appear on the left-side of a += or -= (except when" +
128                                       " used from within the type '" + ei.DeclaringType + "')");
129                 }
130
131                 public override Expression DoResolve (EmitContext ec)
132                 {
133                         source = source.Resolve (ec);
134                         if (source == null)
135                                 return null;
136
137                         target = target.ResolveLValue (ec, source);
138                         
139                         if (target == null)
140                                 return null;
141
142                         Type target_type = target.Type;
143                         Type source_type = source.Type;
144
145                         type = target_type;
146                         eclass = ExprClass.Value;
147                         
148                         //
149                         // If we are doing a property assignment, then
150                         // set the `value' field on the property, and Resolve
151                         // it.
152                         //
153                         if (target is PropertyExpr){
154                                 PropertyExpr property_assign = (PropertyExpr) target;
155
156                                 //
157                                 // FIXME: Maybe handle this in the LValueResolve
158                                 //
159                                 if (!property_assign.VerifyAssignable ())
160                                         return null;
161                                 
162                                 return this;
163                         }
164
165                         if (target is IndexerAccess){
166                                 IndexerAccess ia = (IndexerAccess) target;
167
168                                 return this;
169                         }
170
171                         if (target is EventExpr) {
172
173                                 Binary tmp;
174                                 EventInfo ei = ((EventExpr) target).EventInfo;
175
176
177                                 Expression ml = MemberLookup (
178                                         ec, ec.TypeContainer.TypeBuilder, ei.Name,
179                                         MemberTypes.Event, AllBindingFlags, l);
180
181                                 if (ml == null) {
182                                         //
183                                         // If this is the case, then the Event does not belong 
184                                         // to this TypeContainer and so, according to the spec
185                                         // is allowed to only appear on the left hand of
186                                         // the += and -= operators
187                                         //
188                                         // Note that if target will not appear as an EventExpr
189                                         // in the case it is being referenced within the same type container;
190                                         // it will appear as a FieldExpr in that case.
191                                         //
192                                         
193                                         if (!(source is Binary)) {
194                                                 error70 (ei, l);
195                                                 return null;
196                                         } else {
197                                                 tmp = ((Binary) source);
198                                                 if (tmp.Oper != Binary.Operator.Addition &&
199                                                     tmp.Oper != Binary.Operator.Subtraction) {
200                                                         error70 (ei, l);
201                                                         return null;
202                                                 }
203                                         }
204                                 }
205                         }
206                         
207                         if (source is New && target_type.IsSubclassOf (TypeManager.value_type)){
208                                 New n = (New) source;
209
210                                 n.ValueTypeVariable = target;
211                                 return n;
212                         }
213
214                         if (target_type != source_type){
215                                 source = ConvertImplicitRequired (ec, source, target_type, l);
216                                 if (source == null)
217                                         return null;
218                         }
219
220                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
221                                 Report.Error (131, l,
222                                               "Left hand of an assignment must be a variable, " +
223                                               "a property or an indexer");
224                                 return null;
225                         }
226
227                         return this;
228                 }
229
230                 void Emit (EmitContext ec, bool is_statement)
231                 {
232                         if (target is EventExpr) {
233                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
234                                 return;
235                         }
236
237                         //
238                         // FIXME! We need a way to "probe" if the process can
239                         // just use `dup' to propagate the result
240                         // 
241                         IAssignMethod am = (IAssignMethod) target;
242                         
243                         if (is_statement)
244                                 am.EmitAssign (ec, source);
245                         else {
246                                 LocalTemporary tempo;
247                                 
248                                 tempo = new LocalTemporary (ec, source.Type);
249                                 
250                                 source.Emit (ec);
251                                 tempo.Store (ec);
252                                 am.EmitAssign (ec, tempo);
253                                 tempo.Emit (ec);
254                                 tempo.Release (ec);
255                         }
256                 }
257                 
258                 public override void Emit (EmitContext ec)
259                 {
260                         Emit (ec, false);
261                 }
262
263                 public override void EmitStatement (EmitContext ec)
264                 {
265                         Emit (ec, true);
266                 }
267         }
268 }
269
270
271
272