2002-03-06 Miguel de Icaza <miguel@ximian.com>
[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
15 namespace Mono.CSharp {
16
17         /// <summary>
18         ///    Code generator class.
19         /// </summary>
20         public class CodeGen {
21                 AppDomain current_domain;
22                 AssemblyBuilder assembly_builder;
23                 ModuleBuilder   module_builder;
24
25                 public static string Basename (string name)
26                 {
27                         int pos = name.LastIndexOf ("/");
28
29                         if (pos != -1)
30                                 return name.Substring (pos + 1);
31
32                         pos = name.LastIndexOf ("\\");
33                         if (pos != -1)
34                                 return name.Substring (pos + 1);
35
36                         return name;
37                 }
38
39                 string TrimExt (string name)
40                 {
41                         int pos = name.LastIndexOf (".");
42
43                         return name.Substring (0, pos);
44                 }
45
46                 public string FileName;
47                 
48                 public CodeGen (string name, string output)
49                 {
50                         AssemblyName an;
51
52                         FileName = output;
53                         an = new AssemblyName ();
54                         an.Name = TrimExt (Basename (name));
55                         current_domain = AppDomain.CurrentDomain;
56                         assembly_builder = current_domain.DefineDynamicAssembly (
57                                 an, AssemblyBuilderAccess.RunAndSave);
58
59                         //
60                         // Pass a path-less name to DefineDynamicModule.  Wonder how
61                         // this copes with output in different directories then.
62                         // FIXME: figure out how this copes with --output /tmp/blah
63                         //
64                         module_builder = assembly_builder.DefineDynamicModule (
65                                 Basename (name), Basename (output));
66
67                 }
68                 
69                 public AssemblyBuilder AssemblyBuilder {
70                         get {
71                                 return assembly_builder;
72                         }
73                 }
74                 
75                 public ModuleBuilder ModuleBuilder {
76                         get {
77                                 return module_builder;
78                         }
79                 }
80                 
81                 public void Save (string name)
82                 {
83                         try {
84                                 assembly_builder.Save (Basename (name));
85                         } catch (System.IO.IOException io){
86                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
87                         } 
88                 }
89         }
90
91         /// <summary>
92         ///   An Emit Context is created for each body of code (from methods,
93         ///   properties bodies, indexer bodies or constructor bodies)
94         /// </summary>
95         public class EmitContext {
96                 public DeclSpace DeclSpace;
97                 public TypeContainer TypeContainer;
98                 public ILGenerator   ig;
99
100                 /// <summary>
101                 ///   This variable tracks the `checked' state of the compilation,
102                 ///   it controls whether we should generate code that does overflow
103                 ///   checking, or if we generate code that ignores overflows.
104                 ///
105                 ///   The default setting comes from the command line option to generate
106                 ///   checked or unchecked code plus any source code changes using the
107                 ///   checked/unchecked statements or expressions.   Contrast this with
108                 ///   the ConstantCheckState flag.
109                 /// </summary>
110                 
111                 public bool CheckState;
112
113                 /// <summary>
114                 ///   The constant check state is always set to `true' and cant be changed
115                 ///   from the command line.  The source code can change this setting with
116                 ///   the `checked' and `unchecked' statements and expressions. 
117                 /// </summary>
118                 public bool ConstantCheckState;
119
120                 /// <summary>
121                 ///   Whether we are emitting code inside a static or instance method
122                 /// </summary>
123                 public bool IsStatic;
124
125                 /// <summary>
126                 ///   The value that is allowed to be returned or NULL if there is no
127                 ///   return type.
128                 /// </summary>
129                 public Type ReturnType;
130
131                 /// <summary>
132                 ///   Points to the Type (extracted from the TypeContainer) that
133                 ///   declares this body of code
134                 /// </summary>
135                 public Type ContainerType;
136                 
137                 /// <summary>
138                 ///   Whether this is generating code for a constructor
139                 /// </summary>
140                 public bool IsConstructor;
141                 
142                 /// <summary>
143                 ///   Keeps track of the Type to LocalBuilder temporary storage created
144                 ///   to store structures (used to compute the address of the structure
145                 ///   value on structure method invocations)
146                 /// </summary>
147                 public Hashtable temporary_storage;
148
149                 public Block CurrentBlock;
150
151                 /// <summary>
152                 ///   The location where we store the return value.
153                 /// </summary>
154                 LocalBuilder return_value;
155
156                 /// <summary>
157                 ///   The location where return has to jump to return the
158                 ///   value
159                 /// </summary>
160                 public Label ReturnLabel;
161
162                 /// <summary>
163                 ///   Whether we are in a Finally block
164                 /// </summary>
165                 public bool InFinally;
166
167                 /// <summary>
168                 ///   Whether we are in a Try block
169                 /// </summary>
170                 public bool InTry;
171
172                 /// <summary>
173                 ///   Whether we are in a Catch block
174                 /// </summary>
175                 public bool InCatch;
176
177                 /// <summary>
178                 ///  Whether we are inside an unsafe block
179                 /// </summary>
180                 public bool InUnsafe;
181                 
182                 /// <summary>
183                 ///   Location for this EmitContext
184                 /// </summary>
185                 public Location loc;
186                 
187                 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
188                                     Type return_type, int code_flags, bool is_constructor)
189                 {
190                         this.ig = ig;
191
192                         TypeContainer = parent;
193                         DeclSpace = ds;
194                         CheckState = RootContext.Checked;
195                         ConstantCheckState = true;
196                         
197                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
198                         ReturnType = return_type;
199                         IsConstructor = is_constructor;
200                         CurrentBlock = null;
201                         ContainerType = parent.TypeBuilder;
202                         InUnsafe = ((parent.ModFlags | code_flags) & Modifiers.UNSAFE) != 0;
203                         loc = l;
204                         
205                         if (ReturnType == TypeManager.void_type)
206                                 ReturnType = null;
207                 }
208
209                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
210                                     Type return_type, int code_flags, bool is_constructor)
211                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
212                 {
213                 }
214
215                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
216                                     Type return_type, int code_flags)
217                         : this (tc, tc, l, ig, return_type, code_flags, false)
218                 {
219                 }
220
221                 public void EmitTopBlock (Block block, Location loc)
222                 {
223                         bool has_ret = false;
224
225 //                      Console.WriteLine ("Emitting: " + loc);
226                         if (block != null){
227                                 int errors = Report.Errors;
228                                 
229                                 block.EmitMeta (this, block, 0);
230                                 
231                                 if (Report.Errors == errors){
232                                         has_ret = block.Emit (this);
233
234                                         if (Report.Errors == errors){
235                                                 if (RootContext.WarningLevel >= 3)
236                                                         block.UsageWarning ();
237                                         }
238                                 }
239                         }
240
241                         if (ReturnType != null && !has_ret){
242                                 //
243                                 // FIXME: we need full flow analysis to implement this
244                                 // correctly and emit an error instead of a warning.
245                                 //
246                                 //
247                                 Report.Error (161, loc, "Not all code paths return a value");
248                                 return;
249                         }
250
251                         if (return_value != null){
252                                 ig.MarkLabel (ReturnLabel);
253                                 ig.Emit (OpCodes.Ldloc, return_value);
254                                 ig.Emit (OpCodes.Ret);
255                         } else {
256                                 if (!has_ret){
257                                         if (!InTry)
258                                                 ig.Emit (OpCodes.Ret);
259                                 }
260                         }
261                 }
262
263                 /// <summary>
264                 ///   Returns a temporary storage for a variable of type t as 
265                 ///   a local variable in the current body.
266                 /// </summary>
267                 public LocalBuilder GetTemporaryStorage (Type t)
268                 {
269                         LocalBuilder location;
270                         
271                         if (temporary_storage != null){
272                                 location = (LocalBuilder) temporary_storage [t];
273                                 if (location != null)
274                                         return location;
275                         }
276                         
277                         location = ig.DeclareLocal (t);
278                         
279                         return location;
280                 }
281
282                 public void FreeTemporaryStorage (LocalBuilder b)
283                 {
284                         // Empty for now.
285                 }
286
287                 /// <summary>
288                 ///   Current loop begin and end labels.
289                 /// </summary>
290                 public Label LoopBegin, LoopEnd;
291
292                 /// <summary>
293                 ///   Whether we are inside a loop and break/continue are possible.
294                 /// </summary>
295                 public bool  InLoop;
296
297                 /// <summary>
298                 ///   Default target in a switch statement.   Only valid if
299                 ///   InSwitch is true
300                 /// </summary>
301                 public Label DefaultTarget;
302
303                 /// <summary>
304                 ///   If this is non-null, points to the current switch statement
305                 /// </summary>
306                 public Switch Switch;
307
308                 /// <summary>
309                 ///   ReturnValue creates on demand the LocalBuilder for the
310                 ///   return value from the function.  By default this is not
311                 ///   used.  This is only required when returns are found inside
312                 ///   Try or Catch statements.
313                 /// </summary>
314                 public LocalBuilder TemporaryReturn ()
315                 {
316                         if (return_value == null){
317                                 return_value = ig.DeclareLocal (ReturnType);
318                                 ReturnLabel = ig.DefineLabel ();
319                         }
320
321                         return return_value;
322                 }
323
324                 /// <summary>
325                 ///   A dynamic This that is shared by all variables in a emitcontext.
326                 ///   Created on demand.
327                 /// </summary>
328                 public Expression my_this;
329                 public Expression This {
330                         get {
331                                 if (my_this == null)
332                                         my_this = new This (loc).Resolve (this);
333
334                                 return my_this;
335                         }
336                 }
337         }
338 }