1 // 2 // codegen.cs: The code generator 3 // 4 // Authors: 5 // Miguel de Icaza (miguel@ximian.com) 6 // Marek Safar (marek.safar@gmail.com) 7 // 8 // Copyright 2001, 2002, 2003 Ximian, Inc. 9 // Copyright 2004 Novell, Inc. 10 // Copyright 2011 Xamarin Inc 11 // 12 13 using System; 14 using System.Collections.Generic; 15 using Mono.CompilerServices.SymbolWriter; 16 17 #if STATIC 18 using MetaType = IKVM.Reflection.Type; 19 using IKVM.Reflection; 20 using IKVM.Reflection.Emit; 21 #else 22 using MetaType = System.Type; 23 using System.Reflection; 24 using System.Reflection.Emit; 25 #endif 26 27 namespace Mono.CSharp 28 { 29 /// <summary> 30 /// An Emit Context is created for each body of code (from methods, 31 /// properties bodies, indexer bodies or constructor bodies) 32 /// </summary> 33 public class EmitContext : BuilderContext 34 { 35 // TODO: Has to be private 36 public readonly ILGenerator ig; 37 38 /// <summary> 39 /// The value that is allowed to be returned or NULL if there is no 40 /// return type. 41 /// </summary> 42 readonly TypeSpec return_type; 43 44 /// <summary> 45 /// Keeps track of the Type to LocalBuilder temporary storage created 46 /// to store structures (used to compute the address of the structure 47 /// value on structure method invocations) 48 /// </summary> 49 Dictionary<TypeSpec, object> temporary_storage; 50 51 /// <summary> 52 /// The location where we store the return value. 53 /// </summary> 54 public LocalBuilder return_value; 55 56 57 /// <summary> 58 /// Current loop begin and end labels. 59 /// </summary> 60 public Label LoopBegin, LoopEnd; 61 62 /// <summary> 63 /// Default target in a switch statement. Only valid if 64 /// InSwitch is true 65 /// </summary> 66 public Label DefaultTarget; 67 68 /// <summary> 69 /// If this is non-null, points to the current switch statement 70 /// </summary> 71 public Switch Switch; 72 73 /// <summary> 74 /// Whether we are inside an anonymous method. 75 /// </summary> 76 public AnonymousExpression CurrentAnonymousMethod; 77 78 readonly IMemberContext member_context; 79 80 readonly SourceMethodBuilder methodSymbols; 81 82 DynamicSiteClass dynamic_site_container; 83 84 Label? return_label; 85 86 List<IExpressionCleanup> epilogue_expressions; 87 EmitContext(IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols)88 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type, SourceMethodBuilder methodSymbols) 89 { 90 this.member_context = rc; 91 this.ig = ig; 92 this.return_type = return_type; 93 94 if (rc.Module.Compiler.Settings.Checked) 95 flags |= Options.CheckedScope; 96 97 if (methodSymbols != null) { 98 this.methodSymbols = methodSymbols; 99 if (!rc.Module.Compiler.Settings.Optimize) 100 flags |= Options.AccurateDebugInfo; 101 } else { 102 flags |= Options.OmitDebugInfo; 103 } 104 105 #if STATIC 106 ig.__CleverExceptionBlockAssistance (); 107 #endif 108 } 109 110 #region Properties 111 112 internal AsyncTaskStorey AsyncTaskStorey { 113 get { 114 return CurrentAnonymousMethod.Storey as AsyncTaskStorey; 115 } 116 } 117 118 public BuiltinTypes BuiltinTypes { 119 get { 120 return MemberContext.Module.Compiler.BuiltinTypes; 121 } 122 } 123 124 public ConditionalAccessContext ConditionalAccess { get; set; } 125 126 public TypeSpec CurrentType { 127 get { return member_context.CurrentType; } 128 } 129 130 public TypeParameters CurrentTypeParameters { 131 get { return member_context.CurrentTypeParameters; } 132 } 133 134 public MemberCore CurrentTypeDefinition { 135 get { return member_context.CurrentMemberDefinition; } 136 } 137 138 public bool EmitAccurateDebugInfo { 139 get { 140 return (flags & Options.AccurateDebugInfo) != 0; 141 } 142 } 143 144 public bool HasMethodSymbolBuilder { 145 get { 146 return methodSymbols != null; 147 } 148 } 149 150 public bool HasReturnLabel { 151 get { 152 return return_label.HasValue; 153 } 154 } 155 156 public bool IsStatic { 157 get { return member_context.IsStatic; } 158 } 159 160 public bool IsStaticConstructor { 161 get { 162 return member_context.IsStatic && (flags & Options.ConstructorScope) != 0; 163 } 164 } 165 166 public bool IsAnonymousStoreyMutateRequired { 167 get { 168 return CurrentAnonymousMethod != null && 169 CurrentAnonymousMethod.Storey != null && 170 CurrentAnonymousMethod.Storey.Mutator != null; 171 } 172 } 173 174 public IMemberContext MemberContext { 175 get { 176 return member_context; 177 } 178 } 179 180 public ModuleContainer Module { 181 get { 182 return member_context.Module; 183 } 184 } 185 186 public bool NotifyEvaluatorOnStore { 187 get { 188 return Module.Evaluator != null && Module.Evaluator.ModificationListener != null; 189 } 190 } 191 192 // Has to be used for specific emitter errors only any 193 // possible resolver errors have to be reported during Resolve 194 public Report Report { 195 get { 196 return member_context.Module.Compiler.Report; 197 } 198 } 199 200 public TypeSpec ReturnType { 201 get { 202 return return_type; 203 } 204 } 205 206 // 207 // The label where we have to jump before leaving the context 208 // 209 public Label ReturnLabel { 210 get { 211 return return_label.Value; 212 } 213 } 214 215 public List<IExpressionCleanup> StatementEpilogue { 216 get { 217 return epilogue_expressions; 218 } 219 } 220 221 public LocalVariable AsyncThrowVariable { get; set; } 222 223 public List<TryFinally> TryFinallyUnwind { get; set; } 224 225 public Label RecursivePatternLabel { get; set; } 226 227 #endregion 228 AddStatementEpilog(IExpressionCleanup cleanupExpression)229 public void AddStatementEpilog (IExpressionCleanup cleanupExpression) 230 { 231 if (epilogue_expressions == null) { 232 epilogue_expressions = new List<IExpressionCleanup> (); 233 } else if (epilogue_expressions.Contains (cleanupExpression)) { 234 return; 235 } 236 237 epilogue_expressions.Add (cleanupExpression); 238 } 239 AssertEmptyStack()240 public void AssertEmptyStack () 241 { 242 #if STATIC 243 if (ig.__StackHeight != 0) 244 throw new InternalErrorException ("Await yields with non-empty stack in `{0}", 245 member_context.GetSignatureForError ()); 246 #endif 247 } 248 249 /// <summary> 250 /// This is called immediately before emitting an IL opcode to tell the symbol 251 /// writer to which source line this opcode belongs. 252 /// </summary> Mark(Location loc)253 public bool Mark (Location loc) 254 { 255 if ((flags & Options.OmitDebugInfo) != 0) 256 return false; 257 258 if (loc.IsNull || methodSymbols == null) 259 return false; 260 261 var sf = loc.SourceFile; 262 if (sf.IsHiddenLocation (loc)) 263 return false; 264 265 methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false); 266 return true; 267 } 268 MarkCallEntry(Location loc)269 public void MarkCallEntry (Location loc) 270 { 271 if (!EmitAccurateDebugInfo) 272 return; 273 274 // 275 // TODO: This should emit different kind of sequence point to make 276 // step-over work for statement over multiple lines 277 // 278 // Debugging experience for Foo (A () + B ()) where A and B are 279 // on separate lines is not great 280 // 281 Mark (loc); 282 } 283 DefineLocalVariable(string name, LocalBuilder builder)284 public void DefineLocalVariable (string name, LocalBuilder builder) 285 { 286 if ((flags & Options.OmitDebugInfo) != 0) 287 return; 288 289 methodSymbols.AddLocal (builder.LocalIndex, name); 290 } 291 BeginCatchBlock(TypeSpec type)292 public void BeginCatchBlock (TypeSpec type) 293 { 294 if (IsAnonymousStoreyMutateRequired) 295 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 296 297 ig.BeginCatchBlock (type.GetMetaInfo ()); 298 } 299 BeginFilterHandler()300 public void BeginFilterHandler () 301 { 302 ig.BeginCatchBlock (null); 303 } 304 BeginExceptionBlock()305 public void BeginExceptionBlock () 306 { 307 ig.BeginExceptionBlock (); 308 } 309 BeginExceptionFilterBlock()310 public void BeginExceptionFilterBlock () 311 { 312 ig.BeginExceptFilterBlock (); 313 } 314 BeginFinallyBlock()315 public void BeginFinallyBlock () 316 { 317 ig.BeginFinallyBlock (); 318 } 319 BeginScope(int scopeIndex)320 public void BeginScope (int scopeIndex) 321 { 322 if ((flags & Options.OmitDebugInfo) != 0) 323 return; 324 325 methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset, scopeIndex); 326 } 327 BeginCompilerScope(int scopeIndex)328 public void BeginCompilerScope (int scopeIndex) 329 { 330 if ((flags & Options.OmitDebugInfo) != 0) 331 return; 332 333 methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset, scopeIndex); 334 } 335 EndExceptionBlock()336 public void EndExceptionBlock () 337 { 338 ig.EndExceptionBlock (); 339 } 340 EndScope()341 public void EndScope () 342 { 343 if ((flags & Options.OmitDebugInfo) != 0) 344 return; 345 346 methodSymbols.EndBlock (ig.ILOffset); 347 } 348 CloseConditionalAccess(TypeSpec type)349 public void CloseConditionalAccess (TypeSpec type) 350 { 351 if (type != null) 352 Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type)); 353 354 MarkLabel (ConditionalAccess.EndLabel); 355 ConditionalAccess = null; 356 } 357 358 // 359 // Creates a nested container in this context for all dynamic compiler generated stuff 360 // CreateDynamicSite()361 internal DynamicSiteClass CreateDynamicSite () 362 { 363 if (dynamic_site_container == null) { 364 var mc = member_context.CurrentMemberDefinition as MemberBase; 365 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters); 366 367 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container); 368 dynamic_site_container.CreateContainer (); 369 dynamic_site_container.DefineContainer (); 370 dynamic_site_container.Define (); 371 372 var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes); 373 var inflated = dynamic_site_container.CurrentType.InflateMember (inflator); 374 CurrentType.MemberCache.AddMember (inflated); 375 } 376 377 return dynamic_site_container; 378 } 379 CreateReturnLabel()380 public Label CreateReturnLabel () 381 { 382 if (!return_label.HasValue) 383 return_label = DefineLabel (); 384 385 return return_label.Value; 386 } 387 DeclareLocal(TypeSpec type, bool pinned)388 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned) 389 { 390 if (IsAnonymousStoreyMutateRequired) 391 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 392 393 if (pinned) { 394 // 395 // This is for .net compatibility. I am not sure why pinned 396 // pointer temps are converted to & even if they are pointers to 397 // pointers. 398 // 399 var pt = type as PointerContainer; 400 if (pt != null) { 401 type = pt.Element; 402 if (type.Kind == MemberKind.Void) 403 type = Module.Compiler.BuiltinTypes.IntPtr; 404 405 return ig.DeclareLocal (type.GetMetaInfo ().MakeByRefType (), true); 406 } 407 } 408 409 return ig.DeclareLocal (type.GetMetaInfo (), pinned); 410 } 411 DefineLabel()412 public Label DefineLabel () 413 { 414 return ig.DefineLabel (); 415 } 416 417 // 418 // Creates temporary field in current async storey 419 // GetTemporaryField(TypeSpec type, bool initializedFieldRequired = false)420 public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false) 421 { 422 var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired); 423 var fexpr = new StackFieldExpr (f); 424 fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null); 425 return fexpr; 426 } 427 MarkLabel(Label label)428 public void MarkLabel (Label label) 429 { 430 ig.MarkLabel (label); 431 } 432 Emit(OpCode opcode)433 public void Emit (OpCode opcode) 434 { 435 ig.Emit (opcode); 436 } 437 Emit(OpCode opcode, LocalBuilder local)438 public void Emit (OpCode opcode, LocalBuilder local) 439 { 440 ig.Emit (opcode, local); 441 } 442 Emit(OpCode opcode, string arg)443 public void Emit (OpCode opcode, string arg) 444 { 445 ig.Emit (opcode, arg); 446 } 447 Emit(OpCode opcode, double arg)448 public void Emit (OpCode opcode, double arg) 449 { 450 ig.Emit (opcode, arg); 451 } 452 Emit(OpCode opcode, float arg)453 public void Emit (OpCode opcode, float arg) 454 { 455 ig.Emit (opcode, arg); 456 } 457 Emit(OpCode opcode, Label label)458 public void Emit (OpCode opcode, Label label) 459 { 460 ig.Emit (opcode, label); 461 } 462 Emit(OpCode opcode, Label[] labels)463 public void Emit (OpCode opcode, Label[] labels) 464 { 465 ig.Emit (opcode, labels); 466 } 467 Emit(OpCode opcode, TypeSpec type)468 public void Emit (OpCode opcode, TypeSpec type) 469 { 470 if (IsAnonymousStoreyMutateRequired) 471 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 472 473 ig.Emit (opcode, type.GetMetaInfo ()); 474 } 475 Emit(OpCode opcode, FieldSpec field)476 public void Emit (OpCode opcode, FieldSpec field) 477 { 478 if (IsAnonymousStoreyMutateRequired) 479 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator); 480 481 ig.Emit (opcode, field.GetMetaInfo ()); 482 } 483 Emit(OpCode opcode, MethodSpec method)484 public void Emit (OpCode opcode, MethodSpec method) 485 { 486 if (IsAnonymousStoreyMutateRequired) 487 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator); 488 489 if (method.IsConstructor) 490 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ()); 491 else 492 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ()); 493 } 494 495 // TODO: REMOVE breaks mutator Emit(OpCode opcode, MethodInfo method)496 public void Emit (OpCode opcode, MethodInfo method) 497 { 498 ig.Emit (opcode, method); 499 } 500 Emit(OpCode opcode, MethodSpec method, MetaType[] vargs)501 public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs) 502 { 503 // TODO MemberCache: This should mutate too 504 ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs); 505 } 506 EmitArrayNew(ArrayContainer ac)507 public void EmitArrayNew (ArrayContainer ac) 508 { 509 if (ac.Rank == 1) { 510 var type = IsAnonymousStoreyMutateRequired ? 511 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : 512 ac.Element; 513 514 ig.Emit (OpCodes.Newarr, type.GetMetaInfo ()); 515 } else { 516 if (IsAnonymousStoreyMutateRequired) 517 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); 518 519 ig.Emit (OpCodes.Newobj, ac.GetConstructor ()); 520 } 521 } 522 EmitArrayAddress(ArrayContainer ac)523 public void EmitArrayAddress (ArrayContainer ac) 524 { 525 if (ac.Rank > 1) { 526 if (IsAnonymousStoreyMutateRequired) 527 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); 528 529 ig.Emit (OpCodes.Call, ac.GetAddressMethod ()); 530 } else { 531 var type = IsAnonymousStoreyMutateRequired ? 532 CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) : 533 ac.Element; 534 535 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); 536 } 537 } 538 539 // 540 // Emits the right opcode to load from an array 541 // EmitArrayLoad(ArrayContainer ac)542 public void EmitArrayLoad (ArrayContainer ac) 543 { 544 if (ac.Rank > 1) { 545 if (IsAnonymousStoreyMutateRequired) 546 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); 547 548 ig.Emit (OpCodes.Call, ac.GetGetMethod ()); 549 return; 550 } 551 552 553 var type = ac.Element; 554 if (type.Kind == MemberKind.Enum) 555 type = EnumSpec.GetUnderlyingType (type); 556 557 switch (type.BuiltinType) { 558 case BuiltinTypeSpec.Type.Bool: 559 // 560 // bool array can actually store any byte value in underlying byte slot 561 // and C# spec does not specify any normalization rule, except the result 562 // is undefined 563 // 564 case BuiltinTypeSpec.Type.Byte: 565 ig.Emit (OpCodes.Ldelem_U1); 566 break; 567 case BuiltinTypeSpec.Type.SByte: 568 ig.Emit (OpCodes.Ldelem_I1); 569 break; 570 case BuiltinTypeSpec.Type.Short: 571 ig.Emit (OpCodes.Ldelem_I2); 572 break; 573 case BuiltinTypeSpec.Type.UShort: 574 case BuiltinTypeSpec.Type.Char: 575 ig.Emit (OpCodes.Ldelem_U2); 576 break; 577 case BuiltinTypeSpec.Type.Int: 578 ig.Emit (OpCodes.Ldelem_I4); 579 break; 580 case BuiltinTypeSpec.Type.UInt: 581 ig.Emit (OpCodes.Ldelem_U4); 582 break; 583 case BuiltinTypeSpec.Type.ULong: 584 case BuiltinTypeSpec.Type.Long: 585 ig.Emit (OpCodes.Ldelem_I8); 586 break; 587 case BuiltinTypeSpec.Type.Float: 588 ig.Emit (OpCodes.Ldelem_R4); 589 break; 590 case BuiltinTypeSpec.Type.Double: 591 ig.Emit (OpCodes.Ldelem_R8); 592 break; 593 case BuiltinTypeSpec.Type.IntPtr: 594 ig.Emit (OpCodes.Ldelem_I); 595 break; 596 default: 597 switch (type.Kind) { 598 case MemberKind.Struct: 599 if (IsAnonymousStoreyMutateRequired) 600 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 601 602 ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ()); 603 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); 604 break; 605 case MemberKind.TypeParameter: 606 if (IsAnonymousStoreyMutateRequired) 607 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 608 609 ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ()); 610 break; 611 case MemberKind.PointerType: 612 ig.Emit (OpCodes.Ldelem_I); 613 break; 614 default: 615 ig.Emit (OpCodes.Ldelem_Ref); 616 break; 617 } 618 break; 619 } 620 } 621 622 // 623 // Emits the right opcode to store to an array 624 // EmitArrayStore(ArrayContainer ac)625 public void EmitArrayStore (ArrayContainer ac) 626 { 627 if (ac.Rank > 1) { 628 if (IsAnonymousStoreyMutateRequired) 629 ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator); 630 631 ig.Emit (OpCodes.Call, ac.GetSetMethod ()); 632 return; 633 } 634 635 var type = ac.Element; 636 637 if (type.Kind == MemberKind.Enum) 638 type = EnumSpec.GetUnderlyingType (type); 639 640 switch (type.BuiltinType) { 641 case BuiltinTypeSpec.Type.Byte: 642 case BuiltinTypeSpec.Type.SByte: 643 case BuiltinTypeSpec.Type.Bool: 644 Emit (OpCodes.Stelem_I1); 645 return; 646 case BuiltinTypeSpec.Type.Short: 647 case BuiltinTypeSpec.Type.UShort: 648 case BuiltinTypeSpec.Type.Char: 649 Emit (OpCodes.Stelem_I2); 650 return; 651 case BuiltinTypeSpec.Type.Int: 652 case BuiltinTypeSpec.Type.UInt: 653 Emit (OpCodes.Stelem_I4); 654 return; 655 case BuiltinTypeSpec.Type.Long: 656 case BuiltinTypeSpec.Type.ULong: 657 Emit (OpCodes.Stelem_I8); 658 return; 659 case BuiltinTypeSpec.Type.Float: 660 Emit (OpCodes.Stelem_R4); 661 return; 662 case BuiltinTypeSpec.Type.Double: 663 Emit (OpCodes.Stelem_R8); 664 return; 665 } 666 667 switch (type.Kind) { 668 case MemberKind.Struct: 669 Emit (OpCodes.Stobj, type); 670 break; 671 case MemberKind.TypeParameter: 672 Emit (OpCodes.Stelem, type); 673 break; 674 case MemberKind.PointerType: 675 Emit (OpCodes.Stelem_I); 676 break; 677 default: 678 Emit (OpCodes.Stelem_Ref); 679 break; 680 } 681 } 682 EmitInt(int i)683 public void EmitInt (int i) 684 { 685 EmitIntConstant (i); 686 } 687 EmitIntConstant(int i)688 void EmitIntConstant (int i) 689 { 690 switch (i) { 691 case -1: 692 ig.Emit (OpCodes.Ldc_I4_M1); 693 break; 694 695 case 0: 696 ig.Emit (OpCodes.Ldc_I4_0); 697 break; 698 699 case 1: 700 ig.Emit (OpCodes.Ldc_I4_1); 701 break; 702 703 case 2: 704 ig.Emit (OpCodes.Ldc_I4_2); 705 break; 706 707 case 3: 708 ig.Emit (OpCodes.Ldc_I4_3); 709 break; 710 711 case 4: 712 ig.Emit (OpCodes.Ldc_I4_4); 713 break; 714 715 case 5: 716 ig.Emit (OpCodes.Ldc_I4_5); 717 break; 718 719 case 6: 720 ig.Emit (OpCodes.Ldc_I4_6); 721 break; 722 723 case 7: 724 ig.Emit (OpCodes.Ldc_I4_7); 725 break; 726 727 case 8: 728 ig.Emit (OpCodes.Ldc_I4_8); 729 break; 730 731 default: 732 if (i >= -128 && i <= 127) { 733 ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i); 734 } else 735 ig.Emit (OpCodes.Ldc_I4, i); 736 break; 737 } 738 } 739 EmitLong(long l)740 public void EmitLong (long l) 741 { 742 if (l >= int.MinValue && l <= int.MaxValue) { 743 EmitIntConstant (unchecked ((int) l)); 744 ig.Emit (OpCodes.Conv_I8); 745 } else if (l >= 0 && l <= uint.MaxValue) { 746 EmitIntConstant (unchecked ((int) l)); 747 ig.Emit (OpCodes.Conv_U8); 748 } else { 749 ig.Emit (OpCodes.Ldc_I8, l); 750 } 751 } 752 753 // 754 // Load the object from the pointer. 755 // EmitLoadFromPtr(TypeSpec type)756 public void EmitLoadFromPtr (TypeSpec type) 757 { 758 if (type.Kind == MemberKind.Enum) 759 type = EnumSpec.GetUnderlyingType (type); 760 761 switch (type.BuiltinType) { 762 case BuiltinTypeSpec.Type.Int: 763 ig.Emit (OpCodes.Ldind_I4); 764 break; 765 case BuiltinTypeSpec.Type.UInt: 766 ig.Emit (OpCodes.Ldind_U4); 767 break; 768 case BuiltinTypeSpec.Type.Short: 769 ig.Emit (OpCodes.Ldind_I2); 770 break; 771 case BuiltinTypeSpec.Type.UShort: 772 case BuiltinTypeSpec.Type.Char: 773 ig.Emit (OpCodes.Ldind_U2); 774 break; 775 case BuiltinTypeSpec.Type.Byte: 776 ig.Emit (OpCodes.Ldind_U1); 777 break; 778 case BuiltinTypeSpec.Type.SByte: 779 case BuiltinTypeSpec.Type.Bool: 780 ig.Emit (OpCodes.Ldind_I1); 781 break; 782 case BuiltinTypeSpec.Type.ULong: 783 case BuiltinTypeSpec.Type.Long: 784 ig.Emit (OpCodes.Ldind_I8); 785 break; 786 case BuiltinTypeSpec.Type.Float: 787 ig.Emit (OpCodes.Ldind_R4); 788 break; 789 case BuiltinTypeSpec.Type.Double: 790 ig.Emit (OpCodes.Ldind_R8); 791 break; 792 case BuiltinTypeSpec.Type.IntPtr: 793 ig.Emit (OpCodes.Ldind_I); 794 break; 795 default: 796 switch (type.Kind) { 797 case MemberKind.Struct: 798 case MemberKind.TypeParameter: 799 if (IsAnonymousStoreyMutateRequired) 800 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 801 802 ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ()); 803 break; 804 case MemberKind.PointerType: 805 ig.Emit (OpCodes.Ldind_I); 806 break; 807 default: 808 ig.Emit (OpCodes.Ldind_Ref); 809 break; 810 } 811 break; 812 } 813 } 814 EmitNull()815 public void EmitNull () 816 { 817 ig.Emit (OpCodes.Ldnull); 818 } 819 EmitArgumentAddress(int pos)820 public void EmitArgumentAddress (int pos) 821 { 822 if (!IsStatic) 823 ++pos; 824 825 if (pos > byte.MaxValue) 826 ig.Emit (OpCodes.Ldarga, pos); 827 else 828 ig.Emit (OpCodes.Ldarga_S, (byte) pos); 829 } 830 EmitArgumentLoad(int pos)831 public void EmitArgumentLoad (int pos) 832 { 833 if (!IsStatic) 834 ++pos; 835 836 switch (pos) { 837 case 0: ig.Emit (OpCodes.Ldarg_0); break; 838 case 1: ig.Emit (OpCodes.Ldarg_1); break; 839 case 2: ig.Emit (OpCodes.Ldarg_2); break; 840 case 3: ig.Emit (OpCodes.Ldarg_3); break; 841 default: 842 if (pos > byte.MaxValue) 843 ig.Emit (OpCodes.Ldarg, pos); 844 else 845 ig.Emit (OpCodes.Ldarg_S, (byte) pos); 846 break; 847 } 848 } 849 EmitArgumentStore(int pos)850 public void EmitArgumentStore (int pos) 851 { 852 if (!IsStatic) 853 ++pos; 854 855 if (pos > byte.MaxValue) 856 ig.Emit (OpCodes.Starg, pos); 857 else 858 ig.Emit (OpCodes.Starg_S, (byte) pos); 859 } 860 861 // 862 // The stack contains the pointer and the value of type `type' 863 // EmitStoreFromPtr(TypeSpec type)864 public void EmitStoreFromPtr (TypeSpec type) 865 { 866 if (type.IsEnum) 867 type = EnumSpec.GetUnderlyingType (type); 868 869 switch (type.BuiltinType) { 870 case BuiltinTypeSpec.Type.Int: 871 case BuiltinTypeSpec.Type.UInt: 872 ig.Emit (OpCodes.Stind_I4); 873 return; 874 case BuiltinTypeSpec.Type.Long: 875 case BuiltinTypeSpec.Type.ULong: 876 ig.Emit (OpCodes.Stind_I8); 877 return; 878 case BuiltinTypeSpec.Type.Char: 879 case BuiltinTypeSpec.Type.Short: 880 case BuiltinTypeSpec.Type.UShort: 881 ig.Emit (OpCodes.Stind_I2); 882 return; 883 case BuiltinTypeSpec.Type.Float: 884 ig.Emit (OpCodes.Stind_R4); 885 return; 886 case BuiltinTypeSpec.Type.Double: 887 ig.Emit (OpCodes.Stind_R8); 888 return; 889 case BuiltinTypeSpec.Type.Byte: 890 case BuiltinTypeSpec.Type.SByte: 891 case BuiltinTypeSpec.Type.Bool: 892 ig.Emit (OpCodes.Stind_I1); 893 return; 894 case BuiltinTypeSpec.Type.IntPtr: 895 ig.Emit (OpCodes.Stind_I); 896 return; 897 } 898 899 switch (type.Kind) { 900 case MemberKind.Struct: 901 case MemberKind.TypeParameter: 902 if (IsAnonymousStoreyMutateRequired) 903 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type); 904 905 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ()); 906 break; 907 case MemberKind.PointerType: 908 ig.Emit (OpCodes.Stind_I); 909 break; 910 default: 911 ig.Emit (OpCodes.Stind_Ref); 912 break; 913 } 914 } 915 EmitThis()916 public void EmitThis () 917 { 918 ig.Emit (OpCodes.Ldarg_0); 919 } 920 EmitEpilogue()921 public void EmitEpilogue () 922 { 923 if (epilogue_expressions == null) 924 return; 925 926 foreach (var e in epilogue_expressions) 927 e.EmitCleanup (this); 928 929 epilogue_expressions = null; 930 } 931 932 /// <summary> 933 /// Returns a temporary storage for a variable of type t as 934 /// a local variable in the current body. 935 /// </summary> GetTemporaryLocal(TypeSpec t)936 public LocalBuilder GetTemporaryLocal (TypeSpec t) 937 { 938 if (temporary_storage != null) { 939 object o; 940 if (temporary_storage.TryGetValue (t, out o)) { 941 if (o is Stack<LocalBuilder>) { 942 var s = (Stack<LocalBuilder>) o; 943 o = s.Count == 0 ? null : s.Pop (); 944 } else { 945 temporary_storage.Remove (t); 946 } 947 } 948 if (o != null) 949 return (LocalBuilder) o; 950 } 951 return DeclareLocal (t, false); 952 } 953 FreeTemporaryLocal(LocalBuilder b, TypeSpec t)954 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t) 955 { 956 if (temporary_storage == null) { 957 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default); 958 temporary_storage.Add (t, b); 959 return; 960 } 961 object o; 962 963 if (!temporary_storage.TryGetValue (t, out o)) { 964 temporary_storage.Add (t, b); 965 return; 966 } 967 var s = o as Stack<LocalBuilder>; 968 if (s == null) { 969 s = new Stack<LocalBuilder> (); 970 s.Push ((LocalBuilder)o); 971 temporary_storage [t] = s; 972 } 973 s.Push (b); 974 } 975 976 /// <summary> 977 /// ReturnValue creates on demand the LocalBuilder for the 978 /// return value from the function. By default this is not 979 /// used. This is only required when returns are found inside 980 /// Try or Catch statements. 981 /// 982 /// This method is typically invoked from the Emit phase, so 983 /// we allow the creation of a return label if it was not 984 /// requested during the resolution phase. Could be cleaned 985 /// up, but it would replicate a lot of logic in the Emit phase 986 /// of the code that uses it. 987 /// </summary> TemporaryReturn()988 public LocalBuilder TemporaryReturn () 989 { 990 if (return_value == null){ 991 return_value = DeclareLocal (return_type, false); 992 } 993 994 return return_value; 995 } 996 } 997 998 public class ConditionalAccessContext 999 { ConditionalAccessContext(TypeSpec type, Label endLabel)1000 public ConditionalAccessContext (TypeSpec type, Label endLabel) 1001 { 1002 Type = type; 1003 EndLabel = endLabel; 1004 } 1005 1006 public bool Statement { get; set; } 1007 public Label EndLabel { get; private set; } 1008 public TypeSpec Type { get; private set; } 1009 } 1010 1011 struct CallEmitter 1012 { 1013 public Expression InstanceExpression; 1014 1015 // 1016 // When call has to leave an extra copy of all arguments on the stack 1017 // 1018 public bool DuplicateArguments; 1019 1020 // 1021 // Does not emit InstanceExpression load when InstanceExpressionOnStack 1022 // is set. Used by compound assignments. 1023 // 1024 public bool InstanceExpressionOnStack; 1025 1026 // 1027 // Any of arguments contains await expression 1028 // 1029 public bool HasAwaitArguments; 1030 1031 public bool ConditionalAccess; 1032 1033 // 1034 // When dealing with await arguments the original arguments are converted 1035 // into a new set with hoisted stack results 1036 // 1037 public Arguments EmittedArguments; 1038 EmitMono.CSharp.CallEmitter1039 public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) 1040 { 1041 EmitPredefined (ec, method, Arguments, false, loc); 1042 } 1043 EmitStatementMono.CSharp.CallEmitter1044 public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc) 1045 { 1046 EmitPredefined (ec, method, Arguments, true, loc); 1047 } 1048 EmitPredefinedMono.CSharp.CallEmitter1049 public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null) 1050 { 1051 Expression instance_copy = null; 1052 1053 if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) { 1054 HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait (); 1055 if (HasAwaitArguments && InstanceExpressionOnStack) { 1056 throw new NotSupportedException (); 1057 } 1058 } 1059 1060 OpCode call_op; 1061 LocalTemporary lt = null; 1062 1063 if (method.IsStatic) { 1064 call_op = OpCodes.Call; 1065 } else { 1066 call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call; 1067 1068 if (HasAwaitArguments) { 1069 instance_copy = InstanceExpression.EmitToField (ec); 1070 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); 1071 1072 if (Arguments == null) { 1073 if (ConditionalAccess) 1074 ie.Emit (ec, true); 1075 else 1076 ie.EmitLoad (ec, true); 1077 } 1078 } else if (!InstanceExpressionOnStack) { 1079 var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType)); 1080 ie.Emit (ec, ConditionalAccess); 1081 1082 if (DuplicateArguments) { 1083 ec.Emit (OpCodes.Dup); 1084 if (Arguments != null && Arguments.Count != 0) { 1085 lt = new LocalTemporary (ie.GetStackType (ec)); 1086 lt.Store (ec); 1087 instance_copy = lt; 1088 } 1089 } 1090 } 1091 } 1092 1093 if (Arguments != null && !InstanceExpressionOnStack) { 1094 EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments); 1095 if (EmittedArguments != null) { 1096 if (instance_copy != null) { 1097 var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType)); 1098 ie.Emit (ec, ConditionalAccess); 1099 1100 if (lt != null) 1101 lt.Release (ec); 1102 } 1103 1104 EmittedArguments.Emit (ec); 1105 } 1106 } 1107 1108 if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) { 1109 ec.Emit (OpCodes.Constrained, InstanceExpression.Type); 1110 } 1111 1112 if (loc != null) { 1113 // 1114 // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to 1115 // break at right place when LHS expression can be stepped-into 1116 // 1117 ec.MarkCallEntry (loc.Value); 1118 } 1119 1120 // 1121 // Set instance expression to actual result expression. When it contains await it can be 1122 // picked up by caller 1123 // 1124 InstanceExpression = instance_copy; 1125 1126 if (method.Parameters.HasArglist) { 1127 var varargs_types = GetVarargsTypes (method, Arguments); 1128 ec.Emit (call_op, method, varargs_types); 1129 } else { 1130 // 1131 // If you have: 1132 // this.DoFoo (); 1133 // and DoFoo is not virtual, you can omit the callvirt, 1134 // because you don't need the null checking behavior. 1135 // 1136 ec.Emit (call_op, method); 1137 } 1138 1139 // 1140 // Pop the return value if there is one and stack should be empty 1141 // 1142 if (statement && method.ReturnType.Kind != MemberKind.Void) 1143 ec.Emit (OpCodes.Pop); 1144 } 1145 GetVarargsTypesMono.CSharp.CallEmitter1146 static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments) 1147 { 1148 AParametersCollection pd = method.Parameters; 1149 1150 Argument a = arguments[pd.Count - 1]; 1151 Arglist list = (Arglist) a.Expr; 1152 1153 return list.ArgumentTypes; 1154 } 1155 1156 // 1157 // Used to decide whether call or callvirt is needed 1158 // IsVirtualCallRequiredMono.CSharp.CallEmitter1159 static bool IsVirtualCallRequired (Expression instance, MethodSpec method) 1160 { 1161 // 1162 // There are 2 scenarious where we emit callvirt 1163 // 1164 // Case 1: A method is virtual and it's not used to call base 1165 // Case 2: A method instance expression can be null. In this casen callvirt ensures 1166 // correct NRE exception when the method is called 1167 // 1168 var decl_type = method.DeclaringType; 1169 if (decl_type.IsStruct || decl_type.IsEnum) 1170 return false; 1171 1172 if (instance is BaseThis) 1173 return false; 1174 1175 // 1176 // It's non-virtual and will never be null and it can be determined 1177 // whether it's known value or reference type by verifier 1178 // 1179 if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter) 1180 return false; 1181 1182 return true; 1183 } 1184 IsAddressCallMono.CSharp.CallEmitter1185 static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType) 1186 { 1187 var instance_type = instance.Type; 1188 return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) || 1189 instance_type.IsGenericParameter || declaringType.IsNullableType; 1190 } 1191 } 1192 1193 public struct InstanceEmitter 1194 { 1195 readonly Expression instance; 1196 readonly bool addressRequired; 1197 InstanceEmitterMono.CSharp.InstanceEmitter1198 public InstanceEmitter (Expression instance, bool addressLoad) 1199 { 1200 this.instance = instance; 1201 this.addressRequired = addressLoad; 1202 } 1203 EmitMono.CSharp.InstanceEmitter1204 public void Emit (EmitContext ec, bool conditionalAccess) 1205 { 1206 Label NullOperatorLabel; 1207 Nullable.Unwrap unwrap; 1208 1209 if (conditionalAccess && Expression.IsNeverNull (instance)) 1210 conditionalAccess = false; 1211 1212 if (conditionalAccess) { 1213 NullOperatorLabel = ec.DefineLabel (); 1214 unwrap = instance as Nullable.Unwrap; 1215 } else { 1216 NullOperatorLabel = new Label (); 1217 unwrap = null; 1218 } 1219 1220 IMemoryLocation instance_address = null; 1221 bool conditional_access_dup = false; 1222 1223 if (unwrap != null) { 1224 unwrap.Store (ec); 1225 unwrap.EmitCheck (ec); 1226 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); 1227 } else { 1228 if (conditionalAccess && addressRequired) { 1229 // 1230 // Don't allocate temp variable when instance load is cheap and load and load-address 1231 // operate on same memory 1232 // 1233 instance_address = instance as VariableReference; 1234 if (instance_address == null) 1235 instance_address = instance as LocalTemporary; 1236 1237 if (instance_address == null) { 1238 EmitLoad (ec, false); 1239 ec.Emit (OpCodes.Dup); 1240 ec.EmitLoadFromPtr (instance.Type); 1241 1242 conditional_access_dup = true; 1243 } else { 1244 instance.Emit (ec); 1245 } 1246 } else { 1247 EmitLoad (ec, !conditionalAccess); 1248 1249 if (conditionalAccess) { 1250 conditional_access_dup = !ExpressionAnalyzer.IsInexpensiveLoad (instance); 1251 if (conditional_access_dup) 1252 ec.Emit (OpCodes.Dup); 1253 } 1254 } 1255 1256 if (conditionalAccess) { 1257 if (instance.Type.Kind == MemberKind.TypeParameter) 1258 ec.Emit (OpCodes.Box, instance.Type); 1259 1260 ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel); 1261 1262 if (conditional_access_dup) 1263 ec.Emit (OpCodes.Pop); 1264 } 1265 } 1266 1267 if (conditionalAccess) { 1268 if (!ec.ConditionalAccess.Statement) { 1269 var t = ec.ConditionalAccess.Type; 1270 if (t.IsNullableType) 1271 Nullable.LiftedNull.Create (t, Location.Null).Emit (ec); 1272 else { 1273 ec.EmitNull (); 1274 1275 if (t.IsGenericParameter) 1276 ec.Emit (OpCodes.Unbox_Any, t); 1277 } 1278 } 1279 1280 ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel); 1281 ec.MarkLabel (NullOperatorLabel); 1282 1283 if (instance_address != null) { 1284 instance_address.AddressOf (ec, AddressOp.Load); 1285 } else if (unwrap != null) { 1286 unwrap.Emit (ec); 1287 if (addressRequired) { 1288 var tmp = ec.GetTemporaryLocal (unwrap.Type); 1289 ec.Emit (OpCodes.Stloc, tmp); 1290 ec.Emit (OpCodes.Ldloca, tmp); 1291 ec.FreeTemporaryLocal (tmp, unwrap.Type); 1292 } 1293 } else if (!conditional_access_dup) { 1294 instance.Emit (ec); 1295 } 1296 } 1297 } 1298 EmitLoadMono.CSharp.InstanceEmitter1299 public void EmitLoad (EmitContext ec, bool boxInstance) 1300 { 1301 var instance_type = instance.Type; 1302 1303 // 1304 // Push the instance expression 1305 // 1306 if (addressRequired) { 1307 // 1308 // If the expression implements IMemoryLocation, then 1309 // we can optimize and use AddressOf on the 1310 // return. 1311 // 1312 // If not we have to use some temporary storage for 1313 // it. 1314 var iml = instance as IMemoryLocation; 1315 if (iml != null) { 1316 iml.AddressOf (ec, AddressOp.Load); 1317 } else { 1318 LocalTemporary temp = new LocalTemporary (instance_type); 1319 instance.Emit (ec); 1320 temp.Store (ec); 1321 temp.AddressOf (ec, AddressOp.Load); 1322 } 1323 1324 return; 1325 } 1326 1327 instance.Emit (ec); 1328 1329 // Only to make verifier happy 1330 if (boxInstance && ExpressionAnalyzer.RequiresBoxing (instance)) { 1331 ec.Emit (OpCodes.Box, instance_type); 1332 } 1333 } 1334 GetStackTypeMono.CSharp.InstanceEmitter1335 public TypeSpec GetStackType (EmitContext ec) 1336 { 1337 var instance_type = instance.Type; 1338 1339 if (addressRequired) 1340 return ReferenceContainer.MakeType (ec.Module, instance_type); 1341 1342 if (instance_type.IsStructOrEnum) 1343 return ec.Module.Compiler.BuiltinTypes.Object; 1344 1345 return instance_type; 1346 } 1347 1348 1349 } 1350 1351 static class ExpressionAnalyzer 1352 { RequiresBoxing(Expression instance)1353 public static bool RequiresBoxing (Expression instance) 1354 { 1355 var instance_type = instance.Type; 1356 if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) 1357 return true; 1358 1359 if (instance_type.IsStructOrEnum) 1360 return true; 1361 1362 return false; 1363 } 1364 1365 // 1366 // Returns true for cheap race-free load, where we can avoid using dup 1367 // IsInexpensiveLoad(Expression instance)1368 public static bool IsInexpensiveLoad (Expression instance) 1369 { 1370 if (instance is Constant) 1371 return instance.IsSideEffectFree; 1372 1373 if (RequiresBoxing (instance)) 1374 return false; 1375 1376 var vr = instance as VariableReference; 1377 if (vr != null) { 1378 // Load from captured local would be racy without dup 1379 return !vr.IsRef && !vr.IsHoisted; 1380 } 1381 1382 if (instance is LocalTemporary) 1383 return true; 1384 1385 var fe = instance as FieldExpr; 1386 if (fe != null) 1387 return fe.IsStatic || fe.InstanceExpression is This; 1388 1389 return false; 1390 } 1391 } 1392 } 1393