Merge branch 'master' of http://github.com/mono/mono
[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.Reflection;
14 using System.Reflection.Emit;
15 using System.Collections.Generic;
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                         None = 0,
27                         Ref = 1,                        // ref modifier used
28                         Out = 2,                        // out modifier used
29                         Default = 3,            // argument created from default parameter value
30                         DynamicTypeName = 4     // System.Type argument for dynamic binding
31                 }
32
33                 public readonly AType ArgType;
34                 public Expression Expr;
35
36                 public Argument (Expression expr, AType type)
37                 {
38                         this.Expr = expr;
39                         this.ArgType = type;
40                 }
41
42                 public Argument (Expression expr)
43                 {
44                         if (expr == null)
45                                 throw new ArgumentNullException ();
46
47                         this.Expr = expr;
48                 }
49
50                 public TypeSpec Type {
51                         get { return Expr.Type; }
52                 }
53
54                 public Parameter.Modifier Modifier {
55                         get {
56                                 switch (ArgType) {
57                                 case AType.Out:
58                                         return Parameter.Modifier.OUT;
59
60                                 case AType.Ref:
61                                         return Parameter.Modifier.REF;
62
63                                 default:
64                                         return Parameter.Modifier.NONE;
65                                 }
66                         }
67                 }
68
69                 public virtual Expression CreateExpressionTree (ResolveContext ec)
70                 {
71                         if (ArgType == AType.Default)
72                                 ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
73
74                         return Expr.CreateExpressionTree (ec);
75                 }
76
77                 public string GetSignatureForError ()
78                 {
79                         if (Expr.eclass == ExprClass.MethodGroup)
80                                 return Expr.ExprClassName;
81
82                         return TypeManager.CSharpName (Expr.Type);
83                 }
84
85                 public bool IsByRef {
86                         get { return ArgType == AType.Ref || ArgType == AType.Out; }
87                 }
88
89                 public bool IsDefaultArgument {
90                         get { return ArgType == AType.Default; }
91                 }
92
93                 public bool ResolveMethodGroup (ResolveContext ec)
94                 {
95                         SimpleName sn = Expr as SimpleName;
96                         if (sn != null)
97                                 Expr = sn.GetMethodGroup ();
98
99                         // FIXME: csc doesn't report any error if you try to use `ref' or
100                         //        `out' in a delegate creation expression.
101                         Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
102                         if (Expr == null)
103                                 return false;
104
105                         return true;
106                 }
107
108                 public void Resolve (ResolveContext ec)
109                 {
110                         if (Expr == EmptyExpression.Null)
111                                 return;
112
113 //                      using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
114                                 // Verify that the argument is readable
115                                 if (ArgType != AType.Out)
116                                         Expr = Expr.Resolve (ec);
117
118                                 // Verify that the argument is writeable
119                                 if (Expr != null && IsByRef)
120                                         Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess.Instance);
121
122                                 if (Expr == null)
123                                         Expr = EmptyExpression.Null;
124 //                      }
125                 }
126
127                 public virtual void Emit (EmitContext ec)
128                 {
129                         if (!IsByRef) {
130                                 Expr.Emit (ec);
131                                 return;
132                         }
133
134                         AddressOp mode = AddressOp.Store;
135                         if (ArgType == AType.Ref)
136                                 mode |= AddressOp.Load;
137
138                         IMemoryLocation ml = (IMemoryLocation) Expr;
139                         ml.AddressOf (ec, mode);
140                 }
141
142                 public Argument Clone (CloneContext clonectx)
143                 {
144                         Argument a = (Argument) MemberwiseClone ();
145                         a.Expr = Expr.Clone (clonectx);
146                         return a;
147                 }
148         }
149
150         public class NamedArgument : Argument
151         {
152                 public readonly string Name;
153                 readonly Location loc;
154                 LocalTemporary variable;
155
156                 public NamedArgument (string name, Location loc, Expression expr)
157                         : this (name, loc, expr, AType.None)
158                 {
159                 }
160
161                 public NamedArgument (string name, Location loc, Expression expr, AType modifier)
162                         : base (expr, modifier)
163                 {
164                         this.Name = name;
165                         this.loc = loc;
166                 }
167
168                 public override Expression CreateExpressionTree (ResolveContext ec)
169                 {
170                         ec.Report.Error (853, loc, "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                         var type = Expr.Type;
187                         if (IsByRef) {
188                                 var ml = (IMemoryLocation) Expr;
189                                 ml.AddressOf (ec, AddressOp.Load);
190                                 type = ReferenceContainer.MakeType (type);
191                         } else {
192                                 Expr.Emit (ec);
193                         }
194
195                         variable = new LocalTemporary (type);
196                         variable.Store (ec);
197
198                         Expr = variable;
199                 }
200
201                 public Location Location {
202                         get { return loc; }
203                 }
204         }
205         
206         public class Arguments
207         {
208                 List<Argument> args;
209                 List<NamedArgument> reordered;
210
211                 public Arguments (int capacity)
212                 {
213                         args = new List<Argument> (capacity);
214                 }
215
216                 public void Add (Argument arg)
217                 {
218                         args.Add (arg);
219                 }
220
221                 public void AddRange (Arguments args)
222                 {
223                         this.args.AddRange (args.args);
224                 }
225
226                 public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
227                 {
228                         Location loc = Location.Null;
229                         var all = new ArrayInitializer (args.Count, loc);
230
231                         MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
232
233                         foreach (Argument a in args) {
234                                 Arguments dargs = new Arguments (2);
235
236                                 // CSharpArgumentInfoFlags.None = 0
237                                 const string info_flags_enum = "CSharpArgumentInfoFlags";
238                                 Expression info_flags = new IntLiteral (0, loc);
239
240                                 var constant = a.Expr as Constant;
241                                 if (constant != null && constant.IsLiteral) {
242                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
243                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc), loc);
244                                 } else if (a.ArgType == Argument.AType.Ref) {
245                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
246                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc), loc);
247                                 } else if (a.ArgType == Argument.AType.Out) {
248                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
249                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc), loc);
250                                 } else if (a.ArgType == Argument.AType.DynamicTypeName) {
251                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
252                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc), loc);
253                                 }
254
255                                 var arg_type = a.Expr.Type;
256
257                                 if (arg_type != InternalType.Dynamic) {
258                                         MethodGroupExpr mg = a.Expr as MethodGroupExpr;
259                                         if (mg != null) {
260                                                 rc.Report.Error (1976, a.Expr.Location,
261                                                         "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
262                                                         mg.Name);
263                                         } else if (arg_type == InternalType.AnonymousMethod) {
264                                                 rc.Report.Error (1977, a.Expr.Location,
265                                                         "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
266                                         } else if (arg_type == TypeManager.void_type || arg_type == InternalType.Arglist || arg_type.IsPointer) {
267                                                 rc.Report.Error (1978, a.Expr.Location,
268                                                         "An expression of type `{0}' cannot be used as an argument of dynamic operation",
269                                                         TypeManager.CSharpName (arg_type));
270                                         }
271
272                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
273                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
274                                 }
275
276                                 string named_value;
277                                 NamedArgument na = a as NamedArgument;
278                                 if (na != null) {
279                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
280                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc), loc);
281
282                                         named_value = na.Name;
283                                 } else {
284                                         named_value = null;
285                                 }
286
287                                 dargs.Add (new Argument (info_flags));
288                                 dargs.Add (new Argument (new StringLiteral (named_value, loc)));
289                                 all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
290                         }
291
292                         return all;
293                 }
294
295                 public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
296                 {
297                         Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
298                         for (int i = 0; i < e.Length; ++i) {
299                                 if (e [i] != null)
300                                         all.Add (new Argument (e[i]));
301                         }
302
303                         if (args != null) {
304                                 foreach (Argument a in args.args) {
305                                         Expression tree_arg = a.CreateExpressionTree (ec);
306                                         if (tree_arg != null)
307                                                 all.Add (new Argument (tree_arg));
308                                 }
309                         }
310
311                         return all;
312                 }
313
314                 public void CheckArrayAsAttribute (CompilerContext ctx)
315                 {
316                         foreach (Argument arg in args) {
317                                 // Type is undefined (was error 246)
318                                 if (arg.Type == null)
319                                         continue;
320
321                                 if (arg.Type.IsArray)
322                                         ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
323                         }
324                 }
325
326                 public Arguments Clone (CloneContext ctx)
327                 {
328                         Arguments cloned = new Arguments (args.Count);
329                         foreach (Argument a in args)
330                                 cloned.Add (a.Clone (ctx));
331
332                         return cloned;
333                 }
334
335                 public int Count {
336                         get { return args.Count; }
337                 }
338
339                 //
340                 // Emits a list of resolved Arguments
341                 // 
342                 public void Emit (EmitContext ec)
343                 {
344                         Emit (ec, false, null);
345                 }
346
347                 //
348                 // if `dup_args' is true, a copy of the arguments will be left
349                 // on the stack. If `dup_args' is true, you can specify `this_arg'
350                 // which will be duplicated before any other args. Only EmitCall
351                 // should be using this interface.
352                 //
353                 public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
354                 {
355                         LocalTemporary[] temps = null;
356
357                         if (dup_args && Count != 0)
358                                 temps = new LocalTemporary [Count];
359
360                         if (reordered != null && Count > 1) {
361                                 foreach (NamedArgument na in reordered)
362                                         na.EmitAssign (ec);
363                         }
364
365                         int i = 0;
366                         foreach (Argument a in args) {
367                                 a.Emit (ec);
368                                 if (dup_args) {
369                                         ec.Emit (OpCodes.Dup);
370                                         (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
371                                 }
372                         }
373
374                         if (dup_args) {
375                                 if (this_arg != null)
376                                         this_arg.Emit (ec);
377
378                                 for (i = 0; i < temps.Length; i++) {
379                                         temps[i].Emit (ec);
380                                         temps[i].Release (ec);
381                                 }
382                         }
383                 }
384
385                 public List<Argument>.Enumerator GetEnumerator ()
386                 {
387                         return args.GetEnumerator ();
388                 }
389
390                 //
391                 // At least one argument is of dynamic type
392                 //
393                 public bool HasDynamic {
394                         get {
395                                 foreach (Argument a in args) {
396                                         if (a.Type == InternalType.Dynamic)
397                                                 return true;
398                                 }
399                                 
400                                 return false;
401                         }
402                 }
403
404                 public void Insert (int index, Argument arg)
405                 {
406                         args.Insert (index, arg);
407                 }
408
409                 public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
410                 {
411                         if (args == null || args.Count == 0)
412                                 return null;
413
414                         var exprs = new System.Linq.Expressions.Expression [args.Count];
415                         for (int i = 0; i < exprs.Length; ++i) {
416                                 Argument a = args.args [i];
417                                 exprs[i] = a.Expr.MakeExpression (ctx);
418                         }
419
420                         return exprs;
421                 }
422
423                 public void MarkReorderedArgument (NamedArgument a)
424                 {
425                         //
426                         // Constant expression can have no effect on left-to-right execution
427                         //
428                         if (a.Expr is Constant)
429                                 return;
430
431                         if (reordered == null)
432                                 reordered = new List<NamedArgument> ();
433
434                         reordered.Add (a);
435                 }
436
437                 //
438                 // Returns dynamic when at least one argument is of dynamic type
439                 //
440                 public void Resolve (ResolveContext ec, out bool dynamic)
441                 {
442                         dynamic = false;
443                         foreach (Argument a in args) {
444                                 a.Resolve (ec);
445                                 dynamic |= a.Type == InternalType.Dynamic;
446                         }
447                 }
448
449                 public void RemoveAt (int index)
450                 {
451                         args.RemoveAt (index);
452                 }
453
454                 public Argument this [int index] {
455                         get { return args [index]; }
456                         set { args [index] = value; }
457                 }
458         }
459 }