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
39
public readonly AType ArgType;
40
public Expression Expr;
42
public Argument (Expression expr, AType type)
48
public Argument (Expression expr)
51
throw new ArgumentNullException ();
59
get { return ArgType == AType.Ref || ArgType == AType.Out; }
62
public bool IsDefaultArgument {
63
get { return ArgType == AType.Default; }
66
public Parameter.Modifier Modifier {
70
return Parameter.Modifier.OUT;
73
return Parameter.Modifier.REF;
76
return Parameter.Modifier.NONE;
81
public TypeSpec Type {
82
get { return Expr.Type; }
87
public Argument Clone (Expression expr)
89
Argument a = (Argument) MemberwiseClone ();
94
public Argument Clone (CloneContext clonectx)
96
return Clone (Expr.Clone (clonectx));
99
public virtual Expression CreateExpressionTree (ResolveContext ec)
101
if (ArgType == AType.Default)
102
ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
104
return Expr.CreateExpressionTree (ec);
108
public virtual void Emit (EmitContext ec)
115
AddressOp mode = AddressOp.Store;
116
if (ArgType == AType.Ref)
117
mode |= AddressOp.Load;
119
IMemoryLocation ml = (IMemoryLocation) Expr;
120
ml.AddressOf (ec, mode);
123
public Argument EmitToField (EmitContext ec, bool cloneResult)
125
var res = Expr.EmitToField (ec);
126
if (cloneResult && res != Expr)
127
return new Argument (res, ArgType);
133
public string GetSignatureForError ()
135
if (Expr.eclass == ExprClass.MethodGroup)
136
return Expr.ExprClassName;
138
return TypeManager.CSharpName (Expr.Type);
141
public bool ResolveMethodGroup (ResolveContext ec)
143
SimpleName sn = Expr as SimpleName;
145
Expr = sn.GetMethodGroup ();
147
// FIXME: csc doesn't report any error if you try to use `ref' or
148
// `out' in a delegate creation expression.
149
Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
156
public void Resolve (ResolveContext ec)
158
// using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
159
// Verify that the argument is readable
160
if (ArgType != AType.Out)
161
Expr = Expr.Resolve (ec);
163
// Verify that the argument is writeable
164
if (Expr != null && IsByRef)
165
Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess);
168
Expr = ErrorExpression.Instance;
173
public class MovableArgument : Argument
175
LocalTemporary variable;
177
public MovableArgument (Argument arg)
178
: this (arg.Expr, arg.ArgType)
182
protected MovableArgument (Expression expr, AType modifier)
183
: base (expr, modifier)
187
public override void Emit (EmitContext ec)
189
// TODO: Should guard against multiple emits
192
// Release temporary variable when used
193
if (variable != null)
194
variable.Release (ec);
197
public void EmitToVariable (EmitContext ec)
199
var type = Expr.Type;
201
var ml = (IMemoryLocation) Expr;
202
ml.AddressOf (ec, AddressOp.LoadStore);
203
type = ReferenceContainer.MakeType (ec.Module, type);
208
variable = new LocalTemporary (type);
215
public class NamedArgument : MovableArgument
217
public readonly string Name;
218
readonly Location loc;
220
public NamedArgument (string name, Location loc, Expression expr)
221
: this (name, loc, expr, AType.None)
225
public NamedArgument (string name, Location loc, Expression expr, AType modifier)
226
: base (expr, modifier)
232
public override Expression CreateExpressionTree (ResolveContext ec)
234
ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
235
return base.CreateExpressionTree (ec);
238
public Location Location {
243
public class Arguments
245
sealed class ArgumentsOrdered : Arguments
247
readonly List<MovableArgument> ordered;
249
public ArgumentsOrdered (Arguments args)
253
ordered = new List<MovableArgument> ();
256
public void AddOrdered (MovableArgument arg)
261
public override Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
263
foreach (var a in ordered) {
265
a.EmitToField (ec, false);
267
a.EmitToVariable (ec);
270
return base.Emit (ec, dup_args, prepareAwait);
274
// Try not to add any more instances to this class, it's allocated a lot
277
public Arguments (int capacity)
279
args = new List<Argument> (capacity);
282
private Arguments (List<Argument> args)
287
public void Add (Argument arg)
292
public void AddRange (Arguments args)
294
this.args.AddRange (args.args);
297
public bool ContainsEmitWithAwait ()
299
foreach (var arg in args) {
300
if (arg.Expr.ContainsEmitWithAwait ())
307
public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
309
Location loc = Location.Null;
310
var all = new ArrayInitializer (args.Count, loc);
312
MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
314
foreach (Argument a in args) {
315
Arguments dargs = new Arguments (2);
317
// CSharpArgumentInfoFlags.None = 0
318
const string info_flags_enum = "CSharpArgumentInfoFlags";
319
Expression info_flags = new IntLiteral (rc.BuiltinTypes, 0, loc);
321
if (a.Expr is Constant) {
322
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
323
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc), loc);
324
} else if (a.ArgType == Argument.AType.Ref) {
325
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
326
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", 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.Out) {
330
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
331
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc), loc);
332
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
333
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
334
} else if (a.ArgType == Argument.AType.DynamicTypeName) {
335
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
336
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc), loc);
339
var arg_type = a.Expr.Type;
341
if (arg_type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && arg_type != InternalType.NullLiteral) {
342
MethodGroupExpr mg = a.Expr as MethodGroupExpr;
344
rc.Report.Error (1976, a.Expr.Location,
345
"The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
347
} else if (arg_type == InternalType.AnonymousMethod) {
348
rc.Report.Error (1977, a.Expr.Location,
349
"An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
350
} else if (arg_type.Kind == MemberKind.Void || arg_type == InternalType.Arglist || arg_type.IsPointer) {
351
rc.Report.Error (1978, a.Expr.Location,
352
"An expression of type `{0}' cannot be used as an argument of dynamic operation",
353
TypeManager.CSharpName (arg_type));
356
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
357
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
361
NamedArgument na = a as NamedArgument;
363
info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
364
new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc), loc);
366
named_value = na.Name;
371
dargs.Add (new Argument (info_flags));
372
dargs.Add (new Argument (new StringLiteral (rc.BuiltinTypes, named_value, loc)));
373
all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
379
public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
381
Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
382
for (int i = 0; i < e.Length; ++i) {
384
all.Add (new Argument (e[i]));
388
foreach (Argument a in args.args) {
389
Expression tree_arg = a.CreateExpressionTree (ec);
390
if (tree_arg != null)
391
all.Add (new Argument (tree_arg));
398
public void CheckArrayAsAttribute (CompilerContext ctx)
400
foreach (Argument arg in args) {
401
// Type is undefined (was error 246)
402
if (arg.Type == null)
405
if (arg.Type.IsArray)
406
ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
410
public Arguments Clone (CloneContext ctx)
412
Arguments cloned = new Arguments (args.Count);
413
foreach (Argument a in args)
414
cloned.Add (a.Clone (ctx));
420
get { return args.Count; }
424
// Emits a list of resolved Arguments
426
public void Emit (EmitContext ec)
428
Emit (ec, false, false);
432
// if `dup_args' is true or any of arguments contains await.
433
// A copy of all arguments will be returned to the caller
435
public virtual Arguments Emit (EmitContext ec, bool dup_args, bool prepareAwait)
439
if ((dup_args && Count != 0) || prepareAwait)
440
dups = new List<Argument> (Count);
445
foreach (Argument a in args) {
447
dups.Add (a.EmitToField (ec, true));
457
if (a.Expr.IsSideEffectFree) {
459
// No need to create a temporary variable for side effect free expressions. I assume
460
// all side-effect free expressions are cheap, this has to be tweaked when we become
461
// more aggressive on detection
465
ec.Emit (OpCodes.Dup);
467
// TODO: Release local temporary on next Emit
468
// Need to add a flag to argument to indicate this
469
lt = new LocalTemporary (a.Type);
472
dups.Add (new Argument (lt, a.ArgType));
477
return new Arguments (dups);
482
public List<Argument>.Enumerator GetEnumerator ()
484
return args.GetEnumerator ();
488
// At least one argument is of dynamic type
490
public bool HasDynamic {
492
foreach (Argument a in args) {
493
if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
502
// At least one argument is named argument
504
public bool HasNamed {
506
foreach (Argument a in args) {
507
if (a is NamedArgument)
516
public void Insert (int index, Argument arg)
518
args.Insert (index, arg);
521
public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
523
if (args == null || args.Count == 0)
526
var exprs = new System.Linq.Expressions.Expression [args.Count];
527
for (int i = 0; i < exprs.Length; ++i) {
528
Argument a = args.args [i];
529
exprs[i] = a.Expr.MakeExpression (ctx);
536
// For named arguments when the order of execution is different
537
// to order of invocation
539
public Arguments MarkOrderedArgument (NamedArgument a)
542
// An expression has no effect on left-to-right execution
544
if (a.Expr.IsSideEffectFree)
547
ArgumentsOrdered ra = this as ArgumentsOrdered;
549
ra = new ArgumentsOrdered (this);
551
for (int i = 0; i < args.Count; ++i) {
557
// When the argument is filled later by default expression
562
var ma = la as MovableArgument;
564
ma = new MovableArgument (la);
577
// Returns dynamic when at least one argument is of dynamic type
579
public void Resolve (ResolveContext ec, out bool dynamic)
582
foreach (Argument a in args) {
584
if (a.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic && !a.IsByRef)
589
public void RemoveAt (int index)
591
args.RemoveAt (index);
594
public Argument this [int index] {
595
get { return args [index]; }
596
set { args [index] = value; }