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