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