2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / mcs / argument.cs
1 //
2 // argument.cs: Argument expressions
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximain.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003-2008 Novell, Inc.
10 //
11
12 using System;
13 using System.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16
17 namespace Mono.CSharp
18 {
19         //
20         // Argument expression used for invocation
21         //
22         public class Argument
23         {
24                 public enum AType : byte
25                 {
26                         Ref = 1,                // ref modifier used
27                         Out = 2,                // out modifier used
28                         Default = 3             // argument created from default parameter value
29                 }
30
31                 public readonly AType ArgType;
32                 public Expression Expr;
33
34                 public Argument (Expression expr, AType type)
35                 {
36                         this.Expr = expr;
37                         this.ArgType = type;
38                 }
39
40                 public Argument (Expression expr)
41                 {
42                         if (expr == null)
43                                 throw new ArgumentNullException ();
44
45                         this.Expr = expr;
46                 }
47
48                 public Type Type {
49                         get { return Expr.Type; }
50                 }
51
52                 public Parameter.Modifier Modifier {
53                         get {
54                                 switch (ArgType) {
55                                 case AType.Out:
56                                         return Parameter.Modifier.OUT;
57
58                                 case AType.Ref:
59                                         return Parameter.Modifier.REF;
60
61                                 default:
62                                         return Parameter.Modifier.NONE;
63                                 }
64                         }
65                 }
66
67                 public virtual Expression CreateExpressionTree (EmitContext ec)
68                 {
69                         if (ArgType == AType.Default)
70                                 Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
71
72                         return Expr.CreateExpressionTree (ec);
73                 }
74
75                 public string GetSignatureForError ()
76                 {
77                         if (Expr.eclass == ExprClass.MethodGroup)
78                                 return Expr.ExprClassName;
79
80                         return TypeManager.CSharpName (Expr.Type);
81                 }
82
83                 public bool IsByRef {
84                         get { return ArgType == AType.Ref || ArgType == AType.Out; }
85                 }
86
87                 public bool IsDefaultArgument {
88                         get { return ArgType == AType.Default; }
89                 }
90
91                 public bool ResolveMethodGroup (EmitContext ec)
92                 {
93                         SimpleName sn = Expr as SimpleName;
94                         if (sn != null)
95                                 Expr = sn.GetMethodGroup ();
96
97                         // FIXME: csc doesn't report any error if you try to use `ref' or
98                         //        `out' in a delegate creation expression.
99                         Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
100                         if (Expr == null)
101                                 return false;
102
103                         return true;
104                 }
105
106                 public void Resolve (EmitContext ec)
107                 {
108                         if (Expr == EmptyExpression.Null)
109                                 return;
110
111                         using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
112                                 // Verify that the argument is readable
113                                 if (ArgType != AType.Out)
114                                         Expr = Expr.Resolve (ec);
115
116                                 // Verify that the argument is writeable
117                                 if (Expr != null && IsByRef)
118                                         Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
119
120                                 if (Expr == null)
121                                         Expr = EmptyExpression.Null;
122                         }
123                 }
124
125                 public virtual void Emit (EmitContext ec)
126                 {
127                         if (!IsByRef) {
128                                 Expr.Emit (ec);
129                                 return;
130                         }
131
132                         AddressOp mode = AddressOp.Store;
133                         if (ArgType == AType.Ref)
134                                 mode |= AddressOp.Load;
135
136                         IMemoryLocation ml = (IMemoryLocation) Expr;
137                         ParameterReference pr = ml as ParameterReference;
138
139                         //
140                         // ParameterReferences might already be references, so we want
141                         // to pass just the value
142                         //
143                         if (pr != null && pr.IsRef)
144                                 pr.EmitLoad (ec);
145                         else
146                                 ml.AddressOf (ec, mode);
147                 }
148
149                 public Argument Clone (CloneContext clonectx)
150                 {
151                         Argument a = (Argument) MemberwiseClone ();
152                         a.Expr = Expr.Clone (clonectx);
153                         return a;
154                 }
155         }
156
157         public class NamedArgument : Argument
158         {
159                 public readonly LocatedToken Name;
160                 LocalTemporary variable;
161
162                 public NamedArgument (LocatedToken name, Expression expr)
163                         : base (expr)
164                 {
165                         Name = name;
166                 }
167
168                 public override Expression CreateExpressionTree (EmitContext ec)
169                 {
170                         Report.Error (853, Name.Location, "An expression tree cannot contain named argument");
171                         return base.CreateExpressionTree (ec);
172                 }
173
174                 public override void Emit (EmitContext ec)
175                 {
176                         // TODO: Should guard against multiple emits
177                         base.Emit (ec);
178
179                         // Release temporary variable when used
180                         if (variable != null)
181                                 variable.Release (ec);
182                 }
183
184                 public void EmitAssign (EmitContext ec)
185                 {
186                         Expr.Emit (ec);
187                         variable = new LocalTemporary (Expr.Type);
188                         variable.Store (ec);
189
190                         Expr = variable;
191                 }
192         }
193
194         public class Arguments
195         {
196                 ArrayList args;                 // TODO: This should really be linked list
197                 ArrayList reordered;    // TODO: LinkedList
198
199                 public Arguments (int capacity)
200                 {
201                         args = new ArrayList (capacity);
202                 }
203
204                 public int Add (Argument arg)
205                 {
206                         return args.Add (arg);
207                 }
208
209                 public void AddRange (Arguments args)
210                 {
211                         this.args.AddRange (args.args);
212                 }
213
214                 public static Arguments CreateForExpressionTree (EmitContext ec, Arguments args, params Expression[] e)
215                 {
216                         Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
217                         for (int i = 0; i < e.Length; ++i) {
218                                 if (e [i] != null)
219                                         all.Add (new Argument (e[i]));
220                         }
221
222                         if (args != null) {
223                                 foreach (Argument a in args.args) {
224                                         Expression tree_arg = a.CreateExpressionTree (ec);
225                                         if (tree_arg != null)
226                                                 all.Add (new Argument (tree_arg));
227                                 }
228                         }
229
230                         return all;
231                 }
232
233                 public void CheckArrayAsAttribute ()
234                 {
235                         foreach (Argument arg in args) {
236                                 // Type is undefined (was error 246)
237                                 if (arg.Type == null)
238                                         continue;
239
240                                 if (arg.Type.IsArray)
241                                         Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
242                         }
243                 }
244
245                 public Arguments Clone (CloneContext ctx)
246                 {
247                         Arguments cloned = new Arguments (args.Count);
248                         foreach (Argument a in args)
249                                 cloned.Add (a.Clone (ctx));
250
251                         return cloned;
252                 }
253
254                 public int Count {
255                         get { return args.Count; }
256                 }
257
258                 //
259                 // Emits a list of resolved Arguments
260                 // 
261                 public void Emit (EmitContext ec)
262                 {
263                         Emit (ec, false, null);
264                 }
265
266                 //
267                 // if `dup_args' is true, a copy of the arguments will be left
268                 // on the stack. If `dup_args' is true, you can specify `this_arg'
269                 // which will be duplicated before any other args. Only EmitCall
270                 // should be using this interface.
271                 //
272                 public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
273                 {
274                         LocalTemporary[] temps = null;
275
276                         if (dup_args && Count != 0)
277                                 temps = new LocalTemporary [Count];
278
279                         if (reordered != null && Count > 1) {
280                                 foreach (NamedArgument na in reordered)
281                                         na.EmitAssign (ec);
282                         }
283
284                         int i = 0;
285                         foreach (Argument a in args) {
286                                 a.Emit (ec);
287                                 if (dup_args) {
288                                         ec.ig.Emit (OpCodes.Dup);
289                                         (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
290                                 }
291                         }
292
293                         if (dup_args) {
294                                 if (this_arg != null)
295                                         this_arg.Emit (ec);
296
297                                 for (i = 0; i < temps.Length; i++) {
298                                         temps[i].Emit (ec);
299                                         temps[i].Release (ec);
300                                 }
301                         }
302                 }
303
304                 public bool GetAttributableValue (EmitContext ec, out object[] values)
305                 {
306                         values = new object [args.Count];
307                         for (int j = 0; j < values.Length; ++j) {
308                                 Argument a = this [j];
309                                 if (!a.Expr.GetAttributableValue (ec, a.Type, out values[j]))
310                                         return false;
311                         }
312
313                         return true;
314                 }
315
316                 public IEnumerator GetEnumerator ()
317                 {
318                         return args.GetEnumerator ();
319                 }
320
321                 public void Insert (int index, Argument arg)
322                 {
323                         args.Insert (index, arg);
324                 }
325
326                 public void MarkReorderedArgument (NamedArgument a)
327                 {
328                         //
329                         // Constant expression can have no effect on left-to-right execution
330                         //
331                         if (a.Expr is Constant)
332                                 return;
333
334                         if (reordered == null)
335                                 reordered = new ArrayList ();
336
337                         reordered.Add (a);
338                 }
339
340                 public void Resolve (EmitContext ec)
341                 {
342                         foreach (Argument a in args)
343                                 a.Resolve (ec);
344                 }
345
346                 public void MutateHoistedGenericType (AnonymousMethodStorey storey)
347                 {
348                         foreach (Argument a in args)
349                                 a.Expr.MutateHoistedGenericType (storey);
350                 }
351
352                 public void RemoveAt (int index)
353                 {
354                         args.RemoveAt (index);
355                 }
356
357                 public Argument this [int index] {
358                         get { return (Argument) args [index]; }
359                         set { args [index] = value; }
360                 }
361         }
362 }