2009-08-03 Marek Safar <marek.safar@gmail.com>
[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 ArrayList CreateDynamicBinderArguments ()
215                 {
216                         ArrayList all = new ArrayList (args.Count);
217                         Location loc = Location.Null;
218
219                         MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
220
221                         foreach (Argument a in args) {
222                                 Arguments dargs = new Arguments (2);
223
224                                 // TODO: Inspect a.Type or a.Expression
225
226                                 // CSharpArgumentInfoFlags.None = 0
227                                 dargs.Add (new Argument (new IntLiteral (0, loc)));
228
229                                 // TODO: named
230                                 dargs.Add (new Argument (new NullLiteral (loc)));
231                                 all.Add (new New (new MemberAccess (binder, "CSharpArgumentInfo", loc), dargs, loc));
232                         }
233
234                         return all;
235                 }
236
237                 public static Arguments CreateForExpressionTree (EmitContext ec, Arguments args, params Expression[] e)
238                 {
239                         Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
240                         for (int i = 0; i < e.Length; ++i) {
241                                 if (e [i] != null)
242                                         all.Add (new Argument (e[i]));
243                         }
244
245                         if (args != null) {
246                                 foreach (Argument a in args.args) {
247                                         Expression tree_arg = a.CreateExpressionTree (ec);
248                                         if (tree_arg != null)
249                                                 all.Add (new Argument (tree_arg));
250                                 }
251                         }
252
253                         return all;
254                 }
255
256                 public void CheckArrayAsAttribute ()
257                 {
258                         foreach (Argument arg in args) {
259                                 // Type is undefined (was error 246)
260                                 if (arg.Type == null)
261                                         continue;
262
263                                 if (arg.Type.IsArray)
264                                         Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
265                         }
266                 }
267
268                 public Arguments Clone (CloneContext ctx)
269                 {
270                         Arguments cloned = new Arguments (args.Count);
271                         foreach (Argument a in args)
272                                 cloned.Add (a.Clone (ctx));
273
274                         return cloned;
275                 }
276
277                 public int Count {
278                         get { return args.Count; }
279                 }
280
281                 //
282                 // Emits a list of resolved Arguments
283                 // 
284                 public void Emit (EmitContext ec)
285                 {
286                         Emit (ec, false, null);
287                 }
288
289                 //
290                 // if `dup_args' is true, a copy of the arguments will be left
291                 // on the stack. If `dup_args' is true, you can specify `this_arg'
292                 // which will be duplicated before any other args. Only EmitCall
293                 // should be using this interface.
294                 //
295                 public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
296                 {
297                         LocalTemporary[] temps = null;
298
299                         if (dup_args && Count != 0)
300                                 temps = new LocalTemporary [Count];
301
302                         if (reordered != null && Count > 1) {
303                                 foreach (NamedArgument na in reordered)
304                                         na.EmitAssign (ec);
305                         }
306
307                         int i = 0;
308                         foreach (Argument a in args) {
309                                 a.Emit (ec);
310                                 if (dup_args) {
311                                         ec.ig.Emit (OpCodes.Dup);
312                                         (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
313                                 }
314                         }
315
316                         if (dup_args) {
317                                 if (this_arg != null)
318                                         this_arg.Emit (ec);
319
320                                 for (i = 0; i < temps.Length; i++) {
321                                         temps[i].Emit (ec);
322                                         temps[i].Release (ec);
323                                 }
324                         }
325                 }
326
327                 public bool GetAttributableValue (EmitContext ec, out object[] values)
328                 {
329                         values = new object [args.Count];
330                         for (int j = 0; j < values.Length; ++j) {
331                                 Argument a = this [j];
332                                 if (!a.Expr.GetAttributableValue (ec, a.Type, out values[j]))
333                                         return false;
334                         }
335
336                         return true;
337                 }
338
339                 public IEnumerator GetEnumerator ()
340                 {
341                         return args.GetEnumerator ();
342                 }
343
344                 public void Insert (int index, Argument arg)
345                 {
346                         args.Insert (index, arg);
347                 }
348
349                 public void MarkReorderedArgument (NamedArgument a)
350                 {
351                         //
352                         // Constant expression can have no effect on left-to-right execution
353                         //
354                         if (a.Expr is Constant)
355                                 return;
356
357                         if (reordered == null)
358                                 reordered = new ArrayList ();
359
360                         reordered.Add (a);
361                 }
362
363                 //
364                 // Returns dynamic when at least one argument is of dynamic type
365                 //
366                 public void Resolve (EmitContext ec, out bool dynamic)
367                 {
368                         dynamic = false;
369                         foreach (Argument a in args) {
370                                 a.Resolve (ec);
371                                 dynamic |= TypeManager.IsDynamicType (a.Type);
372                         }
373                 }
374
375                 public void MutateHoistedGenericType (AnonymousMethodStorey storey)
376                 {
377                         foreach (Argument a in args)
378                                 a.Expr.MutateHoistedGenericType (storey);
379                 }
380
381                 public void RemoveAt (int index)
382                 {
383                         args.RemoveAt (index);
384                 }
385
386                 public Argument this [int index] {
387                         get { return (Argument) args [index]; }
388                         set { args [index] = value; }
389                 }
390         }
391 }