Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / mcs / dynamic.cs
1 //
2 // dynamic.cs: support for dynamic expressions
3 //
4 // Authors: Marek Safar (marek.safar@gmail.com)
5 //
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
7 //
8 // Copyright 2009 Novell, Inc
9 // Copyright 2011 Xamarin Inc.
10 //
11
12 using System;
13 using System.Linq;
14 using SLE = System.Linq.Expressions;
15
16 #if NET_4_0 || MOBILE_DYNAMIC
17 using System.Dynamic;
18 #endif
19
20 namespace Mono.CSharp
21 {
22         //
23         // A copy of Microsoft.CSharp/Microsoft.CSharp.RuntimeBinder/CSharpBinderFlags.cs
24         // has to be kept in sync
25         //
26         [Flags]
27         public enum CSharpBinderFlags
28         {
29                 None = 0,
30                 CheckedContext = 1,
31                 InvokeSimpleName = 1 << 1,
32                 InvokeSpecialName = 1 << 2,
33                 BinaryOperationLogical = 1 << 3,
34                 ConvertExplicit = 1 << 4,
35                 ConvertArrayIndex = 1 << 5,
36                 ResultIndexed = 1 << 6,
37                 ValueFromCompoundAssignment = 1 << 7,
38                 ResultDiscarded = 1 << 8
39         }
40
41         //
42         // Type expression with internal dynamic type symbol
43         //
44         class DynamicTypeExpr : TypeExpr
45         {
46                 public DynamicTypeExpr (Location loc)
47                 {
48                         this.loc = loc;
49                 }
50
51                 public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments)
52                 {
53                         eclass = ExprClass.Type;
54                         type = ec.Module.Compiler.BuiltinTypes.Dynamic;
55                         return type;
56                 }
57         }
58
59         #region Dynamic runtime binder expressions
60
61         //
62         // Expression created from runtime dynamic object value by dynamic binder
63         //
64         public class RuntimeValueExpression : Expression, IDynamicAssign, IMemoryLocation
65         {
66 #if !NET_4_0 && !MOBILE_DYNAMIC
67                 public class DynamicMetaObject
68                 {
69                         public TypeSpec RuntimeType;
70                         public TypeSpec LimitType;
71                         public SLE.Expression Expression;
72                 }
73 #endif
74
75                 readonly DynamicMetaObject obj;
76
77                 public RuntimeValueExpression (DynamicMetaObject obj, TypeSpec type)
78                 {
79                         this.obj = obj;
80                         this.type = type;
81                         this.eclass = ExprClass.Variable;
82                 }
83
84                 #region Properties
85
86                 public bool IsSuggestionOnly { get; set; }
87
88                 public DynamicMetaObject MetaObject {
89                         get { return obj; }
90                 }
91
92                 #endregion
93
94                 public void AddressOf (EmitContext ec, AddressOp mode)
95                 {
96                         throw new NotImplementedException ();
97                 }
98
99                 public override bool ContainsEmitWithAwait ()
100                 {
101                         throw new NotSupportedException ();
102                 }
103
104                 public override Expression CreateExpressionTree (ResolveContext ec)
105                 {
106                         throw new NotSupportedException ();
107                 }
108
109                 protected override Expression DoResolve (ResolveContext ec)
110                 {
111                         return this;
112                 }
113
114                 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
115                 {
116                         return this;
117                 }
118
119                 public override void Emit (EmitContext ec)
120                 {
121                         throw new NotImplementedException ();
122                 }
123
124                 #region IAssignMethod Members
125
126                 public void Emit (EmitContext ec, bool leave_copy)
127                 {
128                         throw new NotImplementedException ();
129                 }
130
131                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
132                 {
133                         throw new NotImplementedException ();
134                 }
135
136                 #endregion
137
138                 public SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source)
139                 {
140                         return obj.Expression;
141                 }
142
143                 public override SLE.Expression MakeExpression (BuilderContext ctx)
144                 {
145 #if STATIC
146                         return base.MakeExpression (ctx);
147 #else
148
149 #if NET_4_0 || MOBILE_DYNAMIC
150                                 if (type.IsStruct && !obj.Expression.Type.IsValueType)
151                                         return SLE.Expression.Unbox (obj.Expression, type.GetMetaInfo ());
152
153                                 if (obj.Expression.NodeType == SLE.ExpressionType.Parameter) {
154                                         if (((SLE.ParameterExpression) obj.Expression).IsByRef)
155                                                 return obj.Expression;
156                                 }
157         #endif
158
159                                 return SLE.Expression.Convert (obj.Expression, type.GetMetaInfo ());
160 #endif
161                 }
162         }
163
164         //
165         // Wraps runtime dynamic expression into expected type. Needed
166         // to satify expected type check by dynamic binder and no conversion
167         // is required (ResultDiscarded).
168         //
169         public class DynamicResultCast : ShimExpression
170         {
171                 public DynamicResultCast (TypeSpec type, Expression expr)
172                         : base (expr)
173                 {
174                         this.type = type;
175                 }
176
177                 protected override Expression DoResolve (ResolveContext ec)
178                 {
179                         expr = expr.Resolve (ec);
180                         eclass = ExprClass.Value;
181                         return this;
182                 }
183
184 #if NET_4_0 || MOBILE_DYNAMIC
185                 public override SLE.Expression MakeExpression (BuilderContext ctx)
186                 {
187 #if STATIC
188                         return base.MakeExpression (ctx);
189 #else
190                         return SLE.Expression.Block (expr.MakeExpression (ctx), SLE.Expression.Default (type.GetMetaInfo ()));
191 #endif
192                 }
193 #endif
194         }
195
196         #endregion
197
198         //
199         // Creates dynamic binder expression
200         //
201         interface IDynamicBinder
202         {
203                 Expression CreateCallSiteBinder (ResolveContext ec, Arguments args);
204         }
205
206         //
207         // Extends standard assignment interface for expressions
208         // supported by dynamic resolver
209         //
210         interface IDynamicAssign : IAssignMethod
211         {
212                 SLE.Expression MakeAssignExpression (BuilderContext ctx, Expression source);
213         }
214
215         //
216         // Base dynamic expression statement creator
217         //
218         class DynamicExpressionStatement : ExpressionStatement
219         {
220                 //
221                 // Binder flag dynamic constant, the value is combination of
222                 // flags known at resolve stage and flags known only at emit
223                 // stage
224                 //
225                 protected class BinderFlags : EnumConstant
226                 {
227                         readonly DynamicExpressionStatement statement;
228                         readonly CSharpBinderFlags flags;
229
230                         public BinderFlags (CSharpBinderFlags flags, DynamicExpressionStatement statement)
231                                 : base (statement.loc)
232                         {
233                                 this.flags = flags;
234                                 this.statement = statement;
235                                 eclass = 0;
236                         }
237
238                         protected override Expression DoResolve (ResolveContext ec)
239                         {
240                                 Child = new IntConstant (ec.BuiltinTypes, (int) (flags | statement.flags), statement.loc);
241
242                                 type = ec.Module.PredefinedTypes.BinderFlags.Resolve ();
243                                 eclass = Child.eclass;
244                                 return this;
245                         }
246                 }
247
248                 readonly Arguments arguments;
249                 protected IDynamicBinder binder;
250                 protected Expression binder_expr;
251
252                 // Used by BinderFlags
253                 protected CSharpBinderFlags flags;
254
255                 TypeSpec binder_type;
256                 TypeParameters context_mvars;
257
258                 public DynamicExpressionStatement (IDynamicBinder binder, Arguments args, Location loc)
259                 {
260                         this.binder = binder;
261                         this.arguments = args;
262                         this.loc = loc;
263                 }
264
265                 public Arguments Arguments {
266                         get {
267                                 return arguments;
268                         }
269                 }
270
271                 public override bool ContainsEmitWithAwait ()
272                 {
273                         return arguments.ContainsEmitWithAwait ();
274                 }
275
276                 public override Expression CreateExpressionTree (ResolveContext ec)
277                 {
278                         ec.Report.Error (1963, loc, "An expression tree cannot contain a dynamic operation");
279                         return null;
280                 }
281
282                 protected override Expression DoResolve (ResolveContext rc)
283                 {
284                         if (DoResolveCore (rc))
285                                 binder_expr = binder.CreateCallSiteBinder (rc, arguments);
286
287                         return this;
288                 }
289
290                 protected bool DoResolveCore (ResolveContext rc)
291                 {
292                         foreach (var arg in arguments) {
293                                 if (arg.Type == InternalType.VarOutType) {
294                                         // Should be special error message about dynamic dispatch
295                                         rc.Report.Error (8047, arg.Expr.Location, "Declaration expression cannot be used in this context");
296                                 }
297                         }
298
299                         if (rc.CurrentTypeParameters != null && rc.CurrentTypeParameters[0].IsMethodTypeParameter)
300                                 context_mvars = rc.CurrentTypeParameters;
301
302                         int errors = rc.Report.Errors;
303                         var pt = rc.Module.PredefinedTypes;
304
305                         binder_type = pt.Binder.Resolve ();
306                         pt.CallSite.Resolve ();
307                         pt.CallSiteGeneric.Resolve ();
308
309                         eclass = ExprClass.Value;
310
311                         if (type == null)
312                                 type = rc.BuiltinTypes.Dynamic;
313
314                         if (rc.Report.Errors == errors)
315                                 return true;
316
317                         rc.Report.Error (1969, loc,
318                                 "Dynamic operation cannot be compiled without `Microsoft.CSharp.dll' assembly reference");
319                         return false;
320                 }
321
322                 public override void Emit (EmitContext ec)
323                 {
324                         EmitCall (ec, binder_expr, arguments,  false);
325                 }
326
327                 public override void EmitStatement (EmitContext ec)
328                 {
329                         EmitCall (ec, binder_expr, arguments, true);
330                 }
331
332                 protected void EmitCall (EmitContext ec, Expression binder, Arguments arguments, bool isStatement)
333                 {
334                         //
335                         // This method generates all internal infrastructure for a dynamic call. The
336                         // reason why it's quite complicated is the mixture of dynamic and anonymous
337                         // methods. Dynamic itself requires a temporary class (ContainerX) and anonymous
338                         // methods can generate temporary storey as well (AnonStorey). Handling MVAR
339                         // type parameters rewrite is non-trivial in such case as there are various
340                         // combinations possible therefore the mutator is not straightforward. Secondly
341                         // we need to keep both MVAR(possibly VAR for anon storey) and type VAR to emit
342                         // correct Site field type and its access from EmitContext.
343                         //
344
345                         int dyn_args_count = arguments == null ? 0 : arguments.Count;
346                         int default_args = isStatement ? 1 : 2;
347                         var module = ec.Module;
348
349                         bool has_ref_out_argument = false;
350                         var targs = new TypeExpression[dyn_args_count + default_args];
351                         targs[0] = new TypeExpression (module.PredefinedTypes.CallSite.TypeSpec, loc);
352
353                         TypeExpression[] targs_for_instance = null;
354                         TypeParameterMutator mutator;
355
356                         var site_container = ec.CreateDynamicSite ();
357
358                         if (context_mvars != null) {
359                                 TypeParameters tparam;
360                                 TypeContainer sc = site_container;
361                                 do {
362                                         tparam = sc.CurrentTypeParameters;
363                                         sc = sc.Parent;
364                                 } while (tparam == null);
365
366                                 mutator = new TypeParameterMutator (context_mvars, tparam);
367
368                                 if (!ec.IsAnonymousStoreyMutateRequired) {
369                                         targs_for_instance = new TypeExpression[targs.Length];
370                                         targs_for_instance[0] = targs[0];
371                                 }
372                         } else {
373                                 mutator = null;
374                         }
375
376                         for (int i = 0; i < dyn_args_count; ++i) {
377                                 Argument a = arguments[i];
378                                 if (a.ArgType == Argument.AType.Out || a.ArgType == Argument.AType.Ref)
379                                         has_ref_out_argument = true;
380
381                                 var t = a.Type;
382
383                                 // Convert any internal type like dynamic or null to object
384                                 if (t.Kind == MemberKind.InternalCompilerType)
385                                         t = ec.BuiltinTypes.Object;
386
387                                 if (targs_for_instance != null)
388                                         targs_for_instance[i + 1] = new TypeExpression (t, loc);
389
390                                 if (mutator != null)
391                                         t = t.Mutate (mutator);
392
393                                 targs[i + 1] = new TypeExpression (t, loc);
394                         }
395
396                         TypeExpr del_type = null;
397                         TypeExpr del_type_instance_access = null;
398                         if (!has_ref_out_argument) {
399                                 string d_name = isStatement ? "Action" : "Func";
400
401                                 TypeSpec te = null;
402                                 Namespace type_ns = module.GlobalRootNamespace.GetNamespace ("System", true);
403                                 if (type_ns != null) {
404                                         te = type_ns.LookupType (module, d_name, dyn_args_count + default_args, LookupMode.Normal, loc);
405                                 }
406
407                                 if (te != null) {
408                                         if (!isStatement) {
409                                                 var t = type;
410                                                 if (t.Kind == MemberKind.InternalCompilerType)
411                                                         t = ec.BuiltinTypes.Object;
412
413                                                 if (targs_for_instance != null)
414                                                         targs_for_instance[targs_for_instance.Length - 1] = new TypeExpression (t, loc);
415
416                                                 if (mutator != null)
417                                                         t = t.Mutate (mutator);
418
419                                                 targs[targs.Length - 1] = new TypeExpression (t, loc);
420                                         }
421
422                                         del_type = new GenericTypeExpr (te, new TypeArguments (targs), loc);
423                                         if (targs_for_instance != null)
424                                                 del_type_instance_access = new GenericTypeExpr (te, new TypeArguments (targs_for_instance), loc);
425                                         else
426                                                 del_type_instance_access = del_type;
427                                 }
428                         }
429
430                         //
431                         // Create custom delegate when no appropriate predefined delegate has been found
432                         //
433                         Delegate d;
434                         if (del_type == null) {
435                                 TypeSpec rt = isStatement ? ec.BuiltinTypes.Void : type;
436                                 Parameter[] p = new Parameter[dyn_args_count + 1];
437                                 p[0] = new Parameter (targs[0], "p0", Parameter.Modifier.NONE, null, loc);
438
439                                 var site = ec.CreateDynamicSite ();
440                                 int index = site.Containers == null ? 0 : site.Containers.Count;
441
442                                 if (mutator != null)
443                                         rt = mutator.Mutate (rt);
444
445                                 for (int i = 1; i < dyn_args_count + 1; ++i) {
446                                         p[i] = new Parameter (targs[i], "p" + i.ToString ("X"), arguments[i - 1].Modifier, null, loc);
447                                 }
448
449                                 d = new Delegate (site, new TypeExpression (rt, loc),
450                                         Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED,
451                                         new MemberName ("Container" + index.ToString ("X")),
452                                         new ParametersCompiled (p), null);
453
454                                 d.CreateContainer ();
455                                 d.DefineContainer ();
456                                 d.Define ();
457                                 d.PrepareEmit ();
458
459                                 site.AddTypeContainer (d);
460
461                                 //
462                                 // Add new container to inflated site container when the
463                                 // member cache already exists
464                                 //
465                                 if (site.CurrentType is InflatedTypeSpec && index > 0)
466                                         site.CurrentType.MemberCache.AddMember (d.CurrentType);
467
468                                 del_type = new TypeExpression (d.CurrentType, loc);
469                                 if (targs_for_instance != null) {
470                                         del_type_instance_access = null;
471                                 } else {
472                                         del_type_instance_access = del_type;
473                                 }
474                         } else {
475                                 d = null;
476                         }
477
478                         var site_type_decl = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec, new TypeArguments (del_type), loc);
479                         var field = site_container.CreateCallSiteField (site_type_decl, loc);
480                         if (field == null)
481                                 return;
482
483                         if (del_type_instance_access == null) {
484                                 var dt = d.CurrentType.DeclaringType.MakeGenericType (module, context_mvars.Types);
485                                 del_type_instance_access = new TypeExpression (MemberCache.GetMember (dt, d.CurrentType), loc);
486                         }
487
488                         var instanceAccessExprType = new GenericTypeExpr (module.PredefinedTypes.CallSiteGeneric.TypeSpec,
489                                 new TypeArguments (del_type_instance_access), loc);
490
491                         if (instanceAccessExprType.ResolveAsType (ec.MemberContext) == null)
492                                 return;
493
494                         bool inflate_using_mvar = context_mvars != null && ec.IsAnonymousStoreyMutateRequired;
495
496                         TypeSpec gt;
497                         if (inflate_using_mvar || context_mvars == null) {
498                                 gt = site_container.CurrentType;
499                         } else {
500                                 gt = site_container.CurrentType.MakeGenericType (module, context_mvars.Types);
501                         }
502
503                         // When site container already exists the inflated version has to be
504                         // updated manually to contain newly created field
505                         if (gt is InflatedTypeSpec && site_container.AnonymousMethodsCounter > 1) {
506                                 var tparams = gt.MemberDefinition.TypeParametersCount > 0 ? gt.MemberDefinition.TypeParameters : TypeParameterSpec.EmptyTypes;
507                                 var inflator = new TypeParameterInflator (module, gt, tparams, gt.TypeArguments);
508                                 gt.MemberCache.AddMember (field.InflateMember (inflator));
509                         }
510
511                         FieldExpr site_field_expr = new FieldExpr (MemberCache.GetMember (gt, field), loc);
512
513                         BlockContext bc = new BlockContext (ec.MemberContext, null, ec.BuiltinTypes.Void);
514
515                         Arguments args = new Arguments (1);
516                         args.Add (new Argument (binder));
517                         StatementExpression s = new StatementExpression (new SimpleAssign (site_field_expr, new Invocation (new MemberAccess (instanceAccessExprType, "Create"), args)));
518
519                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
520                                 if (s.Resolve (bc)) {
521                                         Statement init = new If (new Binary (Binary.Operator.Equality, site_field_expr, new NullLiteral (loc)), s, loc);
522                                         init.Emit (ec);
523                                 }
524
525                                 args = new Arguments (1 + dyn_args_count);
526                                 args.Add (new Argument (site_field_expr));
527                                 if (arguments != null) {
528                                         int arg_pos = 1;
529                                         foreach (Argument a in arguments) {
530                                                 if (a is NamedArgument) {
531                                                         // Name is not valid in this context
532                                                         args.Add (new Argument (a.Expr, a.ArgType));
533                                                 } else {
534                                                         args.Add (a);
535                                                 }
536
537                                                 if (inflate_using_mvar && a.Type != targs[arg_pos].Type)
538                                                         a.Expr.Type = targs[arg_pos].Type;
539
540                                                 ++arg_pos;
541                                         }
542                                 }
543
544                                 Expression target = new DelegateInvocation (new MemberAccess (site_field_expr, "Target", loc).Resolve (bc), args, false, loc).Resolve (bc);
545                                 if (target != null)
546                                         target.Emit (ec);
547                         }
548                 }
549
550                 public override void FlowAnalysis (FlowAnalysisContext fc)
551                 {
552                         arguments.FlowAnalysis (fc);
553                 }
554
555                 public static MemberAccess GetBinderNamespace (Location loc)
556                 {
557                         return new MemberAccess (new MemberAccess (
558                                 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "Microsoft", loc), "CSharp", loc), "RuntimeBinder", loc);
559                 }
560
561                 protected MemberAccess GetBinder (string name, Location loc)
562                 {
563                         return new MemberAccess (new TypeExpression (binder_type, loc), name, loc);
564                 }
565         }
566
567         //
568         // Dynamic member access compound assignment for events
569         //
570         class DynamicEventCompoundAssign : ExpressionStatement
571         {
572                 class IsEvent : DynamicExpressionStatement, IDynamicBinder
573                 {
574                         string name;
575
576                         public IsEvent (string name, Arguments args, Location loc)
577                                 : base (null, args, loc)
578                         {
579                                 this.name = name;
580                                 binder = this;
581                         }
582
583                         public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
584                         {
585                                 type = ec.BuiltinTypes.Bool;
586
587                                 Arguments binder_args = new Arguments (3);
588
589                                 binder_args.Add (new Argument (new BinderFlags (0, this)));
590                                 binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc)));
591                                 binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
592
593                                 return new Invocation (GetBinder ("IsEvent", loc), binder_args);
594                         }
595                 }
596
597                 Expression condition;
598                 ExpressionStatement invoke, assign;
599
600                 public DynamicEventCompoundAssign (string name, Arguments args, ExpressionStatement assignment, ExpressionStatement invoke, Location loc)
601                 {
602                         condition = new IsEvent (name, args, loc);
603                         this.invoke = invoke;
604                         this.assign = assignment;
605                         this.loc = loc;
606                 }
607
608                 public override Expression CreateExpressionTree (ResolveContext ec)
609                 {
610                         return condition.CreateExpressionTree (ec);
611                 }
612
613                 protected override Expression DoResolve (ResolveContext rc)
614                 {
615                         type = rc.BuiltinTypes.Dynamic;
616                         eclass = ExprClass.Value;
617                         condition = condition.Resolve (rc);
618                         return this;
619                 }
620
621                 public override void Emit (EmitContext ec)
622                 {
623                         var rc = new ResolveContext (ec.MemberContext);
624                         var expr = new Conditional (new BooleanExpression (condition), invoke, assign, loc).Resolve (rc);
625                         expr.Emit (ec);
626                 }
627
628                 public override void EmitStatement (EmitContext ec)
629                 {
630                         var stmt = new If (condition, new StatementExpression (invoke), new StatementExpression (assign), loc);
631                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
632                                 stmt.Emit (ec);
633                         }
634                 }
635
636                 public override void FlowAnalysis (FlowAnalysisContext fc)
637                 {
638                         invoke.FlowAnalysis (fc);
639                 }
640         }
641
642         class DynamicConversion : DynamicExpressionStatement, IDynamicBinder
643         {
644                 public DynamicConversion (TypeSpec targetType, CSharpBinderFlags flags, Arguments args, Location loc)
645                         : base (null, args, loc)
646                 {
647                         type = targetType;
648                         base.flags = flags;
649                         base.binder = this;
650                 }
651
652                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
653                 {
654                         Arguments binder_args = new Arguments (3);
655
656                         flags |= ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0;
657
658                         binder_args.Add (new Argument (new BinderFlags (flags, this)));
659                         binder_args.Add (new Argument (new TypeOf (type, loc)));
660                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
661                         return new Invocation (GetBinder ("Convert", loc), binder_args);
662                 }
663         }
664
665         class DynamicConstructorBinder : DynamicExpressionStatement, IDynamicBinder
666         {
667                 public DynamicConstructorBinder (TypeSpec type, Arguments args, Location loc)
668                         : base (null, args, loc)
669                 {
670                         this.type = type;
671                         base.binder = this;
672                 }
673
674                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
675                 {
676                         Arguments binder_args = new Arguments (3);
677
678                         binder_args.Add (new Argument (new BinderFlags (0, this)));
679                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
680                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
681
682                         return new Invocation (GetBinder ("InvokeConstructor", loc), binder_args);
683                 }
684         }
685
686         class DynamicIndexBinder : DynamicMemberAssignable
687         {
688                 bool can_be_mutator;
689
690                 public DynamicIndexBinder (Arguments args, Location loc)
691                         : base (args, loc)
692                 {
693                 }
694
695                 public DynamicIndexBinder (CSharpBinderFlags flags, Arguments args, Location loc)
696                         : this (args, loc)
697                 {
698                         base.flags = flags;
699                 }
700
701                 protected override Expression DoResolve (ResolveContext ec)
702                 {
703                         can_be_mutator = true;
704                         return base.DoResolve (ec);
705                 }
706
707                 protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet)
708                 {
709                         Arguments binder_args = new Arguments (3);
710
711                         binder_args.Add (new Argument (new BinderFlags (flags, this)));
712                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
713                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
714
715                         isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
716                         return new Invocation (GetBinder (isSet ? "SetIndex" : "GetIndex", loc), binder_args);
717                 }
718
719                 protected override Arguments CreateSetterArguments (ResolveContext rc, Expression rhs)
720                 {
721                         //
722                         // Indexer has arguments which complicates things as the setter and getter
723                         // are called in two steps when unary mutator is used. We have to make a
724                         // copy of all variable arguments to not duplicate any side effect.
725                         //
726                         // ++d[++arg, Foo ()]
727                         //
728
729                         if (!can_be_mutator)
730                                 return base.CreateSetterArguments (rc, rhs);
731
732                         var setter_args = new Arguments (Arguments.Count + 1);
733                         for (int i = 0; i < Arguments.Count; ++i) {
734                                 var expr = Arguments[i].Expr;
735
736                                 if (expr is Constant || expr is VariableReference || expr is This) {
737                                         setter_args.Add (Arguments [i]);
738                                         continue;
739                                 }
740
741                                 LocalVariable temp = LocalVariable.CreateCompilerGenerated (expr.Type, rc.CurrentBlock, loc);
742                                 expr = new SimpleAssign (temp.CreateReferenceExpression (rc, expr.Location), expr).Resolve (rc);
743                                 Arguments[i].Expr = temp.CreateReferenceExpression (rc, expr.Location).Resolve (rc);
744                                 setter_args.Add (Arguments [i].Clone (expr));
745                         }
746
747                         setter_args.Add (new Argument (rhs));
748                         return setter_args;
749                 }
750         }
751
752         class DynamicInvocation : DynamicExpressionStatement, IDynamicBinder
753         {
754                 readonly ATypeNameExpression member;
755
756                 public DynamicInvocation (ATypeNameExpression member, Arguments args, Location loc)
757                         : base (null, args, loc)
758                 {
759                         base.binder = this;
760                         this.member = member;
761                 }
762
763                 public static DynamicInvocation CreateSpecialNameInvoke (ATypeNameExpression member, Arguments args, Location loc)
764                 {
765                         return new DynamicInvocation (member, args, loc) {
766                                 flags = CSharpBinderFlags.InvokeSpecialName
767                         };
768                 }
769
770                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
771                 {
772                         Arguments binder_args = new Arguments (member != null ? 5 : 3);
773                         bool is_member_access = member is MemberAccess;
774
775                         CSharpBinderFlags call_flags;
776                         if (!is_member_access && member is SimpleName) {
777                                 call_flags = CSharpBinderFlags.InvokeSimpleName;
778                                 is_member_access = true;
779                         } else {
780                                 call_flags = 0;
781                         }
782
783                         binder_args.Add (new Argument (new BinderFlags (call_flags, this)));
784
785                         if (is_member_access)
786                                 binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, member.Name, member.Location)));
787
788                         if (member != null && member.HasTypeArguments) {
789                                 TypeArguments ta = member.TypeArguments;
790                                 if (ta.Resolve (ec, false)) {
791                                         var targs = new ArrayInitializer (ta.Count, loc);
792                                         foreach (TypeSpec t in ta.Arguments)
793                                                 targs.Add (new TypeOf (t, loc));
794
795                                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (targs, loc)));
796                                 }
797                         } else if (is_member_access) {
798                                 binder_args.Add (new Argument (new NullLiteral (loc)));
799                         }
800
801                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
802
803                         Expression real_args;
804                         if (args == null) {
805                                 // Cannot be null because .NET trips over
806                                 real_args = new ArrayCreation (
807                                         new MemberAccess (GetBinderNamespace (loc), "CSharpArgumentInfo", loc),
808                                         new ArrayInitializer (0, loc), loc);
809                         } else {
810                                 real_args = new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc);
811                         }
812
813                         binder_args.Add (new Argument (real_args));
814
815                         return new Invocation (GetBinder (is_member_access ? "InvokeMember" : "Invoke", loc), binder_args);
816                 }
817
818                 public override void EmitStatement (EmitContext ec)
819                 {
820                         flags |= CSharpBinderFlags.ResultDiscarded;
821                         base.EmitStatement (ec);
822                 }
823         }
824
825         class DynamicMemberBinder : DynamicMemberAssignable
826         {
827                 readonly string name;
828
829                 public DynamicMemberBinder (string name, Arguments args, Location loc)
830                         : base (args, loc)
831                 {
832                         this.name = name;
833                 }
834
835                 public DynamicMemberBinder (string name, CSharpBinderFlags flags, Arguments args, Location loc)
836                         : this (name, args, loc)
837                 {
838                         base.flags = flags;
839                 }
840
841                 protected override Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet)
842                 {
843                         Arguments binder_args = new Arguments (4);
844
845                         binder_args.Add (new Argument (new BinderFlags (flags, this)));
846                         binder_args.Add (new Argument (new StringLiteral (ec.BuiltinTypes, name, loc)));
847                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
848                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
849
850                         isSet |= (flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0;
851                         return new Invocation (GetBinder (isSet ? "SetMember" : "GetMember", loc), binder_args);
852                 }
853         }
854
855         //
856         // Any member binder which can be source and target of assignment
857         //
858         abstract class DynamicMemberAssignable : DynamicExpressionStatement, IDynamicBinder, IAssignMethod
859         {
860                 Expression setter;
861                 Arguments setter_args;
862
863                 protected DynamicMemberAssignable (Arguments args, Location loc)
864                         : base (null, args, loc)
865                 {
866                         base.binder = this;
867                 }
868
869                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
870                 {
871                         //
872                         // DoResolve always uses getter
873                         //
874                         return CreateCallSiteBinder (ec, args, false);
875                 }
876
877                 protected abstract Expression CreateCallSiteBinder (ResolveContext ec, Arguments args, bool isSet);
878
879                 protected virtual Arguments CreateSetterArguments (ResolveContext rc, Expression rhs)
880                 {
881                         var setter_args = new Arguments (Arguments.Count + 1);
882                         setter_args.AddRange (Arguments);
883                         setter_args.Add (new Argument (rhs));
884                         return setter_args;
885                 }
886
887                 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
888                 {
889                         if (right_side == EmptyExpression.OutAccess) {
890                                 right_side.DoResolveLValue (rc, this);
891                                 return null;
892                         }
893
894                         if (DoResolveCore (rc)) {
895                                 setter_args = CreateSetterArguments (rc, right_side);
896                                 setter = CreateCallSiteBinder (rc, setter_args, true);
897                         }
898
899                         eclass = ExprClass.Variable;
900                         return this;
901                 }
902
903                 public override void Emit (EmitContext ec)
904                 {
905                         // It's null for ResolveLValue used without assignment
906                         if (binder_expr == null)
907                                 EmitCall (ec, setter, Arguments, false);
908                         else
909                                 base.Emit (ec);
910                 }
911
912                 public override void EmitStatement (EmitContext ec)
913                 {
914                         // It's null for ResolveLValue used without assignment
915                         if (binder_expr == null)
916                                 EmitCall (ec, setter, Arguments, true);
917                         else
918                                 base.EmitStatement (ec);
919                 }
920
921                 #region IAssignMethod Members
922
923                 public void Emit (EmitContext ec, bool leave_copy)
924                 {
925                         throw new NotImplementedException ();
926                 }
927
928                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
929                 {
930                         EmitCall (ec, setter, setter_args, !leave_copy);
931                 }
932
933                 #endregion
934         }
935
936         class DynamicUnaryConversion : DynamicExpressionStatement, IDynamicBinder
937         {
938                 readonly string name;
939
940                 public DynamicUnaryConversion (string name, Arguments args, Location loc)
941                         : base (null, args, loc)
942                 {
943                         this.name = name;
944                         base.binder = this;
945                 }
946
947                 public static DynamicUnaryConversion CreateIsTrue (ResolveContext rc, Arguments args, Location loc)
948                 {
949                         return new DynamicUnaryConversion ("IsTrue", args, loc) { type = rc.BuiltinTypes.Bool };
950                 }
951
952                 public static DynamicUnaryConversion CreateIsFalse (ResolveContext rc, Arguments args, Location loc)
953                 {
954                         return new DynamicUnaryConversion ("IsFalse", args, loc) { type = rc.BuiltinTypes.Bool };
955                 }
956
957                 public Expression CreateCallSiteBinder (ResolveContext ec, Arguments args)
958                 {
959                         Arguments binder_args = new Arguments (4);
960
961                         MemberAccess sle = new MemberAccess (new MemberAccess (
962                                 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Linq", loc), "Expressions", loc);
963
964                         var flags = ec.HasSet (ResolveContext.Options.CheckedScope) ? CSharpBinderFlags.CheckedContext : 0;
965
966                         binder_args.Add (new Argument (new BinderFlags (flags, this)));
967                         binder_args.Add (new Argument (new MemberAccess (new MemberAccess (sle, "ExpressionType", loc), name, loc)));
968                         binder_args.Add (new Argument (new TypeOf (ec.CurrentType, loc)));
969                         binder_args.Add (new Argument (new ImplicitlyTypedArrayCreation (args.CreateDynamicBinderArguments (ec), loc)));
970
971                         return new Invocation (GetBinder ("UnaryOperation", loc), binder_args);
972                 }
973         }
974
975         sealed class DynamicSiteClass : HoistedStoreyClass
976         {
977                 public DynamicSiteClass (TypeDefinition parent, MemberBase host, TypeParameters tparams)
978                         : base (parent, MakeMemberName (host, "DynamicSite", parent.DynamicSitesCounter, tparams, Location.Null), tparams, Modifiers.STATIC, MemberKind.Class)
979                 {
980                         parent.DynamicSitesCounter++;
981                 }
982
983                 public FieldSpec CreateCallSiteField (FullNamedExpression type, Location loc)
984                 {
985                         int index = AnonymousMethodsCounter++;
986                         Field f = new HoistedField (this, type, Modifiers.PUBLIC | Modifiers.STATIC, "Site" + index.ToString ("X"), null, loc);
987                         f.Define ();
988
989                         AddField (f);
990                         return f.Spec;
991                 }
992         }
993 }