*** empty log message ***
[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                 Block current_block;
80                 
81                 // FIXME: FIXME: FIXME!
82                 // This structure has to be kept small.  We need to figure
83                 // out ways of moving the CheckState somewhere else
84                 //
85                 // tracks the state of checked/unchecked arithmetic
86                 // generation.
87                 //
88                 public bool CheckState;
89                 
90                 public EmitContext (TypeContainer parent, ILGenerator ig)
91                 {
92                         this.parent = parent;
93                         this.ig = ig;
94                         CheckState = false;
95                         current_block = null;
96                 }
97
98                 public bool ConvertTo (Type target, Type source, bool verbose)
99                 {
100                         if (target == source)
101                                 return true;
102
103                         if (verbose)
104                                 Report.Error (
105                                         31, "Can not convert to type bool");
106                         
107                         return false;
108                 }
109                 
110                 public bool EmitBoolExpression (Expression e)
111                 {
112                         e = e.Resolve (parent);
113
114                         if (e == null)
115                                 return false;
116
117                         if (e.Type != TypeManager.bool_type)
118                                 e = Expression.ConvertImplicit (parent, e, TypeManager.bool_type,
119                                                                 new Location (-1));
120
121                         if (e == null){
122                                 Report.Error (
123                                         31, "Can not convert the expression to a boolean");
124                                 return false;
125                         }
126                         
127                         e.Emit (this);
128
129                         return true;
130                 }
131
132                 public void EmitExpression (Expression e)
133                 {
134                         e = e.Resolve (parent);
135
136                         if (e != null)
137                                 e.Emit (this);     
138                 }
139
140                 //
141                 // Emits an If statement.  Returns true if the last opcode
142                 // emitted was a ret opcode.
143                 //
144                 public bool EmitIf (If s)
145                 {
146                         Label false_target = ig.DefineLabel ();
147                         Label end;
148                         Statement false_stat = s.FalseStatement;
149                         bool is_ret;
150                         
151                         if (!EmitBoolExpression (s.Expr))
152                                 return false;
153                         
154                         ig.Emit (OpCodes.Brfalse, false_target);
155                         is_ret = EmitStatement (s.TrueStatement);
156
157                         if (false_stat != null){
158                                 end = ig.DefineLabel ();
159                                 ig.Emit (OpCodes.Br, end);
160                         
161                                 ig.MarkLabel (false_target);
162                                 is_ret = EmitStatement (s.FalseStatement);
163
164                                 if (false_stat != null)
165                                         ig.MarkLabel (end);
166                         } else
167                                 ig.MarkLabel (false_target);
168
169                         return is_ret;
170                 }
171
172                 public void EmitDo (Do s)
173                 {
174                         Label loop = ig.DefineLabel ();
175
176                         ig.MarkLabel (loop);
177                         EmitStatement (s.EmbeddedStatement);
178                         EmitBoolExpression (s.Expr);
179                         ig.Emit (OpCodes.Brtrue, loop);
180                 }
181
182                 public void EmitWhile (While s)
183                 {
184                         Label while_eval = ig.DefineLabel ();
185                         Label exit = ig.DefineLabel ();
186                         
187                         ig.MarkLabel (while_eval);
188                         EmitBoolExpression (s.Expr);
189                         ig.Emit (OpCodes.Brfalse, exit);
190                         EmitStatement (s.Statement);
191                         ig.Emit (OpCodes.Br, while_eval);
192                         ig.MarkLabel (exit);
193                 }
194
195                 public void EmitFor (For s)
196                 {
197                         Statement init = s.InitStatement;
198                         Statement incr = s.Increment;
199                         Label loop = ig.DefineLabel ();
200                         Label exit = ig.DefineLabel ();
201                         
202                         if (! (init is EmptyStatement))
203                                 EmitStatement (init);
204
205                         ig.MarkLabel (loop);
206                         EmitBoolExpression (s.Test);
207                         ig.Emit (OpCodes.Brfalse, exit);
208                         EmitStatement (s.Statement);
209                         if (!(incr is EmptyStatement))
210                                 EmitStatement (incr);
211                         ig.Emit (OpCodes.Br, loop);
212                         ig.MarkLabel (exit);
213                 }
214
215                 bool ProbeCollectionType (Type t)
216                 {
217                         return true;
218                 }
219                 
220                 void EmitForeach (Foreach f)
221                 {
222                         Expression e = f.Expr;
223
224                         e = e.Resolve (parent);
225                         if (e == null)
226                                 return;
227
228                         if (!ProbeCollectionType (e.Type))
229                                 return;
230                 }
231                 
232                 void EmitReturn (Return s)
233                 {
234                         Expression ret_expr = s.Expr;
235                         
236                         if (ret_expr != null)
237                                 EmitExpression (ret_expr);
238                         ig.Emit (OpCodes.Ret);
239                 }
240
241                 void EmitSwitch (Switch s)
242                 {
243                 }
244
245                 void EmitChecked (Checked s)
246                 {
247                         bool previous_state = CheckState;
248
249                         CheckState = true;
250                         EmitBlock (s.Block);
251                         CheckState = previous_state;
252                 }
253
254
255                 void EmitUnChecked (Unchecked s)
256                 {
257                         bool previous_state = CheckState;
258
259                         CheckState = false;
260                         EmitBlock (s.Block);
261                         CheckState = previous_state;
262                 }
263
264                 void EmitStatementExpression (StatementExpression s)
265                 {
266                         ExpressionStatement e = s.Expr;
267                         Expression ne;
268                         
269                         ne = e.Resolve (parent);
270                         if (ne != null){
271                                 if (ne is ExpressionStatement)
272                                         ((ExpressionStatement) ne).EmitStatement (this);
273                                 else {
274                                         ne.Emit (this);
275                                         ig.Emit (OpCodes.Pop);
276                                 }
277                         }
278                 }
279
280                 //
281                 // Emits the statemets `s'.
282                 //
283                 // Returns true if the statement had a `ret' opcode embedded
284                 //
285                 bool EmitStatement (Statement s)
286                 {
287                         // Console.WriteLine ("Emitting statement of type " + s.GetType ());
288                         
289                         if (s is If)
290                                 EmitIf ((If) s);
291                         else if (s is Do)
292                                 EmitDo ((Do) s);
293                         else if (s is While)
294                                 EmitWhile ((While) s);
295                         else if (s is For)
296                                 EmitFor ((For) s);
297                         else if (s is Return){
298                                 EmitReturn ((Return) s);
299                                 return true;
300                         } else if (s is Switch)
301                                 EmitSwitch ((Switch) s);
302                         else if (s is Checked)
303                                 EmitChecked ((Checked) s);
304                         else if (s is Unchecked)
305                                 EmitUnChecked ((Unchecked) s);
306                         else if (s is Block)
307                                 return EmitBlock ((Block) s);
308                         else if (s is StatementExpression)
309                                 EmitStatementExpression ((StatementExpression) s);
310                         else if (s is Foreach)
311                                 EmitForeach ((Foreach) s);
312                         else {
313                                 Console.WriteLine ("Unhandled Statement type: " +
314                                                    s.GetType ().ToString ());
315                         }
316
317                         return false;
318                 }
319
320                 bool EmitBlock (Block block)
321                 {
322                         bool is_ret = false;
323                         Block last_block = current_block;
324                         
325                         current_block = block;
326                         foreach (Statement s in block.Statements){
327                                 is_ret = EmitStatement (s);
328                         }
329                         current_block = last_block;
330
331                         return is_ret;
332                 }
333                 
334                 public void EmitTopBlock (Block block)
335                 {
336                         bool has_ret = false;
337                         
338                         if (block != null){
339                                 int errors = Report.Errors;
340                                 
341                                 block.EmitMeta (parent, ig, block, 0);
342                                 
343                                 if (Report.Errors == errors){
344                                         has_ret = EmitBlock (block);
345                                         
346                                         if (Report.Errors == errors)
347                                                 block.UsageWarning ();
348                                 }
349                         }
350
351                         if (!has_ret)
352                                 ig.Emit (OpCodes.Ret);
353                 }
354         }
355 }