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