2001-10-04 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.Reflection;
12 using System.Reflection.Emit;
13
14 namespace CIR {
15         
16         public class CodeGen {
17                 AppDomain current_domain;
18                 AssemblyBuilder assembly_builder;
19                 ModuleBuilder   module_builder;
20
21                 string Basename (string name)
22                 {
23                         int pos = name.LastIndexOf ("/");
24
25                         if (pos != -1)
26                                 return name.Substring (pos + 1);
27
28                         pos = name.LastIndexOf ("\\");
29                         if (pos != -1)
30                                 return name.Substring (pos + 1);
31
32                         return name;
33                 }
34                 
35                 public CodeGen (string name, string output)
36                 {
37                         AssemblyName an;
38                         
39                         an = new AssemblyName ();
40                         an.Name = name;
41                         current_domain = AppDomain.CurrentDomain;
42                         assembly_builder = current_domain.DefineDynamicAssembly (
43                                 an, AssemblyBuilderAccess.RunAndSave);
44
45                         //
46                         // Pass a path-less name to DefineDynamicModule.  Wonder how
47                         // this copes with output in different directories then.
48                         // FIXME: figure out how this copes with --output /tmp/blah
49                         //
50                         module_builder = assembly_builder.DefineDynamicModule (
51                                 Basename (name), Basename (output));
52                 }
53                 
54                 public AssemblyBuilder AssemblyBuilder {
55                         get {
56                                 return assembly_builder;
57                         }
58                 }
59                 
60                 public ModuleBuilder ModuleBuilder {
61                         get {
62                                 return module_builder;
63                         }
64                 }
65                 
66                 public void Save (string name)
67                 {
68                         try {
69                                 assembly_builder.Save (Basename (name));
70                         } catch (System.IO.IOException io){
71                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
72                         }
73                 }
74         }
75
76         public struct EmitContext {
77                 public TypeContainer parent;
78                 public ILGenerator   ig;
79
80                 // FIXME: FIXME: FIXME!
81                 // This structure has to be kept small.  We need to figure
82                 // out ways of moving the CheckState somewhere else
83                 //
84                 // tracks the state of checked/unchecked arithmetic
85                 // generation.
86                 //
87                 public bool CheckState;
88                 
89                 public EmitContext (TypeContainer parent, ILGenerator ig)
90                 {
91                         this.parent = parent;
92                         this.ig = ig;
93                         CheckState = false;
94                 }
95
96                 public bool ConvertTo (Type target, Type source, bool verbose)
97                 {
98                         if (target == source)
99                                 return true;
100
101                         if (verbose)
102                                 Report.Error (
103                                         31, "Can not convert to type bool");
104                         
105                         return false;
106                 }
107                 
108                 public bool EmitBoolExpression (Expression e)
109                 {
110                         e = e.Resolve (parent);
111
112                         if (e == null)
113                                 return false;
114
115                         if (e.Type != TypeManager.bool_type)
116                                 e = Expression.ConvertImplicit (parent, e, TypeManager.bool_type,
117                                                                 new Location (-1));
118
119                         if (e == null){
120                                 Report.Error (
121                                         31, "Can not convert the expression to a boolean");
122                                 return false;
123                         }
124                         
125                         e.Emit (this);
126
127                         return true;
128                 }
129
130                 public void EmitExpression (Expression e)
131                 {
132                         e = e.Resolve (parent);
133
134                         if (e != null)
135                                 e.Emit (this);     
136                 }
137
138                 //
139                 // Emits an If statement.  Returns true if the last opcode
140                 // emitted was a ret opcode.
141                 //
142                 public bool EmitIf (If s)
143                 {
144                         Label false_target = ig.DefineLabel ();
145                         Label end;
146                         Statement false_stat = s.FalseStatement;
147                         bool is_ret;
148                         
149                         if (!EmitBoolExpression (s.Expr))
150                                 return false;
151                         
152                         ig.Emit (OpCodes.Brfalse, false_target);
153                         is_ret = EmitStatement (s.TrueStatement);
154
155                         if (false_stat != null){
156                                 end = ig.DefineLabel ();
157                                 ig.Emit (OpCodes.Br, end);
158                         
159                                 ig.MarkLabel (false_target);
160                                 is_ret = EmitStatement (s.FalseStatement);
161
162                                 if (false_stat != null)
163                                         ig.MarkLabel (end);
164                         } else
165                                 ig.MarkLabel (false_target);
166
167                         return is_ret;
168                 }
169
170                 public void EmitDo (Do s)
171                 {
172                         Label loop = ig.DefineLabel ();
173
174                         ig.MarkLabel (loop);
175                         EmitStatement (s.EmbeddedStatement);
176                         EmitBoolExpression (s.Expr);
177                         ig.Emit (OpCodes.Brtrue, loop);
178                 }
179
180                 public void EmitWhile (While s)
181                 {
182                         Label while_eval = ig.DefineLabel ();
183                         Label exit = ig.DefineLabel ();
184                         
185                         ig.MarkLabel (while_eval);
186                         EmitBoolExpression (s.Expr);
187                         ig.Emit (OpCodes.Brfalse, exit);
188                         EmitStatement (s.Statement);
189                         ig.Emit (OpCodes.Br, while_eval);
190                         ig.MarkLabel (exit);
191                 }
192
193                 public void EmitFor (For s)
194                 {
195                         Statement init = s.InitStatement;
196                         Statement incr = s.Increment;
197                         Label loop = ig.DefineLabel ();
198                         Label exit = ig.DefineLabel ();
199                         
200                         if (! (init is EmptyStatement))
201                                 EmitStatement (init);
202
203                         ig.MarkLabel (loop);
204                         EmitBoolExpression (s.Test);
205                         ig.Emit (OpCodes.Brfalse, exit);
206                         EmitStatement (s.Statement);
207                         if (!(incr is EmptyStatement))
208                                 EmitStatement (incr);
209                         ig.Emit (OpCodes.Br, loop);
210                         ig.MarkLabel (exit);
211                 }
212
213                 void EmitReturn (Return s)
214                 {
215                         Expression ret_expr = s.Expr;
216                         
217                         if (ret_expr != null)
218                                 EmitExpression (ret_expr);
219                         ig.Emit (OpCodes.Ret);
220                 }
221
222                 void EmitSwitch (Switch s)
223                 {
224                 }
225
226                 void EmitChecked (Checked s)
227                 {
228                         bool previous_state = CheckState;
229
230                         CheckState = true;
231                         EmitBlock (s.Block);
232                         CheckState = previous_state;
233                 }
234
235
236                 void EmitUnChecked (Unchecked s)
237                 {
238                         bool previous_state = CheckState;
239
240                         CheckState = false;
241                         EmitBlock (s.Block);
242                         CheckState = previous_state;
243                 }
244
245                 void EmitStatementExpression (StatementExpression s)
246                 {
247                         ExpressionStatement e = s.Expr;
248                         Expression ne;
249                         
250                         ne = e.Resolve (parent);
251                         if (ne != null){
252                                 if (ne is ExpressionStatement)
253                                         ((ExpressionStatement) ne).EmitStatement (this);
254                                 else {
255                                         ne.Emit (this);
256                                         ig.Emit (OpCodes.Pop);
257                                 }
258                         }
259                 }
260
261                 //
262                 // Emits the statemets `s'.
263                 //
264                 // Returns true if the statement had a `ret' opcode embedded
265                 //
266                 bool EmitStatement (Statement s)
267                 {
268                         // Console.WriteLine ("Emitting statement of type " + s.GetType ());
269                         
270                         if (s is If)
271                                 EmitIf ((If) s);
272                         else if (s is Do)
273                                 EmitDo ((Do) s);
274                         else if (s is While)
275                                 EmitWhile ((While) s);
276                         else if (s is For)
277                                 EmitFor ((For) s);
278                         else if (s is Return){
279                                 EmitReturn ((Return) s);
280                                 return true;
281                         } else if (s is Switch)
282                                 EmitSwitch ((Switch) s);
283                         else if (s is Checked)
284                                 EmitChecked ((Checked) s);
285                         else if (s is Unchecked)
286                                 EmitUnChecked ((Unchecked) s);
287                         else if (s is Block)
288                                 return EmitBlock ((Block) s);
289                         else if (s is StatementExpression)
290                                 EmitStatementExpression ((StatementExpression) s);
291                         else {
292                                 Console.WriteLine ("Unhandled Statement type: " +
293                                                    s.GetType ().ToString ());
294                         }
295
296                         return false;
297                 }
298
299                 bool EmitBlock (Block block)
300                 {
301                         bool is_ret = false;
302                         
303                         foreach (Statement s in block.Statements){
304                                 is_ret = EmitStatement (s);
305                         }
306
307                         return is_ret;
308                 }
309                 
310                 public void EmitTopBlock (Block block)
311                 {
312                         bool has_ret = false;
313                         
314                         if (block != null){
315                                 int errors = Report.Errors;
316                                 
317                                 block.EmitMeta (parent, ig, block, 0);
318                                 
319                                 if (Report.Errors == errors){
320                                         has_ret = EmitBlock (block);
321                                         
322                                         if (Report.Errors == errors)
323                                                 block.UsageWarning ();
324                                 }
325                         }
326
327                         if (!has_ret)
328                                 ig.Emit (OpCodes.Ret);
329                 }
330         }
331 }