Fixed to conform to Mono standards for incomplete functionality
[mono.git] / mcs / mcs / codegen.cs
1 //
2 // codegen.cs: The code generator
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9
10 using System;
11 using System.Collections;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Diagnostics.SymbolStore;
15
16 namespace Mono.CSharp {
17
18         /// <summary>
19         ///    Code generator class.
20         /// </summary>
21         public class CodeGen {
22                 static AppDomain current_domain;
23                 static AssemblyBuilder assembly_builder;
24                 static ModuleBuilder   module_builder;
25
26                 static public ISymbolWriter SymbolWriter;
27
28                 public static string Basename (string name)
29                 {
30                         int pos = name.LastIndexOf ("/");
31
32                         if (pos != -1)
33                                 return name.Substring (pos + 1);
34
35                         pos = name.LastIndexOf ("\\");
36                         if (pos != -1)
37                                 return name.Substring (pos + 1);
38
39                         return name;
40                 }
41
42                 public static string Dirname (string name)
43                 {
44                         int pos = name.LastIndexOf ("/");
45
46                         if (pos != -1)
47                                 return name.Substring (0, pos);
48
49                         pos = name.LastIndexOf ("\\");
50                         if (pos != -1)
51                                 return name.Substring (0, pos);
52
53                         return ".";
54                 }
55
56                 static string TrimExt (string name)
57                 {
58                         int pos = name.LastIndexOf (".");
59
60                         return name.Substring (0, pos);
61                 }
62
63                 static public string FileName;
64
65                 //
66                 // This routine initializes the Mono runtime SymbolWriter.
67                 //
68                 static void InitMonoSymbolWriter (string basename)
69                 {
70                         string symbol_output = basename + "-debug.s";
71
72                         //
73                         // Mono's default symbol writer ignores the first and third argument
74                         // of this method.
75                         //
76                         SymbolWriter.Initialize (new IntPtr (0), symbol_output, true);
77                 }
78
79                 //
80                 // Initializes the symbol writer
81                 //
82                 static void InitializeSymbolWriter (string basename)
83                 {
84                         SymbolWriter = module_builder.GetSymWriter ();
85
86                         //
87                         // If we got an ISymbolWriter instance, initialize it.
88                         //
89                         if (SymbolWriter == null)
90                                 return;
91                         
92                         //
93                         // Due to lacking documentation about the first argument of the
94                         // Initialize method, we cannot use Microsoft's default symbol
95                         // writer yet.
96                         //
97                         // If we're using the mono symbol writer, the SymbolWriter object
98                         // is of type MonoSymbolWriter - but that's defined in a dynamically
99                         // loaded DLL - that's why we're doing a comparision based on the type
100                         // name here instead of using `SymbolWriter is MonoSymbolWriter'.
101                         //
102                         Type sym_type = ((object) SymbolWriter).GetType ();
103                         
104                         switch (sym_type.Name){
105                         case "MonoSymbolWriter":
106                                 InitMonoSymbolWriter (basename);
107                                 break;
108
109                         default:
110                                 Report.Error (
111                                         -18, "Cannot generate debugging information on this platform");
112                                 break;
113                         }
114                 }
115
116                 //
117                 // Initializes the code generator variables
118                 //
119                 static public void Init (string name, string output, bool want_debugging_support)
120                 {
121                         AssemblyName an;
122
123                         FileName = output;
124                         an = new AssemblyName ();
125                         an.Name = TrimExt (Basename (name));
126                         current_domain = AppDomain.CurrentDomain;
127                         assembly_builder = current_domain.DefineDynamicAssembly (
128                                 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
129
130                         //
131                         // Pass a path-less name to DefineDynamicModule.  Wonder how
132                         // this copes with output in different directories then.
133                         // FIXME: figure out how this copes with --output /tmp/blah
134                         //
135                         // If the third argument is true, the ModuleBuilder will dynamically
136                         // load the default symbol writer.
137                         //
138                         module_builder = assembly_builder.DefineDynamicModule (
139                                 Basename (name), Basename (output), want_debugging_support);
140
141                         if (want_debugging_support)
142                                 InitializeSymbolWriter (an.Name);
143                 }
144
145                 static public AssemblyBuilder AssemblyBuilder {
146                         get {
147                                 return assembly_builder;
148                         }
149                 }
150                 
151                 static public ModuleBuilder ModuleBuilder {
152                         get {
153                                 return module_builder;
154                         }
155                 }
156
157                 static public void Save (string name)
158                 {
159                         try {
160                                 assembly_builder.Save (Basename (name));
161                         } catch (System.IO.IOException io){
162                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
163                         }
164                 }
165
166                 static public void SaveSymbols ()
167                 {
168                         if (SymbolWriter != null) {
169                                 // If we have a symbol writer, call its Close() method to write
170                                 // the symbol file to disk.
171                                 //
172                                 // When using Mono's default symbol writer, the Close() method must
173                                 // be called after the assembly has already been written to disk since
174                                 // it opens the assembly and reads its metadata.
175                                 SymbolWriter.Close ();
176                         }
177                 }
178         }
179
180         /// <summary>
181         ///   An Emit Context is created for each body of code (from methods,
182         ///   properties bodies, indexer bodies or constructor bodies)
183         /// </summary>
184         public class EmitContext {
185                 public DeclSpace DeclSpace;
186                 public TypeContainer TypeContainer;
187                 public ILGenerator   ig;
188
189                 /// <summary>
190                 ///   This variable tracks the `checked' state of the compilation,
191                 ///   it controls whether we should generate code that does overflow
192                 ///   checking, or if we generate code that ignores overflows.
193                 ///
194                 ///   The default setting comes from the command line option to generate
195                 ///   checked or unchecked code plus any source code changes using the
196                 ///   checked/unchecked statements or expressions.   Contrast this with
197                 ///   the ConstantCheckState flag.
198                 /// </summary>
199                 
200                 public bool CheckState;
201
202                 /// <summary>
203                 ///   The constant check state is always set to `true' and cant be changed
204                 ///   from the command line.  The source code can change this setting with
205                 ///   the `checked' and `unchecked' statements and expressions. 
206                 /// </summary>
207                 public bool ConstantCheckState;
208
209                 /// <summary>
210                 ///   Whether we are emitting code inside a static or instance method
211                 /// </summary>
212                 public bool IsStatic;
213
214                 /// <summary>
215                 ///   The value that is allowed to be returned or NULL if there is no
216                 ///   return type.
217                 /// </summary>
218                 public Type ReturnType;
219
220                 /// <summary>
221                 ///   Points to the Type (extracted from the TypeContainer) that
222                 ///   declares this body of code
223                 /// </summary>
224                 public Type ContainerType;
225                 
226                 /// <summary>
227                 ///   Whether this is generating code for a constructor
228                 /// </summary>
229                 public bool IsConstructor;
230                 
231                 /// <summary>
232                 ///   Keeps track of the Type to LocalBuilder temporary storage created
233                 ///   to store structures (used to compute the address of the structure
234                 ///   value on structure method invocations)
235                 /// </summary>
236                 public Hashtable temporary_storage;
237
238                 public Block CurrentBlock;
239
240                 /// <summary>
241                 ///   The location where we store the return value.
242                 /// </summary>
243                 LocalBuilder return_value;
244
245                 /// <summary>
246                 ///   The location where return has to jump to return the
247                 ///   value
248                 /// </summary>
249                 public Label ReturnLabel;
250
251                 /// <summary>
252                 ///   Whether we are in a Finally block
253                 /// </summary>
254                 public bool InFinally;
255
256                 /// <summary>
257                 ///   Whether we are in a Try block
258                 /// </summary>
259                 public bool InTry;
260
261                 /// <summary>
262                 ///   Whether we are in a Catch block
263                 /// </summary>
264                 public bool InCatch;
265
266                 /// <summary>
267                 ///  Whether we are inside an unsafe block
268                 /// </summary>
269                 public bool InUnsafe;
270                 
271                 /// <summary>
272                 ///   Location for this EmitContext
273                 /// </summary>
274                 public Location loc;
275
276                 /// <summary>
277                 ///   Used to "flag" the resolution process to only lookup types,
278                 ///   and nothing else.  This is an out-of-band communication
279                 ///   path to SimpleName from the cast operation.
280                 /// </summary>
281                 public bool OnlyLookupTypes;
282
283                 /// <summary>
284                 ///   Inside an enum definition, we do not resolve enumeration values
285                 ///   to their enumerations, but rather to the underlying type/value
286                 ///   This is so EnumVal + EnumValB can be evaluated.
287                 ///
288                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
289                 ///   we relax the rules
290                 /// </summary>
291                 public bool InEnumContext;
292                 
293                 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
294                                     Type return_type, int code_flags, bool is_constructor)
295                 {
296                         this.ig = ig;
297
298                         TypeContainer = parent;
299                         DeclSpace = ds;
300                         CheckState = RootContext.Checked;
301                         ConstantCheckState = true;
302                         
303                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
304                         ReturnType = return_type;
305                         IsConstructor = is_constructor;
306                         CurrentBlock = null;
307                         ContainerType = parent.TypeBuilder;
308                         InUnsafe = ((parent.ModFlags | code_flags) & Modifiers.UNSAFE) != 0;
309                         OnlyLookupTypes = false;
310                         loc = l;
311                         
312                         if (ReturnType == TypeManager.void_type)
313                                 ReturnType = null;
314                 }
315
316                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
317                                     Type return_type, int code_flags, bool is_constructor)
318                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
319                 {
320                 }
321
322                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
323                                     Type return_type, int code_flags)
324                         : this (tc, tc, l, ig, return_type, code_flags, false)
325                 {
326                 }
327
328                 public void EmitTopBlock (Block block, Location loc)
329                 {
330                         bool has_ret = false;
331
332 //                      Console.WriteLine ("Emitting: " + loc);
333
334                         if (CodeGen.SymbolWriter != null)
335                                 Mark (loc);
336
337                         if (block != null){
338                                 int errors = Report.Errors;
339
340                                 block.EmitMeta (this, block);
341
342                                 if (Report.Errors == errors){
343                                         if (!block.Resolve (this))
344                                                 return;
345                                         
346                                         has_ret = block.Emit (this);
347
348                                         if (Report.Errors == errors){
349                                                 if (RootContext.WarningLevel >= 3)
350                                                         block.UsageWarning ();
351                                         }
352                                 }
353                         }
354
355                         if (ReturnType != null && !has_ret){
356                                 //
357                                 // FIXME: we need full flow analysis to implement this
358                                 // correctly and emit an error instead of a warning.
359                                 //
360                                 //
361                                 Report.Error (161, loc, "Not all code paths return a value");
362                                 return;
363                         }
364
365                         if (return_value != null){
366                                 ig.MarkLabel (ReturnLabel);
367                                 ig.Emit (OpCodes.Ldloc, return_value);
368                                 ig.Emit (OpCodes.Ret);
369                         } else {
370                                 if (!has_ret){
371                                         if (!InTry)
372                                                 ig.Emit (OpCodes.Ret);
373                                 }
374                         }
375                 }
376
377                 /// <summary>
378                 ///   This is called immediately before emitting an IL opcode to tell the symbol
379                 ///   writer to which source line this opcode belongs.
380                 /// </summary>
381                 public void Mark (Location loc)
382                 {
383                         if (!Location.IsNull (loc)) {
384                                 ISymbolDocumentWriter doc = loc.SymbolDocument;
385
386                                 if (doc != null)
387                                         ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
388                         }               }
389
390
391                 /// <summary>
392                 ///   Returns a temporary storage for a variable of type t as 
393                 ///   a local variable in the current body.
394                 /// </summary>
395                 public LocalBuilder GetTemporaryStorage (Type t)
396                 {
397                         LocalBuilder location;
398                         
399                         if (temporary_storage != null){
400                                 location = (LocalBuilder) temporary_storage [t];
401                                 if (location != null)
402                                         return location;
403                         }
404                         
405                         location = ig.DeclareLocal (t);
406                         
407                         return location;
408                 }
409
410                 public void FreeTemporaryStorage (LocalBuilder b)
411                 {
412                         // Empty for now.
413                 }
414
415                 /// <summary>
416                 ///   Current loop begin and end labels.
417                 /// </summary>
418                 public Label LoopBegin, LoopEnd;
419
420                 /// <summary>
421                 ///   Whether we are inside a loop and break/continue are possible.
422                 /// </summary>
423                 public bool  InLoop;
424
425                 /// <summary>
426                 ///   Default target in a switch statement.   Only valid if
427                 ///   InSwitch is true
428                 /// </summary>
429                 public Label DefaultTarget;
430
431                 /// <summary>
432                 ///   If this is non-null, points to the current switch statement
433                 /// </summary>
434                 public Switch Switch;
435
436                 /// <summary>
437                 ///   ReturnValue creates on demand the LocalBuilder for the
438                 ///   return value from the function.  By default this is not
439                 ///   used.  This is only required when returns are found inside
440                 ///   Try or Catch statements.
441                 /// </summary>
442                 public LocalBuilder TemporaryReturn ()
443                 {
444                         if (return_value == null){
445                                 return_value = ig.DeclareLocal (ReturnType);
446                                 ReturnLabel = ig.DefineLabel ();
447                         }
448
449                         return return_value;
450                 }
451
452                 /// <summary>
453                 ///   A dynamic This that is shared by all variables in a emitcontext.
454                 ///   Created on demand.
455                 /// </summary>
456                 public Expression my_this;
457                 public Expression This {
458                         get {
459                                 if (my_this == null)
460                                         my_this = new This (loc).Resolve (this);
461
462                                 return my_this;
463                         }
464                 }
465         }
466 }