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