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