Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/codegen.cs @ 11937

Last change on this file since 11937 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 32.5 KB
Line 
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
13using System;
14using System.Collections.Generic;
15using Mono.CompilerServices.SymbolWriter;
16
17#if STATIC
18using MetaType = IKVM.Reflection.Type;
19using IKVM.Reflection;
20using IKVM.Reflection.Emit;
21#else
22using MetaType = System.Type;
23using System.Reflection;
24using System.Reflection.Emit;
25#endif
26
27namespace 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
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    #endregion
226
227    public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
228    {
229      if (epilogue_expressions == null) {
230        epilogue_expressions = new List<IExpressionCleanup> ();
231      } else if (epilogue_expressions.Contains (cleanupExpression)) {
232        return;
233      }
234
235      epilogue_expressions.Add (cleanupExpression);
236    }
237
238    public void AssertEmptyStack ()
239    {
240#if STATIC
241      if (ig.__StackHeight != 0)
242        throw new InternalErrorException ("Await yields with non-empty stack in `{0}",
243          member_context.GetSignatureForError ());
244#endif
245    }
246
247    /// <summary>
248    ///   This is called immediately before emitting an IL opcode to tell the symbol
249    ///   writer to which source line this opcode belongs.
250    /// </summary>
251    public bool Mark (Location loc)
252    {
253      if ((flags & Options.OmitDebugInfo) != 0)
254        return false;
255
256      if (loc.IsNull || methodSymbols == null)
257        return false;
258
259      var sf = loc.SourceFile;
260      if (sf.IsHiddenLocation (loc))
261        return false;
262
263#if NET_4_0
264      methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
265#endif
266      return true;
267    }
268
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
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
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
300    public void BeginFilterHandler ()
301    {
302      ig.BeginCatchBlock (null);
303    }
304
305    public void BeginExceptionBlock ()
306    {
307      ig.BeginExceptionBlock ();
308    }
309
310    public void BeginExceptionFilterBlock ()
311    {
312      ig.BeginExceptFilterBlock ();
313    }
314
315    public void BeginFinallyBlock ()
316    {
317      ig.BeginFinallyBlock ();
318    }
319
320    public void BeginScope ()
321    {
322      if ((flags & Options.OmitDebugInfo) != 0)
323        return;
324
325#if NET_4_0
326      methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
327#endif
328    }
329
330    public void BeginCompilerScope ()
331    {
332      if ((flags & Options.OmitDebugInfo) != 0)
333        return;
334
335#if NET_4_0
336      methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
337#endif
338    }
339
340    public void EndExceptionBlock ()
341    {
342      ig.EndExceptionBlock ();
343    }
344
345    public void EndScope ()
346    {
347      if ((flags & Options.OmitDebugInfo) != 0)
348        return;
349
350#if NET_4_0
351      methodSymbols.EndBlock (ig.ILOffset);
352#endif
353    }
354
355    public void CloseConditionalAccess (TypeSpec type)
356    {
357      if (type != null)
358        Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
359
360      MarkLabel (ConditionalAccess.EndLabel);
361      ConditionalAccess = null;
362    }
363
364    //
365    // Creates a nested container in this context for all dynamic compiler generated stuff
366    //
367    internal DynamicSiteClass CreateDynamicSite ()
368    {
369      if (dynamic_site_container == null) {
370        var mc = member_context.CurrentMemberDefinition as MemberBase;
371        dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, member_context.CurrentTypeParameters);
372
373        CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
374        dynamic_site_container.CreateContainer ();
375        dynamic_site_container.DefineContainer ();
376        dynamic_site_container.Define ();
377
378        var inflator = new TypeParameterInflator (Module, CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
379        var inflated = dynamic_site_container.CurrentType.InflateMember (inflator);
380        CurrentType.MemberCache.AddMember (inflated);
381      }
382
383      return dynamic_site_container;
384    }
385
386    public Label CreateReturnLabel ()
387    {
388      if (!return_label.HasValue)
389        return_label = DefineLabel ();
390
391      return return_label.Value;
392    }
393
394    public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
395    {
396      if (IsAnonymousStoreyMutateRequired)
397        type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
398
399      return ig.DeclareLocal (type.GetMetaInfo (), pinned);
400    }
401
402    public Label DefineLabel ()
403    {
404      return ig.DefineLabel ();
405    }
406
407    //
408    // Creates temporary field in current async storey
409    //
410    public StackFieldExpr GetTemporaryField (TypeSpec type, bool initializedFieldRequired = false)
411    {
412      var f = AsyncTaskStorey.AddCapturedLocalVariable (type, initializedFieldRequired);
413      var fexpr = new StackFieldExpr (f);
414      fexpr.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null);
415      return fexpr;
416    }
417
418    public void MarkLabel (Label label)
419    {
420      ig.MarkLabel (label);
421    }
422
423    public void Emit (OpCode opcode)
424    {
425      ig.Emit (opcode);
426    }
427
428    public void Emit (OpCode opcode, LocalBuilder local)
429    {
430      ig.Emit (opcode, local);
431    }
432
433    public void Emit (OpCode opcode, string arg)
434    {
435      ig.Emit (opcode, arg);
436    }
437
438    public void Emit (OpCode opcode, double arg)
439    {
440      ig.Emit (opcode, arg);
441    }
442
443    public void Emit (OpCode opcode, float arg)
444    {
445      ig.Emit (opcode, arg);
446    }
447
448    public void Emit (OpCode opcode, Label label)
449    {
450      ig.Emit (opcode, label);
451    }
452
453    public void Emit (OpCode opcode, Label[] labels)
454    {
455      ig.Emit (opcode, labels);
456    }
457
458    public void Emit (OpCode opcode, TypeSpec type)
459    {
460      if (IsAnonymousStoreyMutateRequired)
461        type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
462
463      ig.Emit (opcode, type.GetMetaInfo ());
464    }
465
466    public void Emit (OpCode opcode, FieldSpec field)
467    {
468      if (IsAnonymousStoreyMutateRequired)
469        field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
470
471      ig.Emit (opcode, field.GetMetaInfo ());
472    }
473
474    public void Emit (OpCode opcode, MethodSpec method)
475    {
476      if (IsAnonymousStoreyMutateRequired)
477        method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
478
479      if (method.IsConstructor)
480        ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
481      else
482        ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
483    }
484
485    // TODO: REMOVE breaks mutator
486    public void Emit (OpCode opcode, MethodInfo method)
487    {
488      ig.Emit (opcode, method);
489    }
490
491    public void Emit (OpCode opcode, MethodSpec method, MetaType[] vargs)
492    {
493      // TODO MemberCache: This should mutate too
494      ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
495    }
496
497    public void EmitArrayNew (ArrayContainer ac)
498    {
499      if (ac.Rank == 1) {
500        var type = IsAnonymousStoreyMutateRequired ?
501          CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
502          ac.Element;
503
504        ig.Emit (OpCodes.Newarr, type.GetMetaInfo ());
505      } else {
506        if (IsAnonymousStoreyMutateRequired)
507          ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
508
509        ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
510      }
511    }
512
513    public void EmitArrayAddress (ArrayContainer ac)
514    {
515      if (ac.Rank > 1) {
516        if (IsAnonymousStoreyMutateRequired)
517          ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
518
519        ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
520      } else {
521        var type = IsAnonymousStoreyMutateRequired ?
522          CurrentAnonymousMethod.Storey.Mutator.Mutate (ac.Element) :
523          ac.Element;
524
525        ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
526      }
527    }
528
529    //
530    // Emits the right opcode to load from an array
531    //
532    public void EmitArrayLoad (ArrayContainer ac)
533    {
534      if (ac.Rank > 1) {
535        if (IsAnonymousStoreyMutateRequired)
536          ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
537
538        ig.Emit (OpCodes.Call, ac.GetGetMethod ());
539        return;
540      }
541
542
543      var type = ac.Element;
544      if (type.Kind == MemberKind.Enum)
545        type = EnumSpec.GetUnderlyingType (type);
546
547      switch (type.BuiltinType) {
548      case BuiltinTypeSpec.Type.Bool:
549        //
550        // Workaround MSIL limitation. Load bool element as single bit,
551        // bool array can actually store any byte value
552        //
553        ig.Emit (OpCodes.Ldelem_U1);
554        ig.Emit (OpCodes.Ldc_I4_0);
555        ig.Emit (OpCodes.Cgt_Un);
556        break;
557      case BuiltinTypeSpec.Type.Byte:
558        ig.Emit (OpCodes.Ldelem_U1);
559        break;
560      case BuiltinTypeSpec.Type.SByte:
561        ig.Emit (OpCodes.Ldelem_I1);
562        break;
563      case BuiltinTypeSpec.Type.Short:
564        ig.Emit (OpCodes.Ldelem_I2);
565        break;
566      case BuiltinTypeSpec.Type.UShort:
567      case BuiltinTypeSpec.Type.Char:
568        ig.Emit (OpCodes.Ldelem_U2);
569        break;
570      case BuiltinTypeSpec.Type.Int:
571        ig.Emit (OpCodes.Ldelem_I4);
572        break;
573      case BuiltinTypeSpec.Type.UInt:
574        ig.Emit (OpCodes.Ldelem_U4);
575        break;
576      case BuiltinTypeSpec.Type.ULong:
577      case BuiltinTypeSpec.Type.Long:
578        ig.Emit (OpCodes.Ldelem_I8);
579        break;
580      case BuiltinTypeSpec.Type.Float:
581        ig.Emit (OpCodes.Ldelem_R4);
582        break;
583      case BuiltinTypeSpec.Type.Double:
584        ig.Emit (OpCodes.Ldelem_R8);
585        break;
586      case BuiltinTypeSpec.Type.IntPtr:
587        ig.Emit (OpCodes.Ldelem_I);
588        break;
589      default:
590        switch (type.Kind) {
591        case MemberKind.Struct:
592          if (IsAnonymousStoreyMutateRequired)
593            type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
594
595          ig.Emit (OpCodes.Ldelema, type.GetMetaInfo ());
596          ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
597          break;
598        case MemberKind.TypeParameter:
599          if (IsAnonymousStoreyMutateRequired)
600            type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
601
602          ig.Emit (OpCodes.Ldelem, type.GetMetaInfo ());
603          break;
604        case MemberKind.PointerType:
605          ig.Emit (OpCodes.Ldelem_I);
606          break;
607        default:
608          ig.Emit (OpCodes.Ldelem_Ref);
609          break;
610        }
611        break;
612      }
613    }
614
615    //
616    // Emits the right opcode to store to an array
617    //
618    public void EmitArrayStore (ArrayContainer ac)
619    {
620      if (ac.Rank > 1) {
621        if (IsAnonymousStoreyMutateRequired)
622          ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
623
624        ig.Emit (OpCodes.Call, ac.GetSetMethod ());
625        return;
626      }
627
628      var type = ac.Element;
629
630      if (type.Kind == MemberKind.Enum)
631        type = EnumSpec.GetUnderlyingType (type);
632
633      switch (type.BuiltinType) {
634      case BuiltinTypeSpec.Type.Byte:
635      case BuiltinTypeSpec.Type.SByte:
636      case BuiltinTypeSpec.Type.Bool:
637        Emit (OpCodes.Stelem_I1);
638        return;
639      case BuiltinTypeSpec.Type.Short:
640      case BuiltinTypeSpec.Type.UShort:
641      case BuiltinTypeSpec.Type.Char:
642        Emit (OpCodes.Stelem_I2);
643        return;
644      case BuiltinTypeSpec.Type.Int:
645      case BuiltinTypeSpec.Type.UInt:
646        Emit (OpCodes.Stelem_I4);
647        return;
648      case BuiltinTypeSpec.Type.Long:
649      case BuiltinTypeSpec.Type.ULong:
650        Emit (OpCodes.Stelem_I8);
651        return;
652      case BuiltinTypeSpec.Type.Float:
653        Emit (OpCodes.Stelem_R4);
654        return;
655      case BuiltinTypeSpec.Type.Double:
656        Emit (OpCodes.Stelem_R8);
657        return;
658      }
659
660      switch (type.Kind) {
661      case MemberKind.Struct:
662        Emit (OpCodes.Stobj, type);
663        break;
664      case MemberKind.TypeParameter:
665        Emit (OpCodes.Stelem, type);
666        break;
667      case MemberKind.PointerType:
668        Emit (OpCodes.Stelem_I);
669        break;
670      default:
671        Emit (OpCodes.Stelem_Ref);
672        break;
673      }
674    }
675
676    public void EmitInt (int i)
677    {
678      EmitIntConstant (i);
679    }
680
681    void EmitIntConstant (int i)
682    {
683      switch (i) {
684      case -1:
685        ig.Emit (OpCodes.Ldc_I4_M1);
686        break;
687
688      case 0:
689        ig.Emit (OpCodes.Ldc_I4_0);
690        break;
691
692      case 1:
693        ig.Emit (OpCodes.Ldc_I4_1);
694        break;
695
696      case 2:
697        ig.Emit (OpCodes.Ldc_I4_2);
698        break;
699
700      case 3:
701        ig.Emit (OpCodes.Ldc_I4_3);
702        break;
703
704      case 4:
705        ig.Emit (OpCodes.Ldc_I4_4);
706        break;
707
708      case 5:
709        ig.Emit (OpCodes.Ldc_I4_5);
710        break;
711
712      case 6:
713        ig.Emit (OpCodes.Ldc_I4_6);
714        break;
715
716      case 7:
717        ig.Emit (OpCodes.Ldc_I4_7);
718        break;
719
720      case 8:
721        ig.Emit (OpCodes.Ldc_I4_8);
722        break;
723
724      default:
725        if (i >= -128 && i <= 127) {
726          ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
727        } else
728          ig.Emit (OpCodes.Ldc_I4, i);
729        break;
730      }
731    }
732
733    public void EmitLong (long l)
734    {
735      if (l >= int.MinValue && l <= int.MaxValue) {
736        EmitIntConstant (unchecked ((int) l));
737        ig.Emit (OpCodes.Conv_I8);
738      } else if (l >= 0 && l <= uint.MaxValue) {
739        EmitIntConstant (unchecked ((int) l));
740        ig.Emit (OpCodes.Conv_U8);
741      } else {
742        ig.Emit (OpCodes.Ldc_I8, l);
743      }
744    }
745
746    //
747    // Load the object from the pointer. 
748    //
749    public void EmitLoadFromPtr (TypeSpec type)
750    {
751      if (type.Kind == MemberKind.Enum)
752        type = EnumSpec.GetUnderlyingType (type);
753
754      switch (type.BuiltinType) {
755      case BuiltinTypeSpec.Type.Int:
756        ig.Emit (OpCodes.Ldind_I4);
757        break;
758      case BuiltinTypeSpec.Type.UInt:
759        ig.Emit (OpCodes.Ldind_U4);
760        break;
761      case BuiltinTypeSpec.Type.Short:
762        ig.Emit (OpCodes.Ldind_I2);
763        break;
764      case BuiltinTypeSpec.Type.UShort:
765      case BuiltinTypeSpec.Type.Char:
766        ig.Emit (OpCodes.Ldind_U2);
767        break;
768      case BuiltinTypeSpec.Type.Byte:
769        ig.Emit (OpCodes.Ldind_U1);
770        break;
771      case BuiltinTypeSpec.Type.SByte:
772        ig.Emit (OpCodes.Ldind_I1);
773        break;
774      case BuiltinTypeSpec.Type.Bool:
775        ig.Emit (OpCodes.Ldind_I1);
776        ig.Emit (OpCodes.Ldc_I4_0);
777        ig.Emit (OpCodes.Cgt_Un);
778        break;
779      case BuiltinTypeSpec.Type.ULong:
780      case BuiltinTypeSpec.Type.Long:
781        ig.Emit (OpCodes.Ldind_I8);
782        break;
783      case BuiltinTypeSpec.Type.Float:
784        ig.Emit (OpCodes.Ldind_R4);
785        break;
786      case BuiltinTypeSpec.Type.Double:
787        ig.Emit (OpCodes.Ldind_R8);
788        break;
789      case BuiltinTypeSpec.Type.IntPtr:
790        ig.Emit (OpCodes.Ldind_I);
791        break;
792      default:
793        switch (type.Kind) {
794        case MemberKind.Struct:
795        case MemberKind.TypeParameter:
796          if (IsAnonymousStoreyMutateRequired)
797            type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
798
799          ig.Emit (OpCodes.Ldobj, type.GetMetaInfo ());
800          break;
801        case MemberKind.PointerType:
802          ig.Emit (OpCodes.Ldind_I);
803          break;
804        default:
805          ig.Emit (OpCodes.Ldind_Ref);
806          break;
807        }
808        break;
809      }
810    }
811
812    public void EmitNull ()
813    {
814      ig.Emit (OpCodes.Ldnull);
815    }
816
817    public void EmitArgumentAddress (int pos)
818    {
819      if (!IsStatic)
820        ++pos;
821
822      if (pos > byte.MaxValue)
823        ig.Emit (OpCodes.Ldarga, pos);
824      else
825        ig.Emit (OpCodes.Ldarga_S, (byte) pos);
826    }
827
828    public void EmitArgumentLoad (int pos)
829    {
830      if (!IsStatic)
831        ++pos;
832
833      switch (pos) {
834      case 0: ig.Emit (OpCodes.Ldarg_0); break;
835      case 1: ig.Emit (OpCodes.Ldarg_1); break;
836      case 2: ig.Emit (OpCodes.Ldarg_2); break;
837      case 3: ig.Emit (OpCodes.Ldarg_3); break;
838      default:
839        if (pos > byte.MaxValue)
840          ig.Emit (OpCodes.Ldarg, pos);
841        else
842          ig.Emit (OpCodes.Ldarg_S, (byte) pos);
843        break;
844      }
845    }
846
847    public void EmitArgumentStore (int pos)
848    {
849      if (!IsStatic)
850        ++pos;
851
852      if (pos > byte.MaxValue)
853        ig.Emit (OpCodes.Starg, pos);
854      else
855        ig.Emit (OpCodes.Starg_S, (byte) pos);
856    }
857
858    //
859    // The stack contains the pointer and the value of type `type'
860    //
861    public void EmitStoreFromPtr (TypeSpec type)
862    {
863      if (type.IsEnum)
864        type = EnumSpec.GetUnderlyingType (type);
865
866      switch (type.BuiltinType) {
867      case BuiltinTypeSpec.Type.Int:
868      case BuiltinTypeSpec.Type.UInt:
869        ig.Emit (OpCodes.Stind_I4);
870        return;
871      case BuiltinTypeSpec.Type.Long:
872      case BuiltinTypeSpec.Type.ULong:
873        ig.Emit (OpCodes.Stind_I8);
874        return;
875      case BuiltinTypeSpec.Type.Char:
876      case BuiltinTypeSpec.Type.Short:
877      case BuiltinTypeSpec.Type.UShort:
878        ig.Emit (OpCodes.Stind_I2);
879        return;
880      case BuiltinTypeSpec.Type.Float:
881        ig.Emit (OpCodes.Stind_R4);
882        return;
883      case BuiltinTypeSpec.Type.Double:
884        ig.Emit (OpCodes.Stind_R8);
885        return;
886      case BuiltinTypeSpec.Type.Byte:
887      case BuiltinTypeSpec.Type.SByte:
888      case BuiltinTypeSpec.Type.Bool:
889        ig.Emit (OpCodes.Stind_I1);
890        return;
891      case BuiltinTypeSpec.Type.IntPtr:
892        ig.Emit (OpCodes.Stind_I);
893        return;
894      }
895
896      switch (type.Kind) {
897      case MemberKind.Struct:
898      case MemberKind.TypeParameter:
899        if (IsAnonymousStoreyMutateRequired)
900          type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
901
902        ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
903        break;
904      default:
905        ig.Emit (OpCodes.Stind_Ref);
906        break;
907      }
908    }
909
910    public void EmitThis ()
911    {
912      ig.Emit (OpCodes.Ldarg_0);
913    }
914
915    public void EmitEpilogue ()
916    {
917      if (epilogue_expressions == null)
918        return;
919
920      foreach (var e in epilogue_expressions)
921        e.EmitCleanup (this);
922
923      epilogue_expressions = null;
924    }
925
926    /// <summary>
927    ///   Returns a temporary storage for a variable of type t as
928    ///   a local variable in the current body.
929    /// </summary>
930    public LocalBuilder GetTemporaryLocal (TypeSpec t)
931    {
932      if (temporary_storage != null) {
933        object o;
934        if (temporary_storage.TryGetValue (t, out o)) {
935          if (o is Stack<LocalBuilder>) {
936            var s = (Stack<LocalBuilder>) o;
937            o = s.Count == 0 ? null : s.Pop ();
938          } else {
939            temporary_storage.Remove (t);
940          }
941        }
942        if (o != null)
943          return (LocalBuilder) o;
944      }
945      return DeclareLocal (t, false);
946    }
947
948    public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
949    {
950      if (temporary_storage == null) {
951        temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
952        temporary_storage.Add (t, b);
953        return;
954      }
955      object o;
956     
957      if (!temporary_storage.TryGetValue (t, out o)) {
958        temporary_storage.Add (t, b);
959        return;
960      }
961      var s = o as Stack<LocalBuilder>;
962      if (s == null) {
963        s = new Stack<LocalBuilder> ();
964        s.Push ((LocalBuilder)o);
965        temporary_storage [t] = s;
966      }
967      s.Push (b);
968    }
969
970    /// <summary>
971    ///   ReturnValue creates on demand the LocalBuilder for the
972    ///   return value from the function.  By default this is not
973    ///   used.  This is only required when returns are found inside
974    ///   Try or Catch statements.
975    ///
976    ///   This method is typically invoked from the Emit phase, so
977    ///   we allow the creation of a return label if it was not
978    ///   requested during the resolution phase.   Could be cleaned
979    ///   up, but it would replicate a lot of logic in the Emit phase
980    ///   of the code that uses it.
981    /// </summary>
982    public LocalBuilder TemporaryReturn ()
983    {
984      if (return_value == null){
985        return_value = DeclareLocal (return_type, false);
986      }
987
988      return return_value;
989    }
990  }
991
992  public class ConditionalAccessContext
993  {
994    public ConditionalAccessContext (TypeSpec type, Label endLabel)
995    {
996      Type = type;
997      EndLabel = endLabel;
998    }
999
1000    public bool Statement { get; set; }
1001    public Label EndLabel { get; private set; }
1002    public TypeSpec Type { get; private set; }
1003  }
1004
1005  struct CallEmitter
1006  {
1007    public Expression InstanceExpression;
1008
1009    //
1010    // When call has to leave an extra copy of all arguments on the stack
1011    //
1012    public bool DuplicateArguments;
1013
1014    //
1015    // Does not emit InstanceExpression load when InstanceExpressionOnStack
1016    // is set. Used by compound assignments.
1017    //
1018    public bool InstanceExpressionOnStack;
1019
1020    //
1021    // Any of arguments contains await expression
1022    //
1023    public bool HasAwaitArguments;
1024
1025    public bool ConditionalAccess;
1026
1027    //
1028    // When dealing with await arguments the original arguments are converted
1029    // into a new set with hoisted stack results
1030    //
1031    public Arguments EmittedArguments;
1032
1033    public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1034    {
1035      EmitPredefined (ec, method, Arguments, false, loc);
1036    }
1037
1038    public void EmitStatement (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
1039    {
1040      EmitPredefined (ec, method, Arguments, true, loc);
1041    }
1042
1043    public void EmitPredefined (EmitContext ec, MethodSpec method, Arguments Arguments, bool statement = false, Location? loc = null)
1044    {
1045      Expression instance_copy = null;
1046
1047      if (!HasAwaitArguments && ec.HasSet (BuilderContext.Options.AsyncBody)) {
1048        HasAwaitArguments = Arguments != null && Arguments.ContainsEmitWithAwait ();
1049        if (HasAwaitArguments && InstanceExpressionOnStack) {
1050          throw new NotSupportedException ();
1051        }
1052      }
1053
1054      OpCode call_op;
1055      LocalTemporary lt = null;
1056
1057      if (method.IsStatic) {
1058        call_op = OpCodes.Call;
1059      } else {
1060        call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
1061
1062        if (HasAwaitArguments) {
1063          instance_copy = InstanceExpression.EmitToField (ec);
1064          var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1065
1066          if (Arguments == null) {
1067            ie.EmitLoad (ec);
1068          }
1069        } else if (!InstanceExpressionOnStack) {
1070          var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
1071          ie.Emit (ec, ConditionalAccess);
1072
1073          if (DuplicateArguments) {
1074            ec.Emit (OpCodes.Dup);
1075            if (Arguments != null && Arguments.Count != 0) {
1076              lt = new LocalTemporary (ie.GetStackType (ec));
1077              lt.Store (ec);
1078              instance_copy = lt;
1079            }
1080          }
1081        }
1082      }
1083
1084      if (Arguments != null && !InstanceExpressionOnStack) {
1085        EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
1086        if (EmittedArguments != null) {
1087          if (instance_copy != null) {
1088            var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
1089            ie.Emit (ec, ConditionalAccess);
1090
1091            if (lt != null)
1092              lt.Release (ec);
1093          }
1094
1095          EmittedArguments.Emit (ec);
1096        }
1097      }
1098
1099      if (call_op == OpCodes.Callvirt && (InstanceExpression.Type.IsGenericParameter || InstanceExpression.Type.IsStructOrEnum)) {
1100        ec.Emit (OpCodes.Constrained, InstanceExpression.Type);
1101      }
1102
1103      if (loc != null) {
1104        //
1105        // Emit explicit sequence point for expressions like Foo.Bar () to help debugger to
1106        // break at right place when LHS expression can be stepped-into
1107        //
1108        ec.MarkCallEntry (loc.Value);
1109      }
1110
1111      //
1112      // Set instance expression to actual result expression. When it contains await it can be
1113      // picked up by caller
1114      //
1115      InstanceExpression = instance_copy;
1116
1117      if (method.Parameters.HasArglist) {
1118        var varargs_types = GetVarargsTypes (method, Arguments);
1119        ec.Emit (call_op, method, varargs_types);
1120      } else {
1121        //
1122        // If you have:
1123        // this.DoFoo ();
1124        // and DoFoo is not virtual, you can omit the callvirt,
1125        // because you don't need the null checking behavior.
1126        //
1127        ec.Emit (call_op, method);
1128      }
1129
1130      //
1131      // Pop the return value if there is one and stack should be empty
1132      //
1133      if (statement && method.ReturnType.Kind != MemberKind.Void)
1134        ec.Emit (OpCodes.Pop);
1135    }
1136
1137    static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
1138    {
1139      AParametersCollection pd = method.Parameters;
1140
1141      Argument a = arguments[pd.Count - 1];
1142      Arglist list = (Arglist) a.Expr;
1143
1144      return list.ArgumentTypes;
1145    }
1146
1147    //
1148    // Used to decide whether call or callvirt is needed
1149    //
1150    static bool IsVirtualCallRequired (Expression instance, MethodSpec method)
1151    {
1152      //
1153      // There are 2 scenarious where we emit callvirt
1154      //
1155      // Case 1: A method is virtual and it's not used to call base
1156      // Case 2: A method instance expression can be null. In this casen callvirt ensures
1157      // correct NRE exception when the method is called
1158      //
1159      var decl_type = method.DeclaringType;
1160      if (decl_type.IsStruct || decl_type.IsEnum)
1161        return false;
1162
1163      if (instance is BaseThis)
1164        return false;
1165
1166      //
1167      // It's non-virtual and will never be null and it can be determined
1168      // whether it's known value or reference type by verifier
1169      //
1170      if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
1171        return false;
1172
1173      return true;
1174    }
1175
1176    static bool IsAddressCall (Expression instance, OpCode callOpcode, TypeSpec declaringType)
1177    {
1178      var instance_type = instance.Type;
1179      return (instance_type.IsStructOrEnum && (callOpcode == OpCodes.Callvirt || (callOpcode == OpCodes.Call && declaringType.IsStruct))) ||
1180        instance_type.IsGenericParameter || declaringType.IsNullableType;
1181    }
1182  }
1183
1184  public struct InstanceEmitter
1185  {
1186    readonly Expression instance;
1187    readonly bool addressRequired;
1188
1189    public InstanceEmitter (Expression instance, bool addressLoad)
1190    {
1191      this.instance = instance;
1192      this.addressRequired = addressLoad;
1193    }
1194
1195    public void Emit (EmitContext ec, bool conditionalAccess)
1196    {
1197      Label NullOperatorLabel;
1198      Nullable.Unwrap unwrap;
1199
1200      if (conditionalAccess && Expression.IsNeverNull (instance))
1201        conditionalAccess = false;
1202
1203      if (conditionalAccess) {
1204        NullOperatorLabel = ec.DefineLabel ();
1205        unwrap = instance as Nullable.Unwrap;
1206      } else {
1207        NullOperatorLabel = new Label ();
1208        unwrap = null;
1209      }
1210
1211      IMemoryLocation instance_address = null;
1212      bool conditional_access_dup = false;
1213
1214      if (unwrap != null) {
1215        unwrap.Store (ec);
1216        unwrap.EmitCheck (ec);
1217        ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1218      } else {
1219        if (conditionalAccess && addressRequired) {
1220          //
1221          // Don't allocate temp variable when instance load is cheap and load and load-address
1222          // operate on same memory
1223          //
1224          instance_address = instance as VariableReference;
1225          if (instance_address == null)
1226            instance_address = instance as LocalTemporary;
1227
1228          if (instance_address == null) {
1229            EmitLoad (ec);
1230            ec.Emit (OpCodes.Dup);
1231            ec.EmitLoadFromPtr (instance.Type);
1232
1233            conditional_access_dup = true;
1234          } else {
1235            instance.Emit (ec);
1236          }
1237
1238          if (instance.Type.Kind == MemberKind.TypeParameter)
1239            ec.Emit (OpCodes.Box, instance.Type);
1240        } else {
1241          EmitLoad (ec);
1242
1243          if (conditionalAccess) {
1244            conditional_access_dup = !IsInexpensiveLoad ();
1245            if (conditional_access_dup)
1246              ec.Emit (OpCodes.Dup);
1247          }
1248        }
1249
1250        if (conditionalAccess) {
1251          ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
1252
1253          if (conditional_access_dup)
1254            ec.Emit (OpCodes.Pop);
1255        }
1256      }
1257
1258      if (conditionalAccess) {
1259        if (!ec.ConditionalAccess.Statement) {
1260          if (ec.ConditionalAccess.Type.IsNullableType)
1261            Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
1262          else
1263            ec.EmitNull ();
1264        }
1265
1266        ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
1267        ec.MarkLabel (NullOperatorLabel);
1268
1269        if (instance_address != null) {
1270          instance_address.AddressOf (ec, AddressOp.Load);
1271        } else if (unwrap != null) {
1272          unwrap.Emit (ec);
1273          var tmp = ec.GetTemporaryLocal (unwrap.Type);
1274          ec.Emit (OpCodes.Stloc, tmp);
1275          ec.Emit (OpCodes.Ldloca, tmp);
1276          ec.FreeTemporaryLocal (tmp, unwrap.Type);
1277        } else if (!conditional_access_dup) {
1278          instance.Emit (ec);
1279        }
1280      }
1281    }
1282
1283    public void EmitLoad (EmitContext ec)
1284    {
1285      var instance_type = instance.Type;
1286
1287      //
1288      // Push the instance expression
1289      //
1290      if (addressRequired) {
1291        //
1292        // If the expression implements IMemoryLocation, then
1293        // we can optimize and use AddressOf on the
1294        // return.
1295        //
1296        // If not we have to use some temporary storage for
1297        // it.
1298        var iml = instance as IMemoryLocation;
1299        if (iml != null) {
1300          iml.AddressOf (ec, AddressOp.Load);
1301        } else {
1302          LocalTemporary temp = new LocalTemporary (instance_type);
1303          instance.Emit (ec);
1304          temp.Store (ec);
1305          temp.AddressOf (ec, AddressOp.Load);
1306        }
1307
1308        return;
1309      }
1310
1311      instance.Emit (ec);
1312
1313      // Only to make verifier happy
1314      if (RequiresBoxing ())
1315        ec.Emit (OpCodes.Box, instance_type);
1316    }
1317
1318    public TypeSpec GetStackType (EmitContext ec)
1319    {
1320      var instance_type = instance.Type;
1321
1322      if (addressRequired)
1323        return ReferenceContainer.MakeType (ec.Module, instance_type);
1324
1325      if (instance_type.IsStructOrEnum)
1326        return ec.Module.Compiler.BuiltinTypes.Object;
1327
1328      return instance_type;
1329    }
1330
1331    bool RequiresBoxing ()
1332    {
1333      var instance_type = instance.Type;
1334      if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
1335        return true;
1336
1337      if (instance_type.IsStructOrEnum)
1338        return true;
1339
1340      return false;
1341    }
1342
1343    bool IsInexpensiveLoad ()
1344    {
1345      if (instance is Constant)
1346        return instance.IsSideEffectFree;
1347
1348      if (RequiresBoxing ())
1349        return false;
1350
1351      var vr = instance as VariableReference;
1352      if (vr != null)
1353        return !vr.IsRef;
1354
1355      if (instance is LocalTemporary)
1356        return true;
1357
1358      var fe = instance as FieldExpr;
1359      if (fe != null)
1360        return fe.IsStatic || fe.InstanceExpression is This;
1361
1362      return false;
1363    }
1364  }
1365}
Note: See TracBrowser for help on using the repository browser.