PageRenderTime 90ms CodeModel.GetById 63ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/NRefactory/ICSharpCode.NRefactory.CSharp/Parser/mcs/argument.cs

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