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-2008 Novell, Inc.
13 using System
.Collections
;
14 using System
.Reflection
;
15 using System
.Reflection
.Emit
;
20 // Argument expression used for invocation
24 public enum AType
: byte
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
33 public readonly AType ArgType
;
34 public Expression Expr
;
36 public Argument (Expression expr
, AType type
)
42 public Argument (Expression expr
)
45 throw new ArgumentNullException ();
51 get { return Expr.Type; }
54 public Parameter
.Modifier Modifier
{
58 return Parameter
.Modifier
.OUT
;
61 return Parameter
.Modifier
.REF
;
64 return Parameter
.Modifier
.NONE
;
69 public virtual Expression
CreateExpressionTree (ResolveContext ec
)
71 if (ArgType
== AType
.Default
)
72 ec
.Report
.Error (854, Expr
.Location
, "An expression tree cannot contain an invocation which uses optional parameter");
74 return Expr
.CreateExpressionTree (ec
);
77 public string GetSignatureForError ()
79 if (Expr
.eclass
== ExprClass
.MethodGroup
)
80 return Expr
.ExprClassName
;
82 return TypeManager
.CSharpName (Expr
.Type
);
86 get { return ArgType == AType.Ref || ArgType == AType.Out; }
89 public bool IsDefaultArgument
{
90 get { return ArgType == AType.Default; }
93 public bool ResolveMethodGroup (ResolveContext ec
)
95 SimpleName sn
= Expr
as SimpleName
;
97 Expr
= sn
.GetMethodGroup ();
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
);
108 public void Resolve (ResolveContext ec
)
110 if (Expr
== EmptyExpression
.Null
)
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
);
118 // Verify that the argument is writeable
119 if (Expr
!= null && IsByRef
)
120 Expr
= Expr
.ResolveLValue (ec
, EmptyExpression
.OutAccess
.Instance
);
123 Expr
= EmptyExpression
.Null
;
127 public virtual void Emit (EmitContext ec
)
134 AddressOp mode
= AddressOp
.Store
;
135 if (ArgType
== AType
.Ref
)
136 mode
|= AddressOp
.Load
;
138 IMemoryLocation ml
= (IMemoryLocation
) Expr
;
139 ParameterReference pr
= ml
as ParameterReference
;
142 // ParameterReferences might already be references, so we want
143 // to pass just the value
145 if (pr
!= null && pr
.IsRef
)
148 ml
.AddressOf (ec
, mode
);
151 public Argument
Clone (CloneContext clonectx
)
153 Argument a
= (Argument
) MemberwiseClone ();
154 a
.Expr
= Expr
.Clone (clonectx
);
159 public class NamedArgument
: Argument
161 public readonly LocatedToken Name
;
162 LocalTemporary variable
;
164 public NamedArgument (LocatedToken name
, Expression expr
)
170 public override Expression
CreateExpressionTree (ResolveContext ec
)
172 ec
.Report
.Error (853, Name
.Location
, "An expression tree cannot contain named argument");
173 return base.CreateExpressionTree (ec
);
176 public override void Emit (EmitContext ec
)
178 // TODO: Should guard against multiple emits
181 // Release temporary variable when used
182 if (variable
!= null)
183 variable
.Release (ec
);
186 public void EmitAssign (EmitContext ec
)
189 variable
= new LocalTemporary (Expr
.Type
);
196 public class Arguments
198 ArrayList args
; // TODO: This should really be linked list
199 ArrayList reordered
; // TODO: LinkedList
202 public Arguments (int capacity
)
204 args
= new ArrayList (capacity
);
207 public int Add (Argument arg
)
209 return args
.Add (arg
);
212 public void AddRange (Arguments args
)
214 this.args
.AddRange (args
.args
);
217 public ArrayList
CreateDynamicBinderArguments (ResolveContext rc
)
219 ArrayList all
= new ArrayList (args
.Count
);
220 Location loc
= Location
.Null
;
222 MemberAccess binder
= DynamicExpressionStatement
.GetBinderNamespace (loc
);
224 foreach (Argument a
in args
) {
225 Arguments dargs
= new Arguments (2);
227 // CSharpArgumentInfoFlags.None = 0
228 const string info_flags_enum
= "CSharpArgumentInfoFlags";
229 Expression info_flags
= new IntLiteral (0, loc
);
231 var constant
= a
.Expr
as Constant
;
232 if (constant
!= null && constant
.IsLiteral
) {
233 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
234 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "LiteralConstant", loc
));
235 } else if (a
.ArgType
== Argument
.AType
.Ref
) {
236 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
237 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsRef", loc
));
238 } else if (a
.ArgType
== Argument
.AType
.Out
) {
239 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
240 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsOut", loc
));
241 } else if (a
.ArgType
== Argument
.AType
.DynamicTypeName
) {
242 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
243 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsStaticType", loc
));
246 var arg_type
= a
.Expr
.Type
;
248 if (!TypeManager
.IsDynamicType (arg_type
)) {
249 MethodGroupExpr mg
= a
.Expr
as MethodGroupExpr
;
251 rc
.Report
.Error (1976, a
.Expr
.Location
,
252 "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
254 } else if (arg_type
== InternalType
.AnonymousMethod
) {
255 rc
.Report
.Error (1977, a
.Expr
.Location
,
256 "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
257 } else if (arg_type
== TypeManager
.void_type
|| arg_type
== InternalType
.Arglist
|| arg_type
.IsPointer
) {
258 rc
.Report
.Error (1978, a
.Expr
.Location
,
259 "An expression of type `{0}' cannot be used as an argument of dynamic operation",
260 TypeManager
.CSharpName (arg_type
));
263 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
264 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "UseCompileTimeType", loc
));
268 NamedArgument na
= a
as NamedArgument
;
270 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
271 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "NamedArgument", loc
));
273 named_value
= na
.Name
.Value
;
278 dargs
.Add (new Argument (info_flags
));
279 dargs
.Add (new Argument (new StringLiteral (named_value
, loc
)));
280 all
.Add (new Invocation (new MemberAccess (new MemberAccess (binder
, "CSharpArgumentInfo", loc
), "Create", loc
), dargs
));
286 public static Arguments
CreateForExpressionTree (ResolveContext ec
, Arguments args
, params Expression
[] e
)
288 Arguments all
= new Arguments ((args
== null ? 0 : args
.Count
) + e
.Length
);
289 for (int i
= 0; i
< e
.Length
; ++i
) {
291 all
.Add (new Argument (e
[i
]));
295 foreach (Argument a
in args
.args
) {
296 Expression tree_arg
= a
.CreateExpressionTree (ec
);
297 if (tree_arg
!= null)
298 all
.Add (new Argument (tree_arg
));
305 public void CheckArrayAsAttribute (CompilerContext ctx
)
307 foreach (Argument arg
in args
) {
308 // Type is undefined (was error 246)
309 if (arg
.Type
== null)
312 if (arg
.Type
.IsArray
)
313 ctx
.Report
.Warning (3016, 1, arg
.Expr
.Location
, "Arrays as attribute arguments are not CLS-compliant");
317 public Arguments
Clone (CloneContext ctx
)
319 Arguments cloned
= new Arguments (args
.Count
);
320 foreach (Argument a
in args
)
321 cloned
.Add (a
.Clone (ctx
));
327 get { return args.Count; }
331 // Emits a list of resolved Arguments
333 public void Emit (EmitContext ec
)
335 Emit (ec
, false, null);
339 // if `dup_args' is true, a copy of the arguments will be left
340 // on the stack. If `dup_args' is true, you can specify `this_arg'
341 // which will be duplicated before any other args. Only EmitCall
342 // should be using this interface.
344 public void Emit (EmitContext ec
, bool dup_args
, LocalTemporary this_arg
)
346 LocalTemporary
[] temps
= null;
348 if (dup_args
&& Count
!= 0)
349 temps
= new LocalTemporary
[Count
];
351 if (reordered
!= null && Count
> 1) {
352 foreach (NamedArgument na
in reordered
)
357 foreach (Argument a
in args
) {
360 ec
.ig
.Emit (OpCodes
.Dup
);
361 (temps
[i
++] = new LocalTemporary (a
.Type
)).Store (ec
);
366 if (this_arg
!= null)
369 for (i
= 0; i
< temps
.Length
; i
++) {
371 temps
[i
].Release (ec
);
376 public bool GetAttributableValue (ResolveContext ec
, out object[] values
)
378 values
= new object [args
.Count
];
379 for (int j
= 0; j
< values
.Length
; ++j
) {
380 Argument a
= this [j
];
381 if (!a
.Expr
.GetAttributableValue (ec
, a
.Type
, out values
[j
]))
388 public IEnumerator
GetEnumerator ()
390 return args
.GetEnumerator ();
394 // At least one argument is of dynamic type
396 public bool HasDynamic
{
402 public void Insert (int index
, Argument arg
)
404 args
.Insert (index
, arg
);
408 public static System
.Linq
.Expressions
.Expression
[] MakeExpression (Arguments args
, BuilderContext ctx
)
410 if (args
== null || args
.Count
== 0)
413 var exprs
= new System
.Linq
.Expressions
.Expression
[args
.Count
];
414 for (int i
= 0; i
< exprs
.Length
; ++i
) {
415 Argument a
= (Argument
) args
.args
[i
];
416 exprs
[i
] = a
.Expr
.MakeExpression (ctx
);
423 public void MarkReorderedArgument (NamedArgument a
)
426 // Constant expression can have no effect on left-to-right execution
428 if (a
.Expr
is Constant
)
431 if (reordered
== null)
432 reordered
= new ArrayList ();
438 // Returns dynamic when at least one argument is of dynamic type
440 public void Resolve (ResolveContext ec
, out bool dynamic)
442 foreach (Argument a
in args
) {
444 this.has_dynamic
|= TypeManager
.IsDynamicType (a
.Type
);
447 dynamic = has_dynamic
;
450 public void MutateHoistedGenericType (AnonymousMethodStorey storey
)
452 foreach (Argument a
in args
)
453 a
.Expr
.MutateHoistedGenericType (storey
);
456 public void RemoveAt (int index
)
458 args
.RemoveAt (index
);
461 public Argument
this [int index
] {
462 get { return (Argument) args [index]; }
463 set { args [index] = value; }