Initial import of the Generic MCS tree.
[mono.git] / mcs / gmcs / codegen.cs
1 //
2 // codegen.cs: The code generator
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9
10 using System;
11 using System.IO;
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15
16 namespace Mono.CSharp {
17
18         /// <summary>
19         ///    Code generator class.
20         /// </summary>
21         public class CodeGen {
22                 static AppDomain current_domain;
23                 public static AssemblyBuilder AssemblyBuilder;
24                 public static ModuleBuilder   ModuleBuilder;
25
26                 static public SymbolWriter SymbolWriter;
27
28                 public static string Basename (string name)
29                 {
30                         int pos = name.LastIndexOf ("/");
31
32                         if (pos != -1)
33                                 return name.Substring (pos + 1);
34
35                         pos = name.LastIndexOf ("\\");
36                         if (pos != -1)
37                                 return name.Substring (pos + 1);
38
39                         return name;
40                 }
41
42                 public static string Dirname (string name)
43                 {
44                         int pos = name.LastIndexOf ("/");
45
46                         if (pos != -1)
47                                 return name.Substring (0, pos);
48
49                         pos = name.LastIndexOf ("\\");
50                         if (pos != -1)
51                                 return name.Substring (0, pos);
52
53                         return ".";
54                 }
55
56                 static string TrimExt (string name)
57                 {
58                         int pos = name.LastIndexOf (".");
59
60                         return name.Substring (0, pos);
61                 }
62
63                 static public string FileName;
64
65                 //
66                 // Initializes the symbol writer
67                 //
68                 static void InitializeSymbolWriter ()
69                 {
70                         SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
71
72                         //
73                         // If we got an ISymbolWriter instance, initialize it.
74                         //
75                         if (SymbolWriter == null) {
76                                 Report.Warning (
77                                         -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
78                                 return;
79                         }
80                 }
81
82                 //
83                 // Initializes the code generator variables
84                 //
85                 static public void Init (string name, string output, bool want_debugging_support)
86                 {
87                         AssemblyName an;
88
89                         FileName = output;
90                         an = new AssemblyName ();
91                         an.Name = Path.GetFileNameWithoutExtension (name);
92                         
93                         current_domain = AppDomain.CurrentDomain;
94                         AssemblyBuilder = current_domain.DefineDynamicAssembly (
95                                 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
96
97                         //
98                         // Pass a path-less name to DefineDynamicModule.  Wonder how
99                         // this copes with output in different directories then.
100                         // FIXME: figure out how this copes with --output /tmp/blah
101                         //
102                         // If the third argument is true, the ModuleBuilder will dynamically
103                         // load the default symbol writer.
104                         //
105                         ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
106                                 Basename (name), Basename (output), want_debugging_support);
107
108                         if (want_debugging_support)
109                                 InitializeSymbolWriter ();
110                 }
111
112                 static public void Save (string name)
113                 {
114                         try {
115                                 AssemblyBuilder.Save (Basename (name));
116                         } catch (System.IO.IOException io){
117                                 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
118                         }
119                 }
120         }
121
122         /// <summary>
123         ///   An Emit Context is created for each body of code (from methods,
124         ///   properties bodies, indexer bodies or constructor bodies)
125         /// </summary>
126         public class EmitContext {
127                 public DeclSpace DeclSpace;
128                 public DeclSpace TypeContainer;
129                 public ILGenerator   ig;
130
131                 /// <summary>
132                 ///   This variable tracks the `checked' state of the compilation,
133                 ///   it controls whether we should generate code that does overflow
134                 ///   checking, or if we generate code that ignores overflows.
135                 ///
136                 ///   The default setting comes from the command line option to generate
137                 ///   checked or unchecked code plus any source code changes using the
138                 ///   checked/unchecked statements or expressions.   Contrast this with
139                 ///   the ConstantCheckState flag.
140                 /// </summary>
141                 
142                 public bool CheckState;
143
144                 /// <summary>
145                 ///   The constant check state is always set to `true' and cant be changed
146                 ///   from the command line.  The source code can change this setting with
147                 ///   the `checked' and `unchecked' statements and expressions. 
148                 /// </summary>
149                 public bool ConstantCheckState;
150
151                 /// <summary>
152                 ///   Whether we are emitting code inside a static or instance method
153                 /// </summary>
154                 public bool IsStatic;
155
156                 /// <summary>
157                 ///   Whether we are emitting a field initializer
158                 /// </summary>
159                 public bool IsFieldInitializer;
160
161                 /// <summary>
162                 ///   The value that is allowed to be returned or NULL if there is no
163                 ///   return type.
164                 /// </summary>
165                 public Type ReturnType;
166
167                 /// <summary>
168                 ///   Points to the Type (extracted from the TypeContainer) that
169                 ///   declares this body of code
170                 /// </summary>
171                 public Type ContainerType;
172                 
173                 /// <summary>
174                 ///   Whether this is generating code for a constructor
175                 /// </summary>
176                 public bool IsConstructor;
177
178                 /// <summary>
179                 ///   Whether we're control flow analysis enabled
180                 /// </summary>
181                 public bool DoFlowAnalysis;
182                 
183                 /// <summary>
184                 ///   Keeps track of the Type to LocalBuilder temporary storage created
185                 ///   to store structures (used to compute the address of the structure
186                 ///   value on structure method invocations)
187                 /// </summary>
188                 public Hashtable temporary_storage;
189
190                 public Block CurrentBlock;
191
192                 public int CurrentFile;
193
194                 /// <summary>
195                 ///   The location where we store the return value.
196                 /// </summary>
197                 LocalBuilder return_value;
198
199                 /// <summary>
200                 ///   The location where return has to jump to return the
201                 ///   value
202                 /// </summary>
203                 public Label ReturnLabel;
204
205                 /// <summary>
206                 ///   If we already defined the ReturnLabel
207                 /// </summary>
208                 public bool HasReturnLabel;
209
210                 /// <summary>
211                 ///   Whether we are in a Finally block
212                 /// </summary>
213                 public bool InFinally;
214
215                 /// <summary>
216                 ///   Whether we are in a Try block
217                 /// </summary>
218                 public bool InTry;
219
220                 /// <summary>
221                 ///   Whether we are inside an iterator block.
222                 /// </summary>
223                 public bool InIterator;
224
225                 /// <summary>
226                 ///   Whether we need an explicit return statement at the end of the method.
227                 /// </summary>
228                 public bool NeedExplicitReturn;
229                 
230                 /// <summary>
231                 ///   Whether remapping of locals, parameters and fields is turned on.
232                 ///   Used by iterators and anonymous methods.
233                 /// </summary>
234                 public bool RemapToProxy;
235
236                 /// <summary>
237                 ///   Whether we are in a Catch block
238                 /// </summary>
239                 public bool InCatch;
240
241                 /// <summary>
242                 ///  Whether we are inside an unsafe block
243                 /// </summary>
244                 public bool InUnsafe;
245
246                 /// <summary>
247                 ///  Whether we are in a `fixed' initialization
248                 /// </summary>
249                 public bool InFixedInitializer;
250
251                 /// <summary>
252                 ///  Whether we are inside an anonymous method.
253                 /// </summary>
254                 public bool InAnonymousMethod;
255                 
256                 /// <summary>
257                 ///   Location for this EmitContext
258                 /// </summary>
259                 public Location loc;
260
261                 /// <summary>
262                 ///   Used to flag that it is ok to define types recursively, as the
263                 ///   expressions are being evaluated as part of the type lookup
264                 ///   during the type resolution process
265                 /// </summary>
266                 public bool ResolvingTypeTree;
267                 
268                 /// <summary>
269                 ///   Inside an enum definition, we do not resolve enumeration values
270                 ///   to their enumerations, but rather to the underlying type/value
271                 ///   This is so EnumVal + EnumValB can be evaluated.
272                 ///
273                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
274                 ///   we relax the rules
275                 /// </summary>
276                 public bool InEnumContext;
277
278                 /// <summary>
279                 ///   If our type container, or the method we are generating code
280                 ///   for is Generic
281                 /// </summary>
282                 public bool IsGeneric;
283                 
284                 protected Stack FlowStack;
285                 
286                 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
287                                     Type return_type, int code_flags, bool is_constructor)
288                 {
289                         this.ig = ig;
290
291                         TypeContainer = parent;
292                         DeclSpace = ds;
293                         CheckState = RootContext.Checked;
294                         ConstantCheckState = true;
295                         
296                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
297                         InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
298                         RemapToProxy = InIterator;
299                         ReturnType = return_type;
300                         IsConstructor = is_constructor;
301                         CurrentBlock = null;
302                         CurrentFile = 0;
303                         
304                         if (parent != null){
305                                 // Can only be null for the ResolveType contexts.
306                                 ContainerType = parent.TypeBuilder;
307                                 if (parent.UnsafeContext)
308                                         InUnsafe = true;
309                                 else
310                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
311                         }
312                         loc = l;
313
314                         FlowStack = new Stack ();
315                         
316                         if (ReturnType == TypeManager.void_type)
317                                 ReturnType = null;
318
319                         //
320                         // We should also set it if the method is generic
321                         //
322                         IsGeneric = ds.IsGeneric || ((Modifiers.METHOD_GENERIC) != 0);
323                 }
324
325                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
326                                     Type return_type, int code_flags, bool is_constructor)
327                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
328                 {
329                 }
330
331                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
332                                     Type return_type, int code_flags)
333                         : this (tc, tc, l, ig, return_type, code_flags, false)
334                 {
335                 }
336
337                 public FlowBranching CurrentBranching {
338                         get {
339                                 return (FlowBranching) FlowStack.Peek ();
340                         }
341                 }
342
343                 // <summary>
344                 //   Starts a new code branching.  This inherits the state of all local
345                 //   variables and parameters from the current branching.
346                 // </summary>
347                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
348                 {
349                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
350
351                         FlowStack.Push (cfb);
352
353                         return cfb;
354                 }
355
356                 // <summary>
357                 //   Starts a new code branching for block `block'.
358                 // </summary>
359                 public FlowBranching StartFlowBranching (Block block)
360                 {
361                         FlowBranching cfb;
362                         FlowBranchingType type;
363
364                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
365                                 type = FlowBranchingType.SWITCH_SECTION;
366                         else
367                                 type = FlowBranchingType.BLOCK;
368
369                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
370
371                         FlowStack.Push (cfb);
372
373                         return cfb;
374                 }
375
376                 // <summary>
377                 //   Ends a code branching.  Merges the state of locals and parameters
378                 //   from all the children of the ending branching.
379                 // </summary>
380                 public FlowReturns EndFlowBranching ()
381                 {
382                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
383
384                         return CurrentBranching.MergeChild (cfb);
385                 }
386
387                 // <summary>
388                 //   Kills the current code branching.  This throws away any changed state
389                 //   information and should only be used in case of an error.
390                 // </summary>
391                 public void KillFlowBranching ()
392                 {
393                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
394                 }
395
396                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
397                 {
398                         bool has_ret = false;
399
400                         if (!Location.IsNull (loc))
401                                 CurrentFile = loc.File;
402
403                         if (block != null){
404                             try {
405                                 int errors = Report.Errors;
406
407                                 block.EmitMeta (this, ip, block);
408
409                                 if (Report.Errors == errors){
410                                         bool old_do_flow_analysis = DoFlowAnalysis;
411                                         DoFlowAnalysis = true;
412
413                                         FlowBranching cfb = new FlowBranching (block, loc);
414                                         FlowStack.Push (cfb);
415
416                                         if (!block.Resolve (this)) {
417                                                 FlowStack.Pop ();
418                                                 DoFlowAnalysis = old_do_flow_analysis;
419                                                 return;
420                                         }
421
422                                         cfb = (FlowBranching) FlowStack.Pop ();
423                                         FlowReturns returns = cfb.MergeTopBlock ();
424
425                                         DoFlowAnalysis = old_do_flow_analysis;
426
427                                         has_ret = block.Emit (this);
428
429                                         if ((returns == FlowReturns.ALWAYS) ||
430                                             (returns == FlowReturns.EXCEPTION) ||
431                                             (returns == FlowReturns.UNREACHABLE))
432                                                 has_ret = true;
433
434                                         if (Report.Errors == errors){
435                                                 if (RootContext.WarningLevel >= 3)
436                                                         block.UsageWarning ();
437                                         }
438                                 }
439                             } catch {
440                                         Console.WriteLine ("Exception caught by the compiler while compiling:");
441                                         Console.WriteLine ("   Block that caused the problem begin at: " + loc);
442                                         
443                                         if (CurrentBlock != null){
444                                                 Console.WriteLine ("                     Block being compiled: [{0},{1}]",
445                                                                    CurrentBlock.StartLocation, CurrentBlock.EndLocation);
446                                         }
447                                         throw;
448                             }
449                         }
450
451                         if (ReturnType != null && !has_ret){
452                                 //
453                                 // FIXME: we need full flow analysis to implement this
454                                 // correctly and emit an error instead of a warning.
455                                 //
456                                 //
457                                 if (!InIterator){
458                                         Report.Error (161, loc, "Not all code paths return a value");
459                                         return;
460                                 }
461                         }
462
463                         if (HasReturnLabel)
464                                 ig.MarkLabel (ReturnLabel);
465                         if (return_value != null){
466                                 ig.Emit (OpCodes.Ldloc, return_value);
467                                 ig.Emit (OpCodes.Ret);
468                         } else {
469                                 if (!InTry){
470                                         if (InIterator)
471                                                 has_ret = true;
472                                         
473                                         if (!has_ret || HasReturnLabel) {
474                                                 ig.Emit (OpCodes.Ret);
475                                                 NeedExplicitReturn = false;
476                                         }
477                                 }
478
479                                 // Unfortunately, System.Reflection.Emit automatically emits a leave
480                                 // to the end of a finally block.  This is a problem if no code is
481                                 // following the try/finally block since we may jump to a point after
482                                 // the end of the method. As a workaround, emit an explicit ret here.
483
484                                 if (NeedExplicitReturn) {
485                                         if (ReturnType != null)
486                                                 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
487                                         ig.Emit (OpCodes.Ret);
488                                 }
489                         }
490                 }
491
492                 /// <summary>
493                 ///   This is called immediately before emitting an IL opcode to tell the symbol
494                 ///   writer to which source line this opcode belongs.
495                 /// </summary>
496                 public void Mark (Location loc, bool check_file)
497                 {
498                         if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
499                                 return;
500
501                         if (check_file && (CurrentFile != loc.File))
502                                 return;
503
504                         ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
505                 }
506
507                 /// <summary>
508                 ///   Returns a temporary storage for a variable of type t as 
509                 ///   a local variable in the current body.
510                 /// </summary>
511                 public LocalBuilder GetTemporaryStorage (Type t)
512                 {
513                         LocalBuilder location;
514                         
515                         if (temporary_storage != null){
516                                 location = (LocalBuilder) temporary_storage [t];
517                                 if (location != null)
518                                         return location;
519                         }
520                         
521                         location = ig.DeclareLocal (t);
522                         
523                         return location;
524                 }
525
526                 public void FreeTemporaryStorage (LocalBuilder b)
527                 {
528                         // Empty for now.
529                 }
530
531                 /// <summary>
532                 ///   Current loop begin and end labels.
533                 /// </summary>
534                 public Label LoopBegin, LoopEnd;
535
536                 /// <summary>
537                 ///   Whether we are inside a loop and break/continue are possible.
538                 /// </summary>
539                 public bool  InLoop;
540
541                 /// <summary>
542                 ///   This is incremented each time we enter a try/catch block and
543                 ///   decremented if we leave it.
544                 /// </summary>
545                 public int   TryCatchLevel;
546
547                 /// <summary>
548                 ///   The TryCatchLevel at the begin of the current loop.
549                 /// </summary>
550                 public int   LoopBeginTryCatchLevel;
551
552                 /// <summary>
553                 ///   Default target in a switch statement.   Only valid if
554                 ///   InSwitch is true
555                 /// </summary>
556                 public Label DefaultTarget;
557
558                 /// <summary>
559                 ///   If this is non-null, points to the current switch statement
560                 /// </summary>
561                 public Switch Switch;
562
563                 /// <summary>
564                 ///   ReturnValue creates on demand the LocalBuilder for the
565                 ///   return value from the function.  By default this is not
566                 ///   used.  This is only required when returns are found inside
567                 ///   Try or Catch statements.
568                 /// </summary>
569                 public LocalBuilder TemporaryReturn ()
570                 {
571                         if (return_value == null){
572                                 return_value = ig.DeclareLocal (ReturnType);
573                                 ReturnLabel = ig.DefineLabel ();
574                                 HasReturnLabel = true;
575                         }
576
577                         return return_value;
578                 }
579
580                 //
581                 // Creates a field `name' with the type `t' on the proxy class
582                 //
583                 public FieldBuilder MapVariable (string name, Type t)
584                 {
585                         if (InIterator){
586                                 return IteratorHandler.Current.MapVariable (name, t);
587                         }
588
589                         throw new Exception ("MapVariable for an unknown state");
590                 }
591
592                 //
593                 // Emits the proper object to address fields on a remapped
594                 // variable/parameter to field in anonymous-method/iterator proxy classes.
595                 //
596                 public void EmitThis ()
597                 {
598                         ig.Emit (OpCodes.Ldarg_0);
599
600                         if (!IsStatic){
601                                 if (InIterator)
602                                         ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
603                                 else
604                                         throw new Exception ("EmitThis for an unknown state");
605                         }
606                 }
607
608                 public void EmitArgument (int idx)
609                 {
610                         if (InIterator)
611                                 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.parameter_fields [idx]);
612                         else
613                                 throw new Exception ("EmitStoreArgument for an unknown state");
614                 }
615                 
616                 public void EmitStoreArgument (int idx)
617                 {
618                         if (InIterator)
619                                 ig.Emit (OpCodes.Stfld, IteratorHandler.Current.parameter_fields [idx]);
620                         else
621                                 throw new Exception ("EmitStoreArgument for an unknown state");
622                 }
623                 
624                 public Expression GetThis (Location loc)
625                 {
626                         This my_this;
627                         if (CurrentBlock != null)
628                                 my_this = new This (CurrentBlock, loc);
629                         else
630                                 my_this = new This (loc);
631
632                         if (!my_this.ResolveBase (this))
633                                 my_this = null;
634
635                         return my_this;
636                 }
637         }
638 }