2002-01-11 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                 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 TypeContainer TypeContainer;
97                 public ILGenerator   ig;
98                 public bool CheckState;
99
100                 /// <summary>
101                 ///   Whether we are emitting code inside a static or instance method
102                 /// </summary>
103                 public bool IsStatic;
104
105                 /// <summary>
106                 ///   The value that is allowed to be returned or NULL if there is no
107                 ///   return type.
108                 /// </summary>
109                 public Type ReturnType;
110
111                 /// <summary>
112                 ///   Points to the Type (extracted from the TypeContainer) that
113                 ///   declares this body of code
114                 /// </summary>
115                 public Type ContainerType;
116                 
117                 /// <summary>
118                 ///   Whether this is generating code for a constructor
119                 /// </summary>
120                 public bool IsConstructor;
121                 
122                 /// <summary>
123                 ///   Keeps track of the Type to LocalBuilder temporary storage created
124                 ///   to store structures (used to compute the address of the structure
125                 ///   value on structure method invocations)
126                 /// </summary>
127                 public Hashtable temporary_storage;
128
129                 public Block CurrentBlock;
130
131                 /// <summary>
132                 ///   The location where we store the return value.
133                 /// </summary>
134                 LocalBuilder return_value;
135
136                 /// <summary>
137                 ///   The location where return has to jump to return the
138                 ///   value
139                 /// </summary>
140                 public Label ReturnLabel;
141
142                 /// <summary>
143                 ///   Whether we are in a Finally block
144                 /// </summary>
145                 public bool InFinally;
146
147                 /// <summary>
148                 ///   Whether we are in a Try block
149                 /// </summary>
150                 public bool InTry;
151
152                 /// <summary>
153                 ///   Whether we are in a Catch block
154                 /// </summary>
155                 public bool InCatch;
156                 
157                 /// <summary>
158                 ///   Location for this EmitContext
159                 /// </summary>
160                 public Location loc;
161                 
162                 public EmitContext (TypeContainer parent, Location l, ILGenerator ig,
163                                     Type return_type, int code_flags, bool is_constructor)
164                 {
165                         this.ig = ig;
166
167                         TypeContainer = parent;
168                         CheckState = RootContext.Checked;
169                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
170                         ReturnType = return_type;
171                         IsConstructor = is_constructor;
172                         CurrentBlock = null;
173                         ContainerType = parent.TypeBuilder;
174                         loc = l;
175
176                         if (ReturnType == TypeManager.void_type)
177                                 ReturnType = null;
178                 }
179
180                 public EmitContext (TypeContainer parent, Location l, ILGenerator ig,
181                                     Type return_type, int code_flags)
182                         : this (parent, l, ig, return_type, code_flags, false)
183                 {
184                 }
185
186                 public void EmitTopBlock (Block block, Location loc)
187                 {
188                         bool has_ret = false;
189
190 //                      Console.WriteLine ("Emitting: " + loc);
191                         if (block != null){
192                                 int errors = Report.Errors;
193                                 
194                                 block.EmitMeta (TypeContainer, ig, block, 0);
195                                 
196                                 if (Report.Errors == errors){
197                                         has_ret = block.Emit (this);
198
199                                         if (Report.Errors == errors){
200                                                 if (RootContext.WarningLevel >= 3)
201                                                         block.UsageWarning ();
202                                         }
203                                 }
204                         }
205
206                         if (ReturnType != null && !has_ret){
207                                 //
208                                 // FIXME: we need full flow analysis to implement this
209                                 // correctly.
210                                 //
211                                 // Otherwise we report a non-existant error on cs-parser.cs
212                                 // yyparse routine
213                                 //
214                                 //Report.Error (161, loc, "Not all code paths return a value");
215
216                                 return;
217                         }
218
219                         if (return_value != null){
220                                 ig.MarkLabel (ReturnLabel);
221                                 ig.Emit (OpCodes.Ldloc, return_value);
222                                 ig.Emit (OpCodes.Ret);
223                         } else {
224                                 if (!has_ret)
225                                         ig.Emit (OpCodes.Ret);
226                         }
227                 }
228
229                 /// <summary>
230                 ///   Returns a temporary storage for a variable of type t as 
231                 ///   a local variable in the current body.
232                 /// </summary>
233                 public LocalBuilder GetTemporaryStorage (Type t)
234                 {
235                         LocalBuilder location;
236                         
237                         if (temporary_storage == null)
238                                 temporary_storage = new Hashtable ();
239                         
240                         location = (LocalBuilder) temporary_storage [t];
241                         if (location != null)
242                                 return location;
243                         
244                         location = ig.DeclareLocal (t);
245                         temporary_storage.Add (t, location);
246                         
247                         return location;
248                 }
249
250                 /// <summary>
251                 ///   Current loop begin and end labels.
252                 /// </summary>
253                 public Label LoopBegin, LoopEnd;
254
255                 /// <summary>
256                 ///   Whether we are inside a loop and break/continue are possible.
257                 /// </summary>
258                 public bool  InLoop;
259
260                 /// <summary>
261                 ///   Default target in a switch statement.   Only valid if
262                 ///   InSwitch is true
263                 /// </summary>
264                 public Label DefaultTarget;
265
266                 /// <summary>
267                 ///   If this is non-null, points to the current switch statement
268                 /// </summary>
269                 public Switch Switch;
270
271                 /// <summary>
272                 ///   ReturnValue creates on demand the LocalBuilder for the
273                 ///   return value from the function.  By default this is not
274                 ///   used.  This is only required when returns are found inside
275                 ///   Try or Catch statements.
276                 /// </summary>
277                 public LocalBuilder TemporaryReturn ()
278                 {
279                         if (return_value == null){
280                                 return_value = ig.DeclareLocal (ReturnType);
281                                 ReturnLabel = ig.DefineLabel ();
282                         }
283
284                         return return_value;
285                 }
286
287                 /// <summary>
288                 ///   A dynamic This that is shared by all variables in a emitcontext.
289                 ///   Created on demand.
290                 /// </summary>
291                 public Expression my_this;
292                 public Expression This {
293                         get {
294                                 if (my_this == null)
295                                         my_this = new This (loc).Resolve (this);
296
297                                 return my_this;
298                         }
299                 }
300         }
301 }