2001-09-27 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 = "AssemblyName";
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 (Report report, 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                 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                                 parent.RootContext.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
118                         if (e == null || e.Type != TypeManager.bool_type){
119                                 parent.RootContext.Report.Error (
120                                         31, "Can not convert the expression to a boolean");
121                                 return false;
122                         }
123                         
124                         e.Emit (this);
125
126                         return true;
127                 }
128
129                 public void EmitExpression (Expression e)
130                 {
131                         e = e.Resolve (parent);
132
133                         if (e != null)
134                                 e.Emit (this);     
135                 }
136
137                 //
138                 // Emits an If statement.  Returns true if the last opcode
139                 // emitted was a ret opcode.
140                 //
141                 public bool EmitIf (If s)
142                 {
143                         Label false_target = ig.DefineLabel ();
144                         Label end;
145                         Statement false_stat = s.FalseStatement;
146                         bool is_ret;
147                         
148                         if (!EmitBoolExpression (s.Expr))
149                                 return false;
150                         
151                         ig.Emit (OpCodes.Brfalse, false_target);
152                         is_ret = EmitStatement (s.TrueStatement);
153
154                         if (false_stat != null){
155                                 end = ig.DefineLabel ();
156                                 ig.Emit (OpCodes.Br, end);
157                         
158                                 ig.MarkLabel (false_target);
159                                 is_ret = EmitStatement (s.FalseStatement);
160
161                                 if (false_stat != null)
162                                         ig.MarkLabel (end);
163                         } else
164                                 ig.MarkLabel (false_target);
165
166                         return is_ret;
167                 }
168
169                 public void EmitDo (Do s)
170                 {
171                         Label loop = ig.DefineLabel ();
172
173                         ig.MarkLabel (loop);
174                         EmitStatement (s.EmbeddedStatement);
175                         EmitBoolExpression (s.Expr);
176                         ig.Emit (OpCodes.Brtrue, loop);
177                 }
178
179                 public void EmitWhile (While s)
180                 {
181                         Label while_eval = ig.DefineLabel ();
182                         Label exit = ig.DefineLabel ();
183                         
184                         ig.MarkLabel (while_eval);
185                         EmitBoolExpression (s.Expr);
186                         ig.Emit (OpCodes.Brfalse, exit);
187                         EmitStatement (s.Statement);
188                         ig.Emit (OpCodes.Br, while_eval);
189                         ig.MarkLabel (exit);
190                 }
191
192                 public void EmitFor (For s)
193                 {
194                         Statement init = s.InitStatement;
195                         Statement incr = s.Increment;
196                         Label loop = ig.DefineLabel ();
197                         Label exit = ig.DefineLabel ();
198                         
199                         if (! (init is EmptyStatement))
200                                 EmitStatement (init);
201
202                         ig.MarkLabel (loop);
203                         EmitBoolExpression (s.Test);
204                         ig.Emit (OpCodes.Brfalse, exit);
205                         EmitStatement (s.Statement);
206                         if (!(incr is EmptyStatement))
207                                 EmitStatement (incr);
208                         ig.Emit (OpCodes.Br, loop);
209                         ig.MarkLabel (exit);
210                 }
211
212                 void EmitReturn (Return s)
213                 {
214                         Expression ret_expr = s.Expr;
215                         
216                         if (ret_expr != null)
217                                 EmitExpression (ret_expr);
218                         ig.Emit (OpCodes.Ret);
219                 }
220
221                 void EmitSwitch (Switch s)
222                 {
223                 }
224
225                 void EmitChecked (Checked s)
226                 {
227                         bool previous_state = CheckState;
228
229                         CheckState = true;
230                         EmitBlock (s.Block);
231                         CheckState = previous_state;
232                 }
233
234
235                 void EmitUnChecked (Unchecked s)
236                 {
237                         bool previous_state = CheckState;
238
239                         CheckState = false;
240                         EmitBlock (s.Block);
241                         CheckState = previous_state;
242                 }
243
244                 void EmitStatementExpression (StatementExpression s)
245                 {
246                         ExpressionStatement e = s.Expr;
247                         Expression ne;
248                         
249                         ne = e.Resolve (parent);
250                         if (ne != null){
251                                 if (ne is ExpressionStatement)
252                                         ((ExpressionStatement) ne).EmitStatement (this);
253                                 else {
254                                         ne.Emit (this);
255                                         ig.Emit (OpCodes.Pop);
256                                 }
257                         }
258                 }
259
260                 //
261                 // Emits the statemets `s'.
262                 //
263                 // Returns true if the statement had a `ret' opcode embedded
264                 //
265                 bool EmitStatement (Statement s)
266                 {
267                         // Console.WriteLine ("Emitting statement of type " + s.GetType ());
268                         
269                         if (s is If)
270                                 EmitIf ((If) s);
271                         else if (s is Do)
272                                 EmitDo ((Do) s);
273                         else if (s is While)
274                                 EmitWhile ((While) s);
275                         else if (s is For)
276                                 EmitFor ((For) s);
277                         else if (s is Return){
278                                 EmitReturn ((Return) s);
279                                 return true;
280                         } else if (s is Switch)
281                                 EmitSwitch ((Switch) s);
282                         else if (s is Checked)
283                                 EmitChecked ((Checked) s);
284                         else if (s is Unchecked)
285                                 EmitUnChecked ((Unchecked) s);
286                         else if (s is Block)
287                                 return EmitBlock ((Block) s);
288                         else if (s is StatementExpression)
289                                 EmitStatementExpression ((StatementExpression) s);
290                         else {
291                                 Console.WriteLine ("Unhandled Statement type: " +
292                                                    s.GetType ().ToString ());
293                         }
294
295                         return false;
296                 }
297
298                 bool EmitBlock (Block block)
299                 {
300                         bool is_ret = false;
301                         
302                         foreach (Statement s in block.Statements){
303                                 is_ret = EmitStatement (s);
304                         }
305
306                         return is_ret;
307                 }
308                 
309                 public void EmitTopBlock (Block block)
310                 {
311                         bool has_ret = false;
312                         
313                         if (block != null){
314                                 block.EmitMeta (parent, ig, block, 0);
315                                 has_ret = EmitBlock (block);
316                         }
317
318                         if (!has_ret)
319                                 ig.Emit (OpCodes.Ret);
320                 }
321         }
322 }