Merge pull request #129 from grumpydev/CryptoFixo
[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.Generic;
14
15 #if STATIC
16 using IKVM.Reflection.Emit;
17 #else
18 using System.Reflection.Emit;
19 #endif
20
21 namespace Mono.CSharp
22 {
23         //
24         // Argument expression used for invocation
25         //
26         public class Argument
27         {
28                 public enum AType : byte
29                 {
30                         None = 0,
31                         Ref = 1,                        // ref modifier used
32                         Out = 2,                        // out modifier used
33                         Default = 3,            // argument created from default parameter value
34                         DynamicTypeName = 4,    // System.Type argument for dynamic binding
35                         ExtensionType = 5,      // Instance expression inserted as the first argument
36                 }
37
38                 public readonly AType ArgType;
39                 public Expression Expr;
40
41                 public Argument (Expression expr, AType type)
42                 {
43                         this.Expr = expr;
44                         this.ArgType = type;
45                 }
46
47                 public Argument (Expression expr)
48                 {
49                         if (expr == null)
50                                 throw new ArgumentNullException ();
51
52                         this.Expr = expr;
53                 }
54
55                 #region Properties
56
57                 public bool IsByRef {
58                         get { return ArgType == AType.Ref || ArgType == AType.Out; }
59                 }
60
61                 public bool IsDefaultArgument {
62                         get { return ArgType == AType.Default; }
63                 }
64
65                 public Parameter.Modifier Modifier {
66                         get {
67                                 switch (ArgType) {
68                                 case AType.Out:
69                                         return Parameter.Modifier.OUT;
70
71                                 case AType.Ref:
72                                         return Parameter.Modifier.REF;
73
74                                 default:
75                                         return Parameter.Modifier.NONE;
76                                 }
77                         }
78                 }
79
80                 public TypeSpec Type {
81                         get { return Expr.Type; }
82                 }
83
84                 #endregion
85
86                 public Argument Clone (Expression expr)
87                 {
88                         Argument a = (Argument) MemberwiseClone ();
89                         a.Expr = expr;
90                         return a;
91                 }
92
93                 public Argument Clone (CloneContext clonectx)
94                 {
95                         return Clone (Expr.Clone (clonectx));
96                 }
97
98                 public virtual Expression CreateExpressionTree (ResolveContext ec)
99                 {
100                         if (ArgType == AType.Default)
101                                 ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
102
103                         return Expr.CreateExpressionTree (ec);
104                 }
105
106
107                 public virtual void Emit (EmitContext ec)
108                 {
109                         if (!IsByRef) {
110                                 Expr.Emit (ec);
111                                 return;
112                         }
113
114                         AddressOp mode = AddressOp.Store;
115                         if (ArgType == AType.Ref)
116                                 mode |= AddressOp.Load;
117
118                         IMemoryLocation ml = (IMemoryLocation) Expr;
119                         ml.AddressOf (ec, mode);
120                 }
121
122                 public Argument EmitToField (EmitContext ec)
123                 {
124                         var res = Expr.EmitToField (ec);
125                         return res == Expr ? this : new Argument (res, ArgType);
126                 }
127
128                 public string GetSignatureForError ()
129                 {
130                         if (Expr.eclass == ExprClass.MethodGroup)
131                                 return Expr.ExprClassName;
132
133                         return TypeManager.CSharpName (Expr.Type);
134                 }
135
136                 public bool ResolveMethodGroup (ResolveContext ec)
137                 {
138                         SimpleName sn = Expr as SimpleName;
139                         if (sn != null)
140                                 Expr = sn.GetMethodGroup ();
141
142                         // FIXME: csc doesn't report any error if you try to use `ref' or
143                         //        `out' in a delegate creation expression.
144                         Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
145                         if (Expr == null)
146                                 return false;
147
148                         return true;
149                 }
150
151                 public void Resolve (ResolveContext ec)
152                 {
153 //                      using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
154                                 // Verify that the argument is readable
155                                 if (ArgType != AType.Out)
156                                         Expr = Expr.Resolve (ec);
157
158                                 // Verify that the argument is writeable
159                                 if (Expr != null && IsByRef)
160                                         Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
161
162                                 if (Expr == null)
163                                         Expr = ErrorExpression.Instance;
164 //                      }
165                 }
166         }
167
168         public class MovableArgument : Argument
169         {
170                 LocalTemporary variable;
171
172                 public MovableArgument (Argument arg)
173                         : this (arg.Expr, arg.ArgType)
174                 {
175                 }
176
177                 protected MovableArgument (Expression expr, AType modifier)
178                         : base (expr, modifier)
179                 {
180                 }
181
182                 public override void Emit (EmitContext ec)
183                 {
184                         // TODO: Should guard against multiple emits
185                         base.Emit (ec);
186
187                         // Release temporary variable when used
188                         if (variable != null)
189                                 variable.Release (ec);
190                 }
191
192                 public void EmitToVariable (EmitContext ec)
193                 {
194                         var type = Expr.Type;
195                         if (IsByRef) {
196                                 var ml = (IMemoryLocation) Expr;
197                                 ml.AddressOf (ec, AddressOp.LoadStore);
198                                 type = ReferenceContainer.MakeType (ec.Module, type);
199                         } else {
200                                 Expr.Emit (ec);
201                         }
202
203                         variable = new LocalTemporary (type);
204                         variable.Store (ec);
205
206                         Expr = variable;
207                 }
208         }
209
210         public class NamedArgument : MovableArgument
211         {
212                 public readonly string Name;
213                 readonly Location loc;
214
215                 public NamedArgument (string name, Location loc, Expression expr)
216                         : this (name, loc, expr, AType.None)
217                 {
218                 }
219
220                 public NamedArgument (string name, Location loc, Expression expr, AType modifier)
221                         : base (expr, modifier)
222                 {
223                         this.Name = name;
224                         this.loc = loc;
225                 }
226
227                 public override Expression CreateExpressionTree (ResolveContext ec)
228                 {
229                         ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
230                         return base.CreateExpressionTree (ec);
231                 }
232
233                 public Location Location {
234                         get { return loc; }
235                 }
236         }
237         
238         public class Arguments
239         {
240                 sealed class ArgumentsOrdered : Arguments
241                 {
242                         readonly List<MovableArgument> ordered;
243
244                         public ArgumentsOrdered (Arguments args)
245                                 : base (args.Count)
246                         {
247                                 AddRange (args);
248                                 ordered = new List<MovableArgument> ();
249                         }
250
251                         public void AddOrdered (MovableArgument arg)
252                         {
253                                 ordered.Add (arg);
254                         }
255
256                         public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
257                         {
258                                 foreach (var a in ordered) {
259                                         if (prepareAwait)
260                                                 a.EmitToField (ec);
261                                         else
262                                                 a.EmitToVariable (ec);
263                                 }
264
265                                 return base.Emit (ec, dup_args, prepareAwait);
266                         }
267                 }
268
269                 // Try not to add any more instances to this class, it's allocated a lot
270                 List<Argument> args;
271
272                 public Arguments (int capacity)
273                 {
274                         args = new List<Argument> (capacity);
275                 }
276
277                 private Arguments (List<Argument> args)
278                 {
279                         this.args = args;
280                 }
281
282                 public void Add (Argument arg)
283                 {
284                         args.Add (arg);
285                 }
286
287                 public void AddRange (Arguments args)
288                 {
289                         this.args.AddRange (args.args);
290                 }
291
292                 public bool ContainsEmitWithAwait ()
293                 {
294                         foreach (var arg in args) {
295                                 if (arg.Expr.ContainsEmitWithAwait ())
296                                         return true;
297                         }
298
299                         return false;
300                 }
301
302                 public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
303                 {
304                         Location loc = Location.Null;
305                         var all = new ArrayInitializer (args.Count, loc);
306
307                         MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
308
309                         foreach (Argument a in args) {
310                                 Arguments dargs = new Arguments (2);
311
312                                 // CSharpArgumentInfoFlags.None = 0
313                                 const string info_flags_enum = "CSharpArgumentInfoFlags";
314                                 Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc);
315
316                                 if (a.Expr is Constant) {
317                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
318                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc), loc);
319                                 } else if (a.ArgType == Argument.AType.Ref) {
320                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
321                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc), loc);
322                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
323                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
324                                 } else if (a.ArgType == Argument.AType.Out) {
325                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
326                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc), loc);
327                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
328                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
329                                 } else if (a.ArgType == Argument.AType.DynamicTypeName) {
330                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
331                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc), loc);
332                                 }
333
334                                 var arg_type = a.Expr.Type;
335
336                                 if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) {
337                                         MethodGroupExpr mg = a.Expr as MethodGroupExpr;
338                                         if (mg != null) {
339                                                 rc.Report.Error (1976, a.Expr.Location,
340                                                         "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
341                                                         mg.Name);
342                                         } else if (arg_type == InternalType.AnonymousMethod) {
343                                                 rc.Report.Error (1977, a.Expr.Location,
344                                                         "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
345                                         } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) {
346                                                 rc.Report.Error (1978, a.Expr.Location,
347                                                         "An expression of type `{0}' cannot be used as an argument of dynamic operation",
348                                                         TypeManager.CSharpName (arg_type));
349                                         }
350
351                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
352                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
353                                 }
354
355                                 string named_value;
356                                 NamedArgument na = a as NamedArgument;
357                                 if (na != null) {
358                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
359                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc), loc);
360
361                                         named_value = na.Name;
362                                 } else {
363                                         named_value = null;
364                                 }
365
366                                 dargs.Add (new Argument (info_flags));
367                                 dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc)));
368                                 all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
369                         }
370
371                         return all;
372                 }
373
374                 public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
375                 {
376                         Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
377                         for (int i = 0; i < e.Length; ++i) {
378                                 if (e [i] != null)
379                                         all.Add (new Argument (e[i]));
380                         }
381
382                         if (args != null) {
383                                 foreach (Argument a in args.args) {
384                                         Expression tree_arg = a.CreateExpressionTree (ec);
385                                         if (tree_arg != null)
386                                                 all.Add (new Argument (tree_arg));
387                                 }
388                         }
389
390                         return all;
391                 }
392
393                 public void CheckArrayAsAttribute (CompilerContext ctx)
394                 {
395                         foreach (Argument arg in args) {
396                                 // Type is undefined (was error 246)
397                                 if (arg.Type == null)
398                                         continue;
399
400                                 if (arg.Type.IsArray)
401                                         ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
402                         }
403                 }
404
405                 public Arguments Clone (CloneContext ctx)
406                 {
407                         Arguments cloned = new Arguments (args.Count);
408                         foreach (Argument a in args)
409                                 cloned.Add (a.Clone (ctx));
410
411                         return cloned;
412                 }
413
414                 public int Count {
415                         get { return args.Count; }
416                 }
417
418                 //
419                 // Emits a list of resolved Arguments
420                 // 
421                 public void Emit (EmitContext ec)
422                 {
423                         Emit (ec, false, false);
424                 }
425
426                 //
427                 // if `dup_args' is true or any of arguments contains await.
428                 // A copy of all arguments will be returned to the caller
429                 //
430                 public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
431                 {
432                         List<Argument> dups;
433
434                         if ((dup_args && Count != 0) || prepareAwait)
435                                 dups = new List<Argument> (Count);
436                         else
437                                 dups = null;
438
439                         LocalTemporary lt;
440                         foreach (Argument a in args) {
441                                 if (prepareAwait) {
442                                         dups.Add (a.EmitToField (ec));
443                                         continue;
444                                 }
445                                 
446                                 a.Emit (ec);
447
448                                 if (!dup_args) {
449                                         continue;
450                                 }
451
452                                 if (a.Expr.IsSideEffectFree) {
453                                         //
454                                         // No need to create a temporary variable for side effect free expressions. I assume
455                                         // all side-effect free expressions are cheap, this has to be tweaked when we become
456                                         // more aggressive on detection
457                                         //
458                                         dups.Add (a);
459                                 } else {
460                                         ec.Emit (OpCodes.Dup);
461
462                                         // TODO: Release local temporary on next Emit
463                                         // Need to add a flag to argument to indicate this
464                                         lt = new LocalTemporary (a.Type);
465                                         lt.Store (ec);
466
467                                         dups.Add (new Argument (lt, a.ArgType));
468                                 }
469                         }
470
471                         if (dups != null)
472                                 return new Arguments (dups);
473
474                         return null;
475                 }
476
477                 public List<Argument>.Enumerator GetEnumerator ()
478                 {
479                         return args.GetEnumerator ();
480                 }
481
482                 //
483                 // At least one argument is of dynamic type
484                 //
485                 public bool HasDynamic {
486                         get {
487                                 foreach (Argument a in args) {
488                                         if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
489                                                 return true;
490                                 }
491                                 
492                                 return false;
493                         }
494                 }
495
496                 //
497                 // At least one argument is named argument
498                 //
499                 public bool HasNamed {
500                         get {
501                                 foreach (Argument a in args) {
502                                         if (a is NamedArgument)
503                                                 return true;
504                                 }
505                                 
506                                 return false;
507                         }
508                 }
509
510
511                 public void Insert (int index, Argument arg)
512                 {
513                         args.Insert (index, arg);
514                 }
515
516                 public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
517                 {
518                         if (args == null || args.Count == 0)
519                                 return null;
520
521                         var exprs = new System.Linq.Expressions.Expression [args.Count];
522                         for (int i = 0; i < exprs.Length; ++i) {
523                                 Argument a = args.args [i];
524                                 exprs[i] = a.Expr.MakeExpression (ctx);
525                         }
526
527                         return exprs;
528                 }
529
530                 //
531                 // For named arguments when the order of execution is different
532                 // to order of invocation
533                 //
534                 public Arguments MarkOrderedArgument (NamedArgument a)
535                 {
536                         //
537                         // An expression has no effect on left-to-right execution
538                         //
539                         if (a.Expr.IsSideEffectFree)
540                                 return this;
541
542                         ArgumentsOrdered ra = this as ArgumentsOrdered;
543                         if (ra == null) {
544                                 ra = new ArgumentsOrdered (this);
545
546                                 for (int i = 0; i < args.Count; ++i) {
547                                         var la = args [i];
548                                         if (la == a)
549                                                 break;
550
551                                         var ma = la as MovableArgument;
552                                         if (ma == null) {
553                                                 ma = new MovableArgument (la);
554                                                 ra.args[i] = ma;
555                                         }
556
557                                         ra.AddOrdered (ma);
558                                 }
559                         }
560
561                         ra.AddOrdered (a);
562                         return ra;
563                 }
564
565                 //
566                 // Returns dynamic when at least one argument is of dynamic type
567                 //
568                 public void Resolve (ResolveContext ec, out bool dynamic)
569                 {
570                         dynamic = false;
571                         foreach (Argument a in args) {
572                                 a.Resolve (ec);
573                                 if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
574                                         dynamic = true;
575                         }
576                 }
577
578                 public void RemoveAt (int index)
579                 {
580                         args.RemoveAt (index);
581                 }
582
583                 public Argument this [int index] {
584                         get { return args [index]; }
585                         set { args [index] = value; }
586                 }
587         }
588 }