4ad6eadf712522db840ff3cc4774fb28229dbfd0
[mono.git] / mcs / mbas / 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                 ///   Location for this EmitContext
316                 /// </summary>
317                 public Location loc;
318
319                 /// <summary>
320                 ///   Used to flag that it is ok to define types recursively, as the
321                 ///   expressions are being evaluated as part of the type lookup
322                 ///   during the type resolution process
323                 /// </summary>
324                 public bool ResolvingTypeTree;
325                 
326                 /// <summary>
327                 ///   Inside an enum definition, we do not resolve enumeration values
328                 ///   to their enumerations, but rather to the underlying type/value
329                 ///   This is so EnumVal + EnumValB can be evaluated.
330                 ///
331                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
332                 ///   we relax the rules
333                 /// </summary>
334                 public bool InEnumContext;
335
336                 protected Stack FlowStack;
337                 
338                 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
339                                     Type return_type, int code_flags, bool is_constructor)
340                 {
341                         this.ig = ig;
342
343                         TypeContainer = parent;
344                         DeclSpace = ds;
345                         CheckState = RootContext.Checked;
346                         ConstantCheckState = true;
347                         
348                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
349                         ReturnType = return_type;
350                         IsConstructor = is_constructor;
351                         CurrentBlock = null;
352                         
353                         if (parent != null){
354                                 // Can only be null for the ResolveType contexts.
355                                 ContainerType = parent.TypeBuilder;
356                                 if (parent.UnsafeContext)
357                                         InUnsafe = true;
358                                 else
359                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
360                         }
361                         loc = l;
362
363                         FlowStack = new Stack ();
364                         
365                         if (ReturnType == TypeManager.void_type)
366                                 ReturnType = null;
367                 }
368
369                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
370                                     Type return_type, int code_flags, bool is_constructor)
371                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
372                 {
373                 }
374
375                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
376                                     Type return_type, int code_flags)
377                         : this (tc, tc, l, ig, return_type, code_flags, false)
378                 {
379                 }
380
381                 public FlowBranching CurrentBranching {
382                         get {
383                                 return (FlowBranching) FlowStack.Peek ();
384                         }
385                 }
386
387                 // <summary>
388                 //   Starts a new code branching.  This inherits the state of all local
389                 //   variables and parameters from the current branching.
390                 // </summary>
391                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
392                 {
393                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
394
395                         FlowStack.Push (cfb);
396
397                         return cfb;
398                 }
399
400                 // <summary>
401                 //   Starts a new code branching for block `block'.
402                 // </summary>
403                 public FlowBranching StartFlowBranching (Block block)
404                 {
405                         FlowBranching cfb;
406                         FlowBranchingType type;
407
408                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
409                                 type = FlowBranchingType.SWITCH_SECTION;
410                         else
411                                 type = FlowBranchingType.BLOCK;
412
413                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
414
415                         FlowStack.Push (cfb);
416
417                         return cfb;
418                 }
419
420                 // <summary>
421                 //   Ends a code branching.  Merges the state of locals and parameters
422                 //   from all the children of the ending branching.
423                 // </summary>
424                 public FlowReturns EndFlowBranching ()
425                 {
426                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
427
428                         return CurrentBranching.MergeChild (cfb);
429                 }
430
431                 // <summary>
432                 //   Kills the current code branching.  This throws away any changed state
433                 //   information and should only be used in case of an error.
434                 // </summary>
435                 public void KillFlowBranching ()
436                 {
437                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
438                 }
439
440                 // <summary>
441                 //   Checks whether the local variable `vi' is already initialized
442                 //   at the current point of the method's control flow.
443                 //   If this method returns false, the caller must report an
444                 //   error 165.
445                 // </summary>
446                 public bool IsVariableAssigned (VariableInfo vi)
447                 {
448                         if (DoFlowAnalysis)
449                                 return CurrentBranching.IsVariableAssigned (vi);
450                         else
451                                 return true;
452                 }
453
454                 // <summary>
455                 //   Marks the local variable `vi' as being initialized at the current
456                 //   current point of the method's control flow.
457                 // </summary>
458                 public void SetVariableAssigned (VariableInfo vi)
459                 {
460                         if (DoFlowAnalysis)
461                                 CurrentBranching.SetVariableAssigned (vi);
462                 }
463
464                 // <summary>
465                 //   Checks whether the parameter `number' is already initialized
466                 //   at the current point of the method's control flow.
467                 //   If this method returns false, the caller must report an
468                 //   error 165.  This is only necessary for `out' parameters and the
469                 //   call will always succeed for non-`out' parameters.
470                 // </summary>
471                 public bool IsParameterAssigned (int number)
472                 {
473                         if (DoFlowAnalysis)
474                                 return CurrentBranching.IsParameterAssigned (number);
475                         else
476                                 return true;
477                 }
478
479                 // <summary>
480                 //   Marks the parameter `number' as being initialized at the current
481                 //   current point of the method's control flow.  This is only necessary
482                 //   for `out' parameters.
483                 // </summary>
484                 public void SetParameterAssigned (int number)
485                 {
486                         if (DoFlowAnalysis)
487                                 CurrentBranching.SetParameterAssigned (number);
488                 }
489
490                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
491                 {
492                         bool has_ret = false;
493
494 //                      Console.WriteLine ("Emitting: " + loc);
495
496                         if (CodeGen.SymbolWriter != null)
497                                 Mark (loc);
498
499                         if (block != null){
500                                 int errors = Report.Errors;
501
502                                 block.EmitMeta (this, block);
503
504                                 if (Report.Errors == errors){
505                                         bool old_do_flow_analysis = DoFlowAnalysis;
506                                         DoFlowAnalysis = true;
507
508                                         FlowBranching cfb = new FlowBranching (block, ip, loc);
509                                         FlowStack.Push (cfb);
510
511                                         if (!block.Resolve (this)) {
512                                                 FlowStack.Pop ();
513                                                 DoFlowAnalysis = old_do_flow_analysis;
514                                                 return;
515                                         }
516
517                                         cfb = (FlowBranching) FlowStack.Pop ();
518                                         FlowReturns returns = cfb.MergeTopBlock ();
519
520                                         DoFlowAnalysis = old_do_flow_analysis;
521
522                                         has_ret = block.Emit (this);
523
524                                         if ((returns == FlowReturns.ALWAYS) ||
525                                             (returns == FlowReturns.EXCEPTION) ||
526                                             (returns == FlowReturns.UNREACHABLE))
527                                                 has_ret = true;
528
529                                         if (Report.Errors == errors){
530                                                 if (RootContext.WarningLevel >= 3)
531                                                         block.UsageWarning ();
532                                         }
533                                 }
534                         }
535
536                         if (ReturnType != null && !has_ret){
537                                 //
538                                 // FIXME: we need full flow analysis to implement this
539                                 // correctly and emit an error instead of a warning.
540                                 //
541                                 //
542                                 Report.Error (161, loc, "Not all code paths return a value");
543                                 return;
544                         }
545
546                         if (HasReturnLabel)
547                                 ig.MarkLabel (ReturnLabel);
548                         if (return_value != null){
549                                 ig.Emit (OpCodes.Ldloc, return_value);
550                                 ig.Emit (OpCodes.Ret);
551                         } else {
552                                 if (!InTry){
553                                         if (!has_ret || HasReturnLabel)
554                                                 ig.Emit (OpCodes.Ret);
555                                 }
556                         }
557                 }
558
559                 /// <summary>
560                 ///   This is called immediately before emitting an IL opcode to tell the symbol
561                 ///   writer to which source line this opcode belongs.
562                 /// </summary>
563                 public void Mark (Location loc)
564                 {
565                         if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
566                                 ISymbolDocumentWriter doc = loc.SymbolDocument;
567
568                                 if (doc != null)
569                                         ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
570                         }
571                 }
572
573                 /// <summary>
574                 ///   Returns a temporary storage for a variable of type t as 
575                 ///   a local variable in the current body.
576                 /// </summary>
577                 public LocalBuilder GetTemporaryStorage (Type t)
578                 {
579                         LocalBuilder location;
580                         
581                         if (temporary_storage != null){
582                                 location = (LocalBuilder) temporary_storage [t];
583                                 if (location != null)
584                                         return location;
585                         }
586                         
587                         location = ig.DeclareLocal (t);
588                         
589                         return location;
590                 }
591
592                 public void FreeTemporaryStorage (LocalBuilder b)
593                 {
594                         // Empty for now.
595                 }
596
597                 /// <summary>
598                 ///   Current loop begin and end labels.
599                 /// </summary>
600                 public Label LoopBegin, LoopEnd;
601
602                 /// <summary>
603                 ///   Whether we are inside a loop and break/continue are possible.
604                 /// </summary>
605                 public bool  InLoop;
606
607                 /// <summary>
608                 ///   This is incremented each time we enter a try/catch block and
609                 ///   decremented if we leave it.
610                 /// </summary>
611                 public int   TryCatchLevel;
612
613                 /// <summary>
614                 ///   The TryCatchLevel at the begin of the current loop.
615                 /// </summary>
616                 public int   LoopBeginTryCatchLevel;
617
618                 /// <summary>
619                 ///   Default target in a switch statement.   Only valid if
620                 ///   InSwitch is true
621                 /// </summary>
622                 public Label DefaultTarget;
623
624                 /// <summary>
625                 ///   If this is non-null, points to the current switch statement
626                 /// </summary>
627                 public Switch Switch;
628
629                 /// <summary>
630                 ///   ReturnValue creates on demand the LocalBuilder for the
631                 ///   return value from the function.  By default this is not
632                 ///   used.  This is only required when returns are found inside
633                 ///   Try or Catch statements.
634                 /// </summary>
635                 public LocalBuilder TemporaryReturn ()
636                 {
637                         if (return_value == null){
638                                 return_value = ig.DeclareLocal (ReturnType);
639                                 ReturnLabel = ig.DefineLabel ();
640                                 HasReturnLabel = true;
641                         }
642
643                         return return_value;
644                 }
645
646                 /// <summary>
647                 ///   A dynamic This that is shared by all variables in a emitcontext.
648                 ///   Created on demand.
649                 /// </summary>
650                 public Expression my_this;
651                 public Expression This {
652                         get {
653                                 if (my_this == null) {
654                                         if (CurrentBlock != null)
655                                                 my_this = new This (CurrentBlock, loc);
656                                         else
657                                                 my_this = new This (loc);
658
659                                         my_this = my_this.Resolve (this);
660                                 }
661
662                                 return my_this;
663                         }
664                 }
665         }
666 }