2002-08-08 Martin Baulig <martin@gnome.org>
[mono.git] / mcs / mcs / 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.Collections;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Diagnostics.SymbolStore;
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 ISymbolWriter 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                 // This routine initializes the Mono runtime SymbolWriter.
67                 //
68                 static void InitMonoSymbolWriter (string basename, string[] debug_args)
69                 {
70                         string symbol_output = basename + "-debug.s";
71
72                         Type itype = SymbolWriter.GetType ();
73                         if (itype == null)
74                                 return;
75
76                         Type[] arg_types = new Type [2];
77                         arg_types [0] = typeof (string);
78                         arg_types [1] = typeof (string[]);
79
80                         MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
81                         if (initialize == null)
82                                 return;
83
84                         object[] args = new object [2];
85                         args [0] = symbol_output;
86                         args [1] = debug_args;
87
88                         initialize.Invoke (SymbolWriter, args);
89                 }
90
91                 //
92                 // Initializes the symbol writer
93                 //
94                 static void InitializeSymbolWriter (string basename, string[] args)
95                 {
96                         SymbolWriter = ModuleBuilder.GetSymWriter ();
97
98                         //
99                         // If we got an ISymbolWriter instance, initialize it.
100                         //
101                         if (SymbolWriter == null)
102                                 return;
103                         
104                         //
105                         // Due to lacking documentation about the first argument of the
106                         // Initialize method, we cannot use Microsoft's default symbol
107                         // writer yet.
108                         //
109                         // If we're using the mono symbol writer, the SymbolWriter object
110                         // is of type MonoSymbolWriter - but that's defined in a dynamically
111                         // loaded DLL - that's why we're doing a comparision based on the type
112                         // name here instead of using `SymbolWriter is MonoSymbolWriter'.
113                         //
114                         Type sym_type = ((object) SymbolWriter).GetType ();
115                         
116                         switch (sym_type.Name){
117                         case "MonoSymbolWriter":
118                                 InitMonoSymbolWriter (basename, args);
119                                 break;
120
121                         default:
122                                 Report.Error (
123                                         -18, "Cannot generate debugging information on this platform");
124                                 break;
125                         }
126                 }
127
128                 //
129                 // Initializes the code generator variables
130                 //
131                 static public void Init (string name, string output, bool want_debugging_support,
132                                          string[] debug_args)
133                 {
134                         AssemblyName an;
135
136                         FileName = output;
137                         an = new AssemblyName ();
138                         an.Name = TrimExt (Basename (name));
139                         current_domain = AppDomain.CurrentDomain;
140                         AssemblyBuilder = current_domain.DefineDynamicAssembly (
141                                 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
142
143                         //
144                         // Pass a path-less name to DefineDynamicModule.  Wonder how
145                         // this copes with output in different directories then.
146                         // FIXME: figure out how this copes with --output /tmp/blah
147                         //
148                         // If the third argument is true, the ModuleBuilder will dynamically
149                         // load the default symbol writer.
150                         //
151                         ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
152                                 Basename (name), Basename (output), want_debugging_support);
153
154                         if (want_debugging_support)
155                                 InitializeSymbolWriter (an.Name, debug_args);
156                 }
157
158                 static public void Save (string name)
159                 {
160                         try {
161                                 AssemblyBuilder.Save (Basename (name));
162                         } catch (System.IO.IOException io){
163                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
164                         }
165                 }
166
167                 static public void SaveSymbols ()
168                 {
169                         if (SymbolWriter != null) {
170                                 // If we have a symbol writer, call its Close() method to write
171                                 // the symbol file to disk.
172                                 //
173                                 // When using Mono's default symbol writer, the Close() method must
174                                 // be called after the assembly has already been written to disk since
175                                 // it opens the assembly and reads its metadata.
176                                 SymbolWriter.Close ();
177                         }
178                 }
179         }
180
181         /// <summary>
182         ///   An Emit Context is created for each body of code (from methods,
183         ///   properties bodies, indexer bodies or constructor bodies)
184         /// </summary>
185         public class EmitContext {
186                 public DeclSpace DeclSpace;
187                 public TypeContainer TypeContainer;
188                 public ILGenerator   ig;
189
190                 /// <summary>
191                 ///   This variable tracks the `checked' state of the compilation,
192                 ///   it controls whether we should generate code that does overflow
193                 ///   checking, or if we generate code that ignores overflows.
194                 ///
195                 ///   The default setting comes from the command line option to generate
196                 ///   checked or unchecked code plus any source code changes using the
197                 ///   checked/unchecked statements or expressions.   Contrast this with
198                 ///   the ConstantCheckState flag.
199                 /// </summary>
200                 
201                 public bool CheckState;
202
203                 /// <summary>
204                 ///   The constant check state is always set to `true' and cant be changed
205                 ///   from the command line.  The source code can change this setting with
206                 ///   the `checked' and `unchecked' statements and expressions. 
207                 /// </summary>
208                 public bool ConstantCheckState;
209
210                 /// <summary>
211                 ///   Whether we are emitting code inside a static or instance method
212                 /// </summary>
213                 public bool IsStatic;
214
215                 /// <summary>
216                 ///   Whether we are emitting a field initializer
217                 /// </summary>
218                 public bool IsFieldInitializer;
219
220                 /// <summary>
221                 ///   The value that is allowed to be returned or NULL if there is no
222                 ///   return type.
223                 /// </summary>
224                 public Type ReturnType;
225
226                 /// <summary>
227                 ///   Points to the Type (extracted from the TypeContainer) that
228                 ///   declares this body of code
229                 /// </summary>
230                 public Type ContainerType;
231                 
232                 /// <summary>
233                 ///   Whether this is generating code for a constructor
234                 /// </summary>
235                 public bool IsConstructor;
236
237                 /// <summary>
238                 ///   Whether we're control flow analysis enabled
239                 /// </summary>
240                 public bool DoFlowAnalysis;
241                 
242                 /// <summary>
243                 ///   Keeps track of the Type to LocalBuilder temporary storage created
244                 ///   to store structures (used to compute the address of the structure
245                 ///   value on structure method invocations)
246                 /// </summary>
247                 public Hashtable temporary_storage;
248
249                 public Block CurrentBlock;
250
251                 /// <summary>
252                 ///   The location where we store the return value.
253                 /// </summary>
254                 LocalBuilder return_value;
255
256                 /// <summary>
257                 ///   The location where return has to jump to return the
258                 ///   value
259                 /// </summary>
260                 public Label ReturnLabel;
261
262                 /// <summary>
263                 ///   If we already defined the ReturnLabel
264                 /// </summary>
265                 public bool HasReturnLabel;
266
267                 /// <summary>
268                 ///   Whether we are in a Finally block
269                 /// </summary>
270                 public bool InFinally;
271
272                 /// <summary>
273                 ///   Whether we are in a Try block
274                 /// </summary>
275                 public bool InTry;
276
277                 /// <summary>
278                 ///   Whether we are in a Catch block
279                 /// </summary>
280                 public bool InCatch;
281
282                 /// <summary>
283                 ///  Whether we are inside an unsafe block
284                 /// </summary>
285                 public bool InUnsafe;
286
287                 /// <summary>
288                 ///   Whether we break from a loop or not
289                 /// </summary>
290                 public bool Breaks;
291                 
292                 /// <summary>
293                 ///   Location for this EmitContext
294                 /// </summary>
295                 public Location loc;
296
297                 /// <summary>
298                 ///   Used to "flag" the resolution process to only lookup types,
299                 ///   and nothing else.  This is an out-of-band communication
300                 ///   path to SimpleName from the cast operation.
301                 /// </summary>
302                 public bool OnlyLookupTypes;
303
304                 /// <summary>
305                 ///   Used to flag that it is ok to define types recursively, as the
306                 ///   expressions are being evaluated as part of the type lookup
307                 ///   during the type resolution process
308                 /// </summary>
309                 public bool ResolvingTypeTree;
310                 
311                 /// <summary>
312                 ///   Inside an enum definition, we do not resolve enumeration values
313                 ///   to their enumerations, but rather to the underlying type/value
314                 ///   This is so EnumVal + EnumValB can be evaluated.
315                 ///
316                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
317                 ///   we relax the rules
318                 /// </summary>
319                 public bool InEnumContext;
320
321                 protected Stack FlowStack;
322                 
323                 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
324                                     Type return_type, int code_flags, bool is_constructor)
325                 {
326                         this.ig = ig;
327
328                         TypeContainer = parent;
329                         DeclSpace = ds;
330                         CheckState = RootContext.Checked;
331                         ConstantCheckState = true;
332                         
333                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
334                         ReturnType = return_type;
335                         IsConstructor = is_constructor;
336                         CurrentBlock = null;
337                         
338                         if (parent != null){
339                                 // Can only be null for the ResolveType contexts.
340                                 ContainerType = parent.TypeBuilder;
341                                 if (parent.UnsafeContext)
342                                         InUnsafe = true;
343                                 else
344                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
345                         }
346                         OnlyLookupTypes = false;
347                         loc = l;
348
349                         FlowStack = new Stack ();
350                         
351                         if (ReturnType == TypeManager.void_type)
352                                 ReturnType = null;
353                 }
354
355                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
356                                     Type return_type, int code_flags, bool is_constructor)
357                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
358                 {
359                 }
360
361                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
362                                     Type return_type, int code_flags)
363                         : this (tc, tc, l, ig, return_type, code_flags, false)
364                 {
365                 }
366
367                 public FlowBranching CurrentBranching {
368                         get {
369                                 return (FlowBranching) FlowStack.Peek ();
370                         }
371                 }
372
373                 // <summary>
374                 //   Starts a new code branching.  This inherits the state of all local
375                 //   variables and parameters from the current branching.
376                 // </summary>
377                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
378                 {
379                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
380
381                         FlowStack.Push (cfb);
382
383                         return cfb;
384                 }
385
386                 // <summary>
387                 //   Starts a new code branching for block `block'.
388                 // </summary>
389                 public FlowBranching StartFlowBranching (Block block)
390                 {
391                         FlowBranching cfb;
392                         FlowBranchingType type;
393
394                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
395                                 type = FlowBranchingType.SWITCH_SECTION;
396                         else
397                                 type = FlowBranchingType.BLOCK;
398
399                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
400
401                         FlowStack.Push (cfb);
402
403                         return cfb;
404                 }
405
406                 // <summary>
407                 //   Ends a code branching.  Merges the state of locals and parameters
408                 //   from all the children of the ending branching.
409                 // </summary>
410                 public FlowReturns EndFlowBranching ()
411                 {
412                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
413
414                         return CurrentBranching.MergeChild (cfb);
415                 }
416
417                 // <summary>
418                 //   Kills the current code branching.  This throws away any changed state
419                 //   information and should only be used in case of an error.
420                 // </summary>
421                 public void KillFlowBranching ()
422                 {
423                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
424                 }
425
426                 // <summary>
427                 //   Checks whether the local variable `vi' is already initialized
428                 //   at the current point of the method's control flow.
429                 //   If this method returns false, the caller must report an
430                 //   error 165.
431                 // </summary>
432                 public bool IsVariableAssigned (VariableInfo vi)
433                 {
434                         if (DoFlowAnalysis)
435                                 return CurrentBranching.IsVariableAssigned (vi);
436                         else
437                                 return true;
438                 }
439
440                 // <summary>
441                 //   Marks the local variable `vi' as being initialized at the current
442                 //   current point of the method's control flow.
443                 // </summary>
444                 public void SetVariableAssigned (VariableInfo vi)
445                 {
446                         if (DoFlowAnalysis)
447                                 CurrentBranching.SetVariableAssigned (vi);
448                 }
449
450                 // <summary>
451                 //   Checks whether the parameter `number' is already initialized
452                 //   at the current point of the method's control flow.
453                 //   If this method returns false, the caller must report an
454                 //   error 165.  This is only necessary for `out' parameters and the
455                 //   call will always succeed for non-`out' parameters.
456                 // </summary>
457                 public bool IsParameterAssigned (int number)
458                 {
459                         if (DoFlowAnalysis)
460                                 return CurrentBranching.IsParameterAssigned (number);
461                         else
462                                 return true;
463                 }
464
465                 // <summary>
466                 //   Marks the parameter `number' as being initialized at the current
467                 //   current point of the method's control flow.  This is only necessary
468                 //   for `out' parameters.
469                 // </summary>
470                 public void SetParameterAssigned (int number)
471                 {
472                         if (DoFlowAnalysis)
473                                 CurrentBranching.SetParameterAssigned (number);
474                 }
475
476                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
477                 {
478                         bool has_ret = false;
479
480 //                      Console.WriteLine ("Emitting: " + loc);
481
482                         if (CodeGen.SymbolWriter != null)
483                                 Mark (loc);
484
485                         if (block != null){
486                                 int errors = Report.Errors;
487
488                                 block.EmitMeta (this, block);
489
490                                 if (Report.Errors == errors){
491                                         bool old_do_flow_analysis = DoFlowAnalysis;
492                                         DoFlowAnalysis = true;
493
494                                         FlowBranching cfb = new FlowBranching (block, ip, loc);
495                                         FlowStack.Push (cfb);
496
497                                         if (!block.Resolve (this)) {
498                                                 FlowStack.Pop ();
499                                                 DoFlowAnalysis = old_do_flow_analysis;
500                                                 return;
501                                         }
502
503                                         cfb = (FlowBranching) FlowStack.Pop ();
504                                         cfb.MergeTopBlock ();
505
506                                         DoFlowAnalysis = old_do_flow_analysis;
507
508                                         has_ret = block.Emit (this);
509
510                                         if (Report.Errors == errors){
511                                                 if (RootContext.WarningLevel >= 3)
512                                                         block.UsageWarning ();
513                                         }
514                                 }
515                         }
516
517                         if (ReturnType != null && !has_ret){
518                                 //
519                                 // FIXME: we need full flow analysis to implement this
520                                 // correctly and emit an error instead of a warning.
521                                 //
522                                 //
523                                 Report.Error (161, loc, "Not all code paths return a value");
524                                 return;
525                         }
526
527                         if (HasReturnLabel)
528                                 ig.MarkLabel (ReturnLabel);
529                         if (return_value != null){
530                                 ig.Emit (OpCodes.Ldloc, return_value);
531                                 ig.Emit (OpCodes.Ret);
532                         } else {
533                                 if (!InTry){
534                                         if (!has_ret || HasReturnLabel)
535                                                 ig.Emit (OpCodes.Ret);
536                                 }
537                         }
538                 }
539
540                 /// <summary>
541                 ///   This is called immediately before emitting an IL opcode to tell the symbol
542                 ///   writer to which source line this opcode belongs.
543                 /// </summary>
544                 public void Mark (Location loc)
545                 {
546                         if (!Location.IsNull (loc)) {
547                                 ISymbolDocumentWriter doc = loc.SymbolDocument;
548
549                                 if (doc != null)
550                                         ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
551                         }               }
552
553
554                 /// <summary>
555                 ///   Returns a temporary storage for a variable of type t as 
556                 ///   a local variable in the current body.
557                 /// </summary>
558                 public LocalBuilder GetTemporaryStorage (Type t)
559                 {
560                         LocalBuilder location;
561                         
562                         if (temporary_storage != null){
563                                 location = (LocalBuilder) temporary_storage [t];
564                                 if (location != null)
565                                         return location;
566                         }
567                         
568                         location = ig.DeclareLocal (t);
569                         
570                         return location;
571                 }
572
573                 public void FreeTemporaryStorage (LocalBuilder b)
574                 {
575                         // Empty for now.
576                 }
577
578                 /// <summary>
579                 ///   Current loop begin and end labels.
580                 /// </summary>
581                 public Label LoopBegin, LoopEnd;
582
583                 /// <summary>
584                 ///   Whether we are inside a loop and break/continue are possible.
585                 /// </summary>
586                 public bool  InLoop;
587
588                 /// <summary>
589                 ///   This is incremented each time we enter a try/catch block and
590                 ///   decremented if we leave it.
591                 /// </summary>
592                 public int   TryCatchLevel;
593
594                 /// <summary>
595                 ///   The TryCatchLevel at the begin of the current loop.
596                 /// </summary>
597                 public int   LoopBeginTryCatchLevel;
598
599                 /// <summary>
600                 ///   Default target in a switch statement.   Only valid if
601                 ///   InSwitch is true
602                 /// </summary>
603                 public Label DefaultTarget;
604
605                 /// <summary>
606                 ///   If this is non-null, points to the current switch statement
607                 /// </summary>
608                 public Switch Switch;
609
610                 /// <summary>
611                 ///   ReturnValue creates on demand the LocalBuilder for the
612                 ///   return value from the function.  By default this is not
613                 ///   used.  This is only required when returns are found inside
614                 ///   Try or Catch statements.
615                 /// </summary>
616                 public LocalBuilder TemporaryReturn ()
617                 {
618                         if (return_value == null){
619                                 return_value = ig.DeclareLocal (ReturnType);
620                                 ReturnLabel = ig.DefineLabel ();
621                                 HasReturnLabel = true;
622                         }
623
624                         return return_value;
625                 }
626
627                 /// <summary>
628                 ///   A dynamic This that is shared by all variables in a emitcontext.
629                 ///   Created on demand.
630                 /// </summary>
631                 public Expression my_this;
632                 public Expression This {
633                         get {
634                                 if (my_this == null)
635                                         my_this = new This (loc).Resolve (this);
636
637                                 return my_this;
638                         }
639                 }
640         }
641 }