2 // argument.cs: Argument expressions
5 // Miguel de Icaza (miguel@ximain.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003-2011 Novell, Inc.
10 // Copyright 2011 Xamarin Inc
14 using System.Collections.Generic;
17 using IKVM.Reflection.Emit;
19 using System.Reflection.Emit;
25 // Argument expression used for invocation
29 public enum AType : byte
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
38 // Conditional instance expression inserted as the first argument
39 ExtensionTypeConditionalAccess = 5 | ConditionalAccessFlag,
41 ConditionalAccessFlag = 1 << 7
44 public readonly AType ArgType;
45 public Expression Expr;
47 public Argument (Expression expr, AType type)
53 public Argument (Expression expr)
61 get { return ArgType == AType.Ref || ArgType == AType.Out; }
64 public bool IsDefaultArgument {
65 get { return ArgType == AType.Default; }
68 public bool IsExtensionType {
70 return (ArgType & AType.ExtensionType) == AType.ExtensionType;
74 public Parameter.Modifier Modifier {
78 return Parameter.Modifier.OUT;
81 return Parameter.Modifier.REF;
84 return Parameter.Modifier.NONE;
89 public TypeSpec Type {
90 get { return Expr.Type; }
95 public Argument Clone (Expression expr)
97 Argument a = (Argument) MemberwiseClone ();
102 public Argument Clone (CloneContext clonectx)
104 return Clone (Expr.Clone (clonectx));
107 public virtual Expression CreateExpressionTree (ResolveContext rc)
109 if (Type.Kind == MemberKind.ByRef) {
110 rc.Report.Error (8153, Expr.Location, "An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference");
114 if (ArgType == AType.Default)
115 rc.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
117 return Expr.CreateExpressionTree (rc);
121 public virtual void Emit (EmitContext ec)
124 if (ArgType == AType.ExtensionTypeConditionalAccess) {
125 var ie = new InstanceEmitter (Expr, false);
134 if (Expr.Type.Kind == MemberKind.ByRef) {
139 AddressOp mode = AddressOp.Store;
140 if (ArgType == AType.Ref)
141 mode |= AddressOp.Load;
143 ((IMemoryLocation)Expr).AddressOf (ec, mode);
146 public Argument EmitToField (EmitContext ec, bool cloneResult)
148 var res = Expr.EmitToField (ec);
149 if (cloneResult && res != Expr)
150 return new Argument (res, ArgType);
156 public void FlowAnalysis (FlowAnalysisContext fc)
158 if (ArgType == AType.Out) {
159 var vr = Expr as VariableReference;
161 if (vr.VariableInfo != null)
162 fc.SetVariableAssigned (vr.VariableInfo);
167 var fe = Expr as FieldExpr;
169 fe.SetFieldAssigned (fc);
176 Expr.FlowAnalysis (fc);
179 public string GetSignatureForError ()
181 if (Expr.eclass == ExprClass.MethodGroup)
182 return Expr.ExprClassName;
184 return Expr.Type.GetSignatureForError ();
187 public bool ResolveMethodGroup (ResolveContext ec)
189 SimpleName sn = Expr as SimpleName;
191 Expr = sn.GetMethodGroup ();
193 // FIXME: csc doesn't report any error if you try to use `ref' or
194 // `out' in a delegate creation expression.
195 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
202 public void Resolve (ResolveContext ec)
204 // Verify that the argument is readable
205 if (ArgType != AType.Out)
206 Expr = Expr.Resolve (ec);
208 // Verify that the argument is writeable
209 if (Expr != null && IsByRef)
210 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
213 Expr = ErrorExpression.Instance;
217 public class MovableArgument : Argument
219 LocalTemporary variable;
221 public MovableArgument (Argument arg)
222 : this (arg.Expr, arg.ArgType)
226 protected MovableArgument (Expression expr, AType modifier)
227 : base (expr, modifier)
231 public override void Emit (EmitContext ec)
233 // TODO: Should guard against multiple emits
236 // Release temporary variable when used
237 if (variable != null)
238 variable.Release (ec);
241 public void EmitToVariable (EmitContext ec)
243 var type = Expr.Type;
245 var ml = (IMemoryLocation) Expr;
246 ml.AddressOf (ec, AddressOp.LoadStore);
247 type = ReferenceContainer.MakeType (ec.Module, type);
252 variable = new LocalTemporary (type);
259 public class NamedArgument : MovableArgument
261 public readonly string Name;
262 readonly Location loc;
264 public NamedArgument (string name, Location loc, Expression expr)
265 : this (name, loc, expr, AType.None)
269 public NamedArgument (string name, Location loc, Expression expr, AType modifier)
270 : base (expr, modifier)
276 public override Expression CreateExpressionTree (ResolveContext ec)
278 ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
279 return base.CreateExpressionTree (ec);
282 public Location Location {
287 public class Arguments
289 sealed class ArgumentsOrdered : Arguments
291 readonly List<MovableArgument> ordered;
293 public ArgumentsOrdered (Arguments args)
297 ordered = new List<MovableArgument> ();
300 public void AddOrdered (MovableArgument arg)
305 public override void FlowAnalysis (FlowAnalysisContext fc, List<MovableArgument> movable = null)
307 foreach (var arg in ordered) {
308 if (arg.ArgType != Argument.AType.Out)
309 arg.FlowAnalysis (fc);
312 base.FlowAnalysis (fc, ordered);
315 public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
317 foreach (var a in ordered) {
319 a.EmitToField (ec, false);
321 a.EmitToVariable (ec);
324 return base.Emit (ec, dup_args, prepareAwait);
328 // Try not to add any more instances to this class, it's allocated a lot
331 public Arguments (int capacity)
333 args = new List<Argument> (capacity);
336 private Arguments (List<Argument> args)
341 public void Add (Argument arg)
346 public void AddRange (Arguments args)
348 this.args.AddRange (args.args);
351 public bool ContainsEmitWithAwait ()
353 foreach (var arg in args) {
354 if (arg.Expr.ContainsEmitWithAwait ())
361 public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
363 Location loc = Location.Null;
364 var all = new ArrayInitializer (args.Count, loc);
366 MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
368 foreach (Argument a in args) {
369 Arguments dargs = new Arguments (2);
371 // CSharpArgumentInfoFlags.None = 0
372 const string info_flags_enum = "CSharpArgumentInfoFlags";
373 Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc);
375 if (a.Expr is Constant) {
376 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
377 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc));
378 } else if (a.ArgType == Argument.AType.Ref) {
379 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
380 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc));
381 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
382 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
383 } else if (a.ArgType == Argument.AType.Out) {
384 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
385 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc));
386 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
387 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
388 } else if (a.ArgType == Argument.AType.DynamicTypeName) {
389 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
390 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc));
393 var arg_type = a.Expr.Type;
395 if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) {
396 MethodGroupExpr mg = a.Expr as MethodGroupExpr;
398 rc.Report.Error (1976, a.Expr.Location,
399 "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
401 } else if (arg_type == InternalType.AnonymousMethod) {
402 rc.Report.Error (1977, a.Expr.Location,
403 "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
404 } else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) {
405 rc.Report.Error (1978, a.Expr.Location,
406 "An expression of type `{0}' cannot be used as an argument of dynamic operation",
407 arg_type.GetSignatureForError ());
410 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
411 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc));
415 NamedArgument na = a as NamedArgument;
417 info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
418 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc));
420 named_value = na.Name;
425 dargs.Add (new Argument (info_flags));
426 dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc)));
427 all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
433 public static Arguments CreateForExpressionTree (ResolveContext rc, Arguments args, params Expression[] e)
435 Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
436 for (int i = 0; i < e.Length; ++i) {
439 all.Add (new Argument (expr));
444 foreach (Argument a in args.args) {
445 Expression tree_arg = a.CreateExpressionTree (rc);
446 if (tree_arg != null)
447 all.Add (new Argument (tree_arg));
454 public void CheckArrayAsAttribute (CompilerContext ctx)
456 foreach (Argument arg in args) {
457 // Type is undefined (was error 246)
458 if (arg.Type == null)
461 if (arg.Type.IsArray)
462 ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
466 public Arguments Clone (CloneContext ctx)
468 Arguments cloned = new Arguments (args.Count);
469 foreach (Argument a in args)
470 cloned.Add (a.Clone (ctx));
476 get { return args.Count; }
480 // Emits a list of resolved Arguments
482 public void Emit (EmitContext ec)
484 Emit (ec, false, false);
487 public void EmitPrepare (EmitContext ec)
489 foreach (var a in args) {
490 a.Expr.EmitPrepare (ec);
495 // if `dup_args' is true or any of arguments contains await.
496 // A copy of all arguments will be returned to the caller
498 public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
502 if ((dup_args && Count != 0) || prepareAwait)
503 dups = new List<Argument> (Count);
508 foreach (Argument a in args) {
510 dups.Add (a.EmitToField (ec, true));
520 if (a.Expr.IsSideEffectFree) {
522 // No need to create a temporary variable for side effect free expressions. I assume
523 // all side-effect free expressions are cheap, this has to be tweaked when we become
524 // more aggressive on detection
528 ec.Emit (OpCodes.Dup);
530 // TODO: Release local temporary on next Emit
531 // Need to add a flag to argument to indicate this
532 lt = new LocalTemporary (a.Type);
535 dups.Add (new Argument (lt, a.ArgType));
540 return new Arguments (dups);
545 public virtual void FlowAnalysis (FlowAnalysisContext fc, List<MovableArgument> movable = null)
547 bool has_out = false;
548 foreach (var arg in args) {
549 if (arg.ArgType == Argument.AType.Out) {
554 if (movable == null) {
555 arg.FlowAnalysis (fc);
559 var ma = arg as MovableArgument;
560 if (ma != null && !movable.Contains (ma))
561 arg.FlowAnalysis (fc);
567 foreach (var arg in args) {
568 if (arg.ArgType != Argument.AType.Out)
571 arg.FlowAnalysis (fc);
575 public List<Argument>.Enumerator GetEnumerator ()
577 return args.GetEnumerator ();
581 // At least one argument is of dynamic type
583 public bool HasDynamic {
585 foreach (Argument a in args) {
586 if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
595 // At least one argument is named argument
597 public bool HasNamed {
599 foreach (Argument a in args) {
600 if (a is NamedArgument)
609 public void Insert (int index, Argument arg)
611 args.Insert (index, arg);
614 public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
616 if (args == null || args.Count == 0)
619 var exprs = new System.Linq.Expressions.Expression [args.Count];
620 for (int i = 0; i < exprs.Length; ++i) {
621 Argument a = args.args [i];
622 exprs[i] = a.Expr.MakeExpression (ctx);
629 // For named arguments when the order of execution is different
630 // to order of invocation
632 public Arguments MarkOrderedArgument (NamedArgument a)
635 // An expression has no effect on left-to-right execution
637 if (a.Expr.IsSideEffectFree)
640 ArgumentsOrdered ra = this as ArgumentsOrdered;
642 ra = new ArgumentsOrdered (this);
644 for (int i = 0; i < args.Count; ++i) {
650 // When the argument is filled later by default expression
655 var ma = la as MovableArgument;
657 ma = new MovableArgument (la);
670 // Returns dynamic when at least one argument is of dynamic type
672 public void Resolve (ResolveContext rc, out bool dynamic)
676 List<LocalVariable> var_locals = null;
677 foreach (Argument a in args) {
680 if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef) {
685 if (a.Type == InternalType.VarOutType) {
686 var de = a.Expr as DeclarationExpression;
688 if (var_locals == null)
689 var_locals = new List<LocalVariable> ();
691 var_locals.Add (de.Variable);
695 var lvr = a.Expr as LocalVariableReference;
696 if (lvr != null && var_locals != null && var_locals.Contains (lvr.local_info)) {
697 rc.Report.Error (8196, lvr.Location, "Reference to an implicitly typed out variable `{0}' is not permitted in the same argument list", lvr.Name);
698 lvr.Type = InternalType.ErrorType;
704 public void RemoveAt (int index)
706 args.RemoveAt (index);
709 public Argument this [int index] {
710 get { return args [index]; }
711 set { args [index] = value; }