Merge branch 'master' of 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                         ExtensionType = 5,      // Instance expression inserted as the first argument
32                 }
33
34                 public readonly AType ArgType;
35                 public Expression Expr;
36
37                 public Argument (Expression expr, AType type)
38                 {
39                         this.Expr = expr;
40                         this.ArgType = type;
41                 }
42
43                 public Argument (Expression expr)
44                 {
45                         if (expr == null)
46                                 throw new ArgumentNullException ();
47
48                         this.Expr = expr;
49                 }
50
51                 public TypeSpec Type {
52                         get { return Expr.Type; }
53                 }
54
55                 public Parameter.Modifier Modifier {
56                         get {
57                                 switch (ArgType) {
58                                 case AType.Out:
59                                         return Parameter.Modifier.OUT;
60
61                                 case AType.Ref:
62                                         return Parameter.Modifier.REF;
63
64                                 default:
65                                         return Parameter.Modifier.NONE;
66                                 }
67                         }
68                 }
69
70                 public virtual Expression CreateExpressionTree (ResolveContext ec)
71                 {
72                         if (ArgType == AType.Default)
73                                 ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
74
75                         return Expr.CreateExpressionTree (ec);
76                 }
77
78                 public string GetSignatureForError ()
79                 {
80                         if (Expr.eclass == ExprClass.MethodGroup)
81                                 return Expr.ExprClassName;
82
83                         return TypeManager.CSharpName (Expr.Type);
84                 }
85
86                 public bool IsByRef {
87                         get { return ArgType == AType.Ref || ArgType == AType.Out; }
88                 }
89
90                 public bool IsDefaultArgument {
91                         get { return ArgType == AType.Default; }
92                 }
93
94                 public bool ResolveMethodGroup (ResolveContext ec)
95                 {
96                         SimpleName sn = Expr as SimpleName;
97                         if (sn != null)
98                                 Expr = sn.GetMethodGroup ();
99
100                         // FIXME: csc doesn't report any error if you try to use `ref' or
101                         //        `out' in a delegate creation expression.
102                         Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
103                         if (Expr == null)
104                                 return false;
105
106                         return true;
107                 }
108
109                 public void Resolve (ResolveContext ec)
110                 {
111                         if (Expr == EmptyExpression.Null)
112                                 return;
113
114 //                      using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
115                                 // Verify that the argument is readable
116                                 if (ArgType != AType.Out)
117                                         Expr = Expr.Resolve (ec);
118
119                                 // Verify that the argument is writeable
120                                 if (Expr != null && IsByRef)
121                                         Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess.Instance);
122
123                                 if (Expr == null)
124                                         Expr = EmptyExpression.Null;
125 //                      }
126                 }
127
128                 public virtual void Emit (EmitContext ec)
129                 {
130                         if (!IsByRef) {
131                                 Expr.Emit (ec);
132                                 return;
133                         }
134
135                         AddressOp mode = AddressOp.Store;
136                         if (ArgType == AType.Ref)
137                                 mode |= AddressOp.Load;
138
139                         IMemoryLocation ml = (IMemoryLocation) Expr;
140                         ml.AddressOf (ec, mode);
141                 }
142
143                 public Argument Clone (CloneContext clonectx)
144                 {
145                         Argument a = (Argument) MemberwiseClone ();
146                         a.Expr = Expr.Clone (clonectx);
147                         return a;
148                 }
149         }
150
151         public class NamedArgument : Argument
152         {
153                 public readonly string Name;
154                 readonly Location loc;
155                 LocalTemporary variable;
156
157                 public NamedArgument (string name, Location loc, Expression expr)
158                         : this (name, loc, expr, AType.None)
159                 {
160                 }
161
162                 public NamedArgument (string name, Location loc, Expression expr, AType modifier)
163                         : base (expr, modifier)
164                 {
165                         this.Name = name;
166                         this.loc = loc;
167                 }
168
169                 public override Expression CreateExpressionTree (ResolveContext ec)
170                 {
171                         ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
172                         return base.CreateExpressionTree (ec);
173                 }
174
175                 public override void Emit (EmitContext ec)
176                 {
177                         // TODO: Should guard against multiple emits
178                         base.Emit (ec);
179
180                         // Release temporary variable when used
181                         if (variable != null)
182                                 variable.Release (ec);
183                 }
184
185                 public void EmitAssign (EmitContext ec)
186                 {
187                         var type = Expr.Type;
188                         if (IsByRef) {
189                                 var ml = (IMemoryLocation) Expr;
190                                 ml.AddressOf (ec, AddressOp.Load);
191                                 type = ReferenceContainer.MakeType (type);
192                         } else {
193                                 Expr.Emit (ec);
194                         }
195
196                         variable = new LocalTemporary (type);
197                         variable.Store (ec);
198
199                         Expr = variable;
200                 }
201
202                 public Location Location {
203                         get { return loc; }
204                 }
205         }
206         
207         public class Arguments
208         {
209                 List<Argument> args;
210                 List<NamedArgument> reordered;
211
212                 public Arguments (int capacity)
213                 {
214                         args = new List<Argument> (capacity);
215                 }
216
217                 public void Add (Argument arg)
218                 {
219                         args.Add (arg);
220                 }
221
222                 public void AddRange (Arguments args)
223                 {
224                         this.args.AddRange (args.args);
225                 }
226
227                 public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
228                 {
229                         Location loc = Location.Null;
230                         var all = new ArrayInitializer (args.Count, loc);
231
232                         MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
233
234                         foreach (Argument a in args) {
235                                 Arguments dargs = new Arguments (2);
236
237                                 // CSharpArgumentInfoFlags.None = 0
238                                 const string info_flags_enum = "CSharpArgumentInfoFlags";
239                                 Expression info_flags = new IntLiteral (0, loc);
240
241                                 if (a.Expr is Constant) {
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 && arg_type != InternalType.Null) {
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 && !a.IsByRef)
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                                 if (a.Type == InternalType.Dynamic && !a.IsByRef)
446                                         dynamic = true;
447                         }
448                 }
449
450                 public void RemoveAt (int index)
451                 {
452                         args.RemoveAt (index);
453                 }
454
455                 public Argument this [int index] {
456                         get { return args [index]; }
457                         set { args [index] = value; }
458                 }
459         }
460 }