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 DynamicStatic
= 4 // static 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
);
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
201 public Arguments (int capacity
)
203 args
= new ArrayList (capacity
);
206 public int Add (Argument arg
)
208 return args
.Add (arg
);
211 public void AddRange (Arguments args
)
213 this.args
.AddRange (args
.args
);
216 public ArrayList
CreateDynamicBinderArguments ()
218 ArrayList all
= new ArrayList (args
.Count
);
219 Location loc
= Location
.Null
;
221 MemberAccess binder
= DynamicExpressionStatement
.GetBinderNamespace (loc
);
223 foreach (Argument a
in args
) {
224 Arguments dargs
= new Arguments (2);
226 // CSharpArgumentInfoFlags.None = 0
227 const string info_flags_enum
= "CSharpArgumentInfoFlags";
228 Expression info_flags
= new IntLiteral (0, loc
);
230 if (a
.Expr
is Constant
) {
231 // Any constant is emitted as a literal
232 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
233 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "LiteralConstant", loc
));
234 } else if (a
.ArgType
== Argument
.AType
.Ref
) {
235 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
236 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsRef", loc
));
237 } else if (a
.ArgType
== Argument
.AType
.Out
) {
238 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
239 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsOut", loc
));
240 } else if (a
.ArgType
== Argument
.AType
.DynamicStatic
) {
241 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
242 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "IsStaticType", loc
));
245 if (!TypeManager
.IsDynamicType (a
.Expr
.Type
)) {
246 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
247 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "UseCompileTimeType", loc
));
251 NamedArgument na
= a
as NamedArgument
;
253 info_flags
= new Binary (Binary
.Operator
.BitwiseOr
, info_flags
,
254 new MemberAccess (new MemberAccess (binder
, info_flags_enum
, loc
), "NamedArgument", loc
));
256 named_value
= na
.Name
.Value
;
261 dargs
.Add (new Argument (info_flags
));
262 dargs
.Add (new Argument (new StringLiteral (named_value
, loc
)));
263 all
.Add (new New (new MemberAccess (binder
, "CSharpArgumentInfo", loc
), dargs
, loc
));
269 public static Arguments
CreateForExpressionTree (ResolveContext ec
, Arguments args
, params Expression
[] e
)
271 Arguments all
= new Arguments ((args
== null ? 0 : args
.Count
) + e
.Length
);
272 for (int i
= 0; i
< e
.Length
; ++i
) {
274 all
.Add (new Argument (e
[i
]));
278 foreach (Argument a
in args
.args
) {
279 Expression tree_arg
= a
.CreateExpressionTree (ec
);
280 if (tree_arg
!= null)
281 all
.Add (new Argument (tree_arg
));
288 public void CheckArrayAsAttribute (CompilerContext ctx
)
290 foreach (Argument arg
in args
) {
291 // Type is undefined (was error 246)
292 if (arg
.Type
== null)
295 if (arg
.Type
.IsArray
)
296 ctx
.Report
.Warning (3016, 1, arg
.Expr
.Location
, "Arrays as attribute arguments are not CLS-compliant");
300 public Arguments
Clone (CloneContext ctx
)
302 Arguments cloned
= new Arguments (args
.Count
);
303 foreach (Argument a
in args
)
304 cloned
.Add (a
.Clone (ctx
));
310 get { return args.Count; }
314 // Emits a list of resolved Arguments
316 public void Emit (EmitContext ec
)
318 Emit (ec
, false, null);
322 // if `dup_args' is true, a copy of the arguments will be left
323 // on the stack. If `dup_args' is true, you can specify `this_arg'
324 // which will be duplicated before any other args. Only EmitCall
325 // should be using this interface.
327 public void Emit (EmitContext ec
, bool dup_args
, LocalTemporary this_arg
)
329 LocalTemporary
[] temps
= null;
331 if (dup_args
&& Count
!= 0)
332 temps
= new LocalTemporary
[Count
];
334 if (reordered
!= null && Count
> 1) {
335 foreach (NamedArgument na
in reordered
)
340 foreach (Argument a
in args
) {
343 ec
.ig
.Emit (OpCodes
.Dup
);
344 (temps
[i
++] = new LocalTemporary (a
.Type
)).Store (ec
);
349 if (this_arg
!= null)
352 for (i
= 0; i
< temps
.Length
; i
++) {
354 temps
[i
].Release (ec
);
359 public bool GetAttributableValue (ResolveContext ec
, out object[] values
)
361 values
= new object [args
.Count
];
362 for (int j
= 0; j
< values
.Length
; ++j
) {
363 Argument a
= this [j
];
364 if (!a
.Expr
.GetAttributableValue (ec
, a
.Type
, out values
[j
]))
371 public IEnumerator
GetEnumerator ()
373 return args
.GetEnumerator ();
376 public void Insert (int index
, Argument arg
)
378 args
.Insert (index
, arg
);
382 public static System
.Linq
.Expressions
.Expression
[] MakeExpression (Arguments args
, BuilderContext ctx
)
384 if (args
== null || args
.Count
== 0)
388 if (args
.reordered
!= null)
389 throw new NotImplementedException ();
391 var exprs
= new System
.Linq
.Expressions
.Expression
[args
.Count
];
392 for (int i
= 0; i
< exprs
.Length
; ++i
) {
393 Argument a
= (Argument
) args
.args
[i
];
394 exprs
[i
] = a
.Expr
.MakeExpression (ctx
);
401 public void MarkReorderedArgument (NamedArgument a
)
404 // Constant expression can have no effect on left-to-right execution
406 if (a
.Expr
is Constant
)
409 if (reordered
== null)
410 reordered
= new ArrayList ();
416 // Returns dynamic when at least one argument is of dynamic type
418 public void Resolve (ResolveContext ec
, out bool dynamic)
421 foreach (Argument a
in args
) {
423 dynamic |= TypeManager
.IsDynamicType (a
.Type
);
427 public void MutateHoistedGenericType (AnonymousMethodStorey storey
)
429 foreach (Argument a
in args
)
430 a
.Expr
.MutateHoistedGenericType (storey
);
433 public void RemoveAt (int index
)
435 args
.RemoveAt (index
);
438 public Argument
this [int index
] {
439 get { return (Argument) args [index]; }
440 set { args [index] = value; }