Merge branch 'cecil-light'
[mono.git] / mcs / mcs / codegen.cs
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 //
11
12 using System;
13 using System.Collections.Generic;
14 using System.Reflection;
15 using System.Reflection.Emit;
16
17 namespace Mono.CSharp
18 {
19         /// <summary>
20         ///   An Emit Context is created for each body of code (from methods,
21         ///   properties bodies, indexer bodies or constructor bodies)
22         /// </summary>
23         public class EmitContext : BuilderContext
24         {
25                 // TODO: Has to be private
26                 public ILGenerator ig;
27
28                 /// <summary>
29                 ///   The value that is allowed to be returned or NULL if there is no
30                 ///   return type.
31                 /// </summary>
32                 TypeSpec return_type;
33
34                 /// <summary>
35                 ///   Keeps track of the Type to LocalBuilder temporary storage created
36                 ///   to store structures (used to compute the address of the structure
37                 ///   value on structure method invocations)
38                 /// </summary>
39                 Dictionary<TypeSpec, object> temporary_storage;
40
41                 /// <summary>
42                 ///   The location where we store the return value.
43                 /// </summary>
44                 public LocalBuilder return_value;
45
46                 /// <summary>
47                 ///   The location where return has to jump to return the
48                 ///   value
49                 /// </summary>
50                 public Label ReturnLabel;
51
52                 /// <summary>
53                 ///   If we already defined the ReturnLabel
54                 /// </summary>
55                 public bool HasReturnLabel;
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                 public readonly IMemberContext MemberContext;
79
80                 DynamicSiteClass dynamic_site_container;
81
82                 // TODO: Replace IMemberContext with MemberCore
83                 public EmitContext (IMemberContext rc, ILGenerator ig, TypeSpec return_type)
84                 {
85                         this.MemberContext = rc;
86                         this.ig = ig;
87
88                         this.return_type = return_type;
89                 }
90
91                 #region Properties
92
93                 public TypeSpec CurrentType {
94                         get { return MemberContext.CurrentType; }
95                 }
96
97                 public TypeParameter[] CurrentTypeParameters {
98                         get { return MemberContext.CurrentTypeParameters; }
99                 }
100
101                 public MemberCore CurrentTypeDefinition {
102                         get { return MemberContext.CurrentMemberDefinition; }
103                 }
104
105                 public bool IsStatic {
106                         get { return MemberContext.IsStatic; }
107                 }
108
109                 bool IsAnonymousStoreyMutateRequired {
110                         get {
111                                 return CurrentAnonymousMethod != null &&
112                                         CurrentAnonymousMethod.Storey != null &&
113                                         CurrentAnonymousMethod.Storey.Mutator != null;
114                         }
115                 }
116
117                 // Has to be used for emitter errors only
118                 public Report Report {
119                         get { return MemberContext.Compiler.Report; }
120                 }
121
122                 public TypeSpec ReturnType {
123                         get {
124                                 return return_type;
125                         }
126                 }
127                 #endregion
128
129                 /// <summary>
130                 ///   This is called immediately before emitting an IL opcode to tell the symbol
131                 ///   writer to which source line this opcode belongs.
132                 /// </summary>
133                 public void Mark (Location loc)
134                 {
135                         if (!SymbolWriter.HasSymbolWriter || HasSet (Options.OmitDebugInfo) || loc.IsNull)
136                                 return;
137
138                         SymbolWriter.MarkSequencePoint (ig, loc);
139                 }
140
141                 public void DefineLocalVariable (string name, LocalBuilder builder)
142                 {
143                         SymbolWriter.DefineLocalVariable (name, builder);
144                 }
145
146                 public void BeginCatchBlock (TypeSpec type)
147                 {
148                         ig.BeginCatchBlock (type.GetMetaInfo ());
149                 }
150
151                 public void BeginExceptionBlock ()
152                 {
153                         ig.BeginExceptionBlock ();
154                 }
155
156                 public void BeginFinallyBlock ()
157                 {
158                         ig.BeginFinallyBlock ();
159                 }
160
161                 public void BeginScope ()
162                 {
163                         ig.BeginScope();
164                         SymbolWriter.OpenScope(ig);
165                 }
166
167                 public void EndExceptionBlock ()
168                 {
169                         ig.EndExceptionBlock ();
170                 }
171
172                 public void EndScope ()
173                 {
174                         ig.EndScope();
175                         SymbolWriter.CloseScope(ig);
176                 }
177
178                 //
179                 // Creates a nested container in this context for all dynamic compiler generated stuff
180                 //
181                 public DynamicSiteClass CreateDynamicSite ()
182                 {
183                         if (dynamic_site_container == null) {
184                                 var mc = MemberContext.CurrentMemberDefinition as MemberBase;
185                                 dynamic_site_container = new DynamicSiteClass (CurrentTypeDefinition.Parent.PartialContainer, mc, CurrentTypeParameters);
186
187                                 CurrentTypeDefinition.Module.AddCompilerGeneratedClass (dynamic_site_container);
188                                 dynamic_site_container.CreateType ();
189                                 dynamic_site_container.DefineType ();
190                                 dynamic_site_container.ResolveTypeParameters ();
191                                 dynamic_site_container.Define ();
192                         }
193
194                         return dynamic_site_container;
195                 }
196
197                 public LocalBuilder DeclareLocal (TypeSpec type, bool pinned)
198                 {
199                         if (IsAnonymousStoreyMutateRequired)
200                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
201
202                         return ig.DeclareLocal (type.GetMetaInfo (), pinned);
203                 }
204
205                 public Label DefineLabel ()
206                 {
207                         return ig.DefineLabel ();
208                 }
209
210                 public void MarkLabel (Label label)
211                 {
212                         ig.MarkLabel (label);
213                 }
214
215                 public void Emit (OpCode opcode)
216                 {
217                         ig.Emit (opcode);
218                 }
219
220                 public void Emit (OpCode opcode, LocalBuilder local)
221                 {
222                         ig.Emit (opcode, local);
223                 }
224
225                 public void Emit (OpCode opcode, string arg)
226                 {
227                         ig.Emit (opcode, arg);
228                 }
229
230                 public void Emit (OpCode opcode, double arg)
231                 {
232                         ig.Emit (opcode, arg);
233                 }
234
235                 public void Emit (OpCode opcode, float arg)
236                 {
237                         ig.Emit (opcode, arg);
238                 }
239
240                 public void Emit (OpCode opcode, int arg)
241                 {
242                         ig.Emit (opcode, arg);
243                 }
244
245                 public void Emit (OpCode opcode, byte arg)
246                 {
247                         ig.Emit (opcode, arg);
248                 }
249
250                 public void Emit (OpCode opcode, Label label)
251                 {
252                         ig.Emit (opcode, label);
253                 }
254
255                 public void Emit (OpCode opcode, Label[] labels)
256                 {
257                         ig.Emit (opcode, labels);
258                 }
259
260                 public void Emit (OpCode opcode, TypeSpec type)
261                 {
262                         if (IsAnonymousStoreyMutateRequired)
263                                 type = CurrentAnonymousMethod.Storey.Mutator.Mutate (type);
264
265                         ig.Emit (opcode, type.GetMetaInfo ());
266                 }
267
268                 public void Emit (OpCode opcode, FieldSpec field)
269                 {
270                         if (IsAnonymousStoreyMutateRequired)
271                                 field = field.Mutate (CurrentAnonymousMethod.Storey.Mutator);
272
273                         ig.Emit (opcode, field.GetMetaInfo ());
274                 }
275
276                 public void Emit (OpCode opcode, MethodSpec method)
277                 {
278                         if (IsAnonymousStoreyMutateRequired)
279                                 method = method.Mutate (CurrentAnonymousMethod.Storey.Mutator);
280
281                         if (method.IsConstructor)
282                                 ig.Emit (opcode, (ConstructorInfo) method.GetMetaInfo ());
283                         else
284                                 ig.Emit (opcode, (MethodInfo) method.GetMetaInfo ());
285                 }
286
287                 // TODO: REMOVE breaks mutator
288                 public void Emit (OpCode opcode, MethodInfo method)
289                 {
290                         ig.Emit (opcode, method);
291                 }
292
293                 // TODO: REMOVE breaks mutator
294                 public void Emit (OpCode opcode, FieldBuilder field)
295                 {
296                         ig.Emit (opcode, field);
297                 }
298
299                 public void Emit (OpCode opcode, MethodSpec method, Type[] vargs)
300                 {
301                         // TODO MemberCache: This should mutate too
302                         ig.EmitCall (opcode, (MethodInfo) method.GetMetaInfo (), vargs);
303                 }
304
305                 public void EmitArrayNew (ArrayContainer ac)
306                 {
307                         if (ac.Rank == 1) {
308                                 Emit (OpCodes.Newarr, ac.Element);
309                         } else {
310                                 if (IsAnonymousStoreyMutateRequired)
311                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
312
313                                 ig.Emit (OpCodes.Newobj, ac.GetConstructor ());
314                         }
315                 }
316
317                 public void EmitArrayAddress (ArrayContainer ac)
318                 {
319                         if (ac.Element.IsGenericParameter)
320                                 ig.Emit (OpCodes.Readonly);
321
322                         if (ac.Rank > 1) {
323                                 if (IsAnonymousStoreyMutateRequired)
324                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
325
326                                 ig.Emit (OpCodes.Call, ac.GetAddressMethod ());
327                         } else {
328                                 Emit (OpCodes.Ldelema, ac.Element);
329                         }
330                 }
331
332                 //
333                 // Emits the right opcode to load from an array
334                 //
335                 public void EmitArrayLoad (ArrayContainer ac)
336                 {
337                         if (ac.Rank > 1) {
338                                 if (IsAnonymousStoreyMutateRequired)
339                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
340
341                                 ig.Emit (OpCodes.Call, ac.GetGetMethod ());
342                                 return;
343                         }
344
345                         var type = ac.Element;
346                         if (TypeManager.IsEnumType (type))
347                                 type = EnumSpec.GetUnderlyingType (type);
348
349                         if (type == TypeManager.byte_type || type == TypeManager.bool_type)
350                                 Emit (OpCodes.Ldelem_U1);
351                         else if (type == TypeManager.sbyte_type)
352                                 Emit (OpCodes.Ldelem_I1);
353                         else if (type == TypeManager.short_type)
354                                 Emit (OpCodes.Ldelem_I2);
355                         else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
356                                 Emit (OpCodes.Ldelem_U2);
357                         else if (type == TypeManager.int32_type)
358                                 Emit (OpCodes.Ldelem_I4);
359                         else if (type == TypeManager.uint32_type)
360                                 Emit (OpCodes.Ldelem_U4);
361                         else if (type == TypeManager.uint64_type)
362                                 Emit (OpCodes.Ldelem_I8);
363                         else if (type == TypeManager.int64_type)
364                                 Emit (OpCodes.Ldelem_I8);
365                         else if (type == TypeManager.float_type)
366                                 Emit (OpCodes.Ldelem_R4);
367                         else if (type == TypeManager.double_type)
368                                 Emit (OpCodes.Ldelem_R8);
369                         else if (type == TypeManager.intptr_type)
370                                 Emit (OpCodes.Ldelem_I);
371                         else if (TypeManager.IsStruct (type)) {
372                                 Emit (OpCodes.Ldelema, type);
373                                 Emit (OpCodes.Ldobj, type);
374                         } else if (type.IsGenericParameter) {
375                                 Emit (OpCodes.Ldelem, type);
376                         } else if (type.IsPointer)
377                                 Emit (OpCodes.Ldelem_I);
378                         else
379                                 Emit (OpCodes.Ldelem_Ref);
380                 }
381
382                 //
383                 // Emits the right opcode to store to an array
384                 //
385                 public void EmitArrayStore (ArrayContainer ac)
386                 {
387                         if (ac.Rank > 1) {
388                                 if (IsAnonymousStoreyMutateRequired)
389                                         ac = (ArrayContainer) ac.Mutate (CurrentAnonymousMethod.Storey.Mutator);
390
391                                 ig.Emit (OpCodes.Call, ac.GetSetMethod ());
392                                 return;
393                         }
394
395                         var type = ac.Element;
396
397                         if (type.IsEnum)
398                                 type = EnumSpec.GetUnderlyingType (type);
399
400                         if (type == TypeManager.byte_type || type == TypeManager.sbyte_type || type == TypeManager.bool_type)
401                                 Emit (OpCodes.Stelem_I1);
402                         else if (type == TypeManager.short_type || type == TypeManager.ushort_type || type == TypeManager.char_type)
403                                 Emit (OpCodes.Stelem_I2);
404                         else if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
405                                 Emit (OpCodes.Stelem_I4);
406                         else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
407                                 Emit (OpCodes.Stelem_I8);
408                         else if (type == TypeManager.float_type)
409                                 Emit (OpCodes.Stelem_R4);
410                         else if (type == TypeManager.double_type)
411                                 Emit (OpCodes.Stelem_R8);
412                         else if (type == TypeManager.intptr_type)
413                                 Emit (OpCodes.Stobj, type);
414                         else if (TypeManager.IsStruct (type))
415                                 Emit (OpCodes.Stobj, type);
416                         else if (type.IsGenericParameter)
417                                 Emit (OpCodes.Stelem, type);
418                         else if (type.IsPointer)
419                                 Emit (OpCodes.Stelem_I);
420                         else
421                                 Emit (OpCodes.Stelem_Ref);
422                 }
423
424                 public void EmitInt (int i)
425                 {
426                         switch (i) {
427                         case -1:
428                                 ig.Emit (OpCodes.Ldc_I4_M1);
429                                 break;
430
431                         case 0:
432                                 ig.Emit (OpCodes.Ldc_I4_0);
433                                 break;
434
435                         case 1:
436                                 ig.Emit (OpCodes.Ldc_I4_1);
437                                 break;
438
439                         case 2:
440                                 ig.Emit (OpCodes.Ldc_I4_2);
441                                 break;
442
443                         case 3:
444                                 ig.Emit (OpCodes.Ldc_I4_3);
445                                 break;
446
447                         case 4:
448                                 ig.Emit (OpCodes.Ldc_I4_4);
449                                 break;
450
451                         case 5:
452                                 ig.Emit (OpCodes.Ldc_I4_5);
453                                 break;
454
455                         case 6:
456                                 ig.Emit (OpCodes.Ldc_I4_6);
457                                 break;
458
459                         case 7:
460                                 ig.Emit (OpCodes.Ldc_I4_7);
461                                 break;
462
463                         case 8:
464                                 ig.Emit (OpCodes.Ldc_I4_8);
465                                 break;
466
467                         default:
468                                 if (i >= -128 && i <= 127) {
469                                         ig.Emit (OpCodes.Ldc_I4_S, (sbyte) i);
470                                 } else
471                                         ig.Emit (OpCodes.Ldc_I4, i);
472                                 break;
473                         }
474                 }
475
476                 public void EmitLong (long l)
477                 {
478                         if (l >= int.MinValue && l <= int.MaxValue) {
479                                 EmitInt (unchecked ((int) l));
480                                 ig.Emit (OpCodes.Conv_I8);
481                                 return;
482                         }
483
484                         if (l >= 0 && l <= uint.MaxValue) {
485                                 EmitInt (unchecked ((int) l));
486                                 ig.Emit (OpCodes.Conv_U8);
487                                 return;
488                         }
489
490                         ig.Emit (OpCodes.Ldc_I8, l);
491                 }
492
493                 //
494                 // Load the object from the pointer.  
495                 //
496                 public void EmitLoadFromPtr (TypeSpec t)
497                 {
498                         if (t == TypeManager.int32_type)
499                                 ig.Emit (OpCodes.Ldind_I4);
500                         else if (t == TypeManager.uint32_type)
501                                 ig.Emit (OpCodes.Ldind_U4);
502                         else if (t == TypeManager.short_type)
503                                 ig.Emit (OpCodes.Ldind_I2);
504                         else if (t == TypeManager.ushort_type)
505                                 ig.Emit (OpCodes.Ldind_U2);
506                         else if (t == TypeManager.char_type)
507                                 ig.Emit (OpCodes.Ldind_U2);
508                         else if (t == TypeManager.byte_type)
509                                 ig.Emit (OpCodes.Ldind_U1);
510                         else if (t == TypeManager.sbyte_type)
511                                 ig.Emit (OpCodes.Ldind_I1);
512                         else if (t == TypeManager.uint64_type)
513                                 ig.Emit (OpCodes.Ldind_I8);
514                         else if (t == TypeManager.int64_type)
515                                 ig.Emit (OpCodes.Ldind_I8);
516                         else if (t == TypeManager.float_type)
517                                 ig.Emit (OpCodes.Ldind_R4);
518                         else if (t == TypeManager.double_type)
519                                 ig.Emit (OpCodes.Ldind_R8);
520                         else if (t == TypeManager.bool_type)
521                                 ig.Emit (OpCodes.Ldind_I1);
522                         else if (t == TypeManager.intptr_type)
523                                 ig.Emit (OpCodes.Ldind_I);
524                         else if (t.IsEnum) {
525                                 if (t == TypeManager.enum_type)
526                                         ig.Emit (OpCodes.Ldind_Ref);
527                                 else
528                                         EmitLoadFromPtr (EnumSpec.GetUnderlyingType (t));
529                         } else if (TypeManager.IsStruct (t) || TypeManager.IsGenericParameter (t))
530                                 Emit (OpCodes.Ldobj, t);
531                         else if (t.IsPointer)
532                                 ig.Emit (OpCodes.Ldind_I);
533                         else
534                                 ig.Emit (OpCodes.Ldind_Ref);
535                 }
536
537                 //
538                 // The stack contains the pointer and the value of type `type'
539                 //
540                 public void EmitStoreFromPtr (TypeSpec type)
541                 {
542                         if (type.IsEnum)
543                                 type = EnumSpec.GetUnderlyingType (type);
544
545                         if (type == TypeManager.int32_type || type == TypeManager.uint32_type)
546                                 ig.Emit (OpCodes.Stind_I4);
547                         else if (type == TypeManager.int64_type || type == TypeManager.uint64_type)
548                                 ig.Emit (OpCodes.Stind_I8);
549                         else if (type == TypeManager.char_type || type == TypeManager.short_type ||
550                                  type == TypeManager.ushort_type)
551                                 ig.Emit (OpCodes.Stind_I2);
552                         else if (type == TypeManager.float_type)
553                                 ig.Emit (OpCodes.Stind_R4);
554                         else if (type == TypeManager.double_type)
555                                 ig.Emit (OpCodes.Stind_R8);
556                         else if (type == TypeManager.byte_type || type == TypeManager.sbyte_type ||
557                                  type == TypeManager.bool_type)
558                                 ig.Emit (OpCodes.Stind_I1);
559                         else if (type == TypeManager.intptr_type)
560                                 ig.Emit (OpCodes.Stind_I);
561                         else if (TypeManager.IsStruct (type) || TypeManager.IsGenericParameter (type))
562                                 ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
563                         else
564                                 ig.Emit (OpCodes.Stind_Ref);
565                 }
566
567                 /// <summary>
568                 ///   Returns a temporary storage for a variable of type t as 
569                 ///   a local variable in the current body.
570                 /// </summary>
571                 public LocalBuilder GetTemporaryLocal (TypeSpec t)
572                 {
573                         if (temporary_storage != null) {
574                                 object o;
575                                 if (temporary_storage.TryGetValue (t, out o)) {
576                                         if (o is Stack<LocalBuilder>) {
577                                                 var s = (Stack<LocalBuilder>) o;
578                                                 o = s.Count == 0 ? null : s.Pop ();
579                                         } else {
580                                                 temporary_storage.Remove (t);
581                                         }
582                                 }
583                                 if (o != null)
584                                         return (LocalBuilder) o;
585                         }
586                         return DeclareLocal (t, false);
587                 }
588
589                 public void FreeTemporaryLocal (LocalBuilder b, TypeSpec t)
590                 {
591                         if (temporary_storage == null) {
592                                 temporary_storage = new Dictionary<TypeSpec, object> (ReferenceEquality<TypeSpec>.Default);
593                                 temporary_storage.Add (t, b);
594                                 return;
595                         }
596                         object o;
597                         
598                         if (!temporary_storage.TryGetValue (t, out o)) {
599                                 temporary_storage.Add (t, b);
600                                 return;
601                         }
602                         var s = o as Stack<LocalBuilder>;
603                         if (s == null) {
604                                 s = new Stack<LocalBuilder> ();
605                                 s.Push ((LocalBuilder)o);
606                                 temporary_storage [t] = s;
607                         }
608                         s.Push (b);
609                 }
610
611                 /// <summary>
612                 ///   ReturnValue creates on demand the LocalBuilder for the
613                 ///   return value from the function.  By default this is not
614                 ///   used.  This is only required when returns are found inside
615                 ///   Try or Catch statements.
616                 ///
617                 ///   This method is typically invoked from the Emit phase, so
618                 ///   we allow the creation of a return label if it was not
619                 ///   requested during the resolution phase.   Could be cleaned
620                 ///   up, but it would replicate a lot of logic in the Emit phase
621                 ///   of the code that uses it.
622                 /// </summary>
623                 public LocalBuilder TemporaryReturn ()
624                 {
625                         if (return_value == null){
626                                 return_value = DeclareLocal (return_type, false);
627                                 if (!HasReturnLabel){
628                                         ReturnLabel = DefineLabel ();
629                                         HasReturnLabel = true;
630                                 }
631                         }
632
633                         return return_value;
634                 }
635         }
636 }