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