2003-05-18 Martin Baulig <martin@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.IO;
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15
16 namespace Mono.CSharp {
17
18         /// <summary>
19         ///    Code generator class.
20         /// </summary>
21         public class CodeGen {
22                 static AppDomain current_domain;
23                 public static AssemblyBuilder AssemblyBuilder;
24                 public static ModuleBuilder   ModuleBuilder;
25
26                 static public SymbolWriter SymbolWriter;
27
28                 public static string Basename (string name)
29                 {
30                         int pos = name.LastIndexOf ("/");
31
32                         if (pos != -1)
33                                 return name.Substring (pos + 1);
34
35                         pos = name.LastIndexOf ("\\");
36                         if (pos != -1)
37                                 return name.Substring (pos + 1);
38
39                         return name;
40                 }
41
42                 public static string Dirname (string name)
43                 {
44                         int pos = name.LastIndexOf ("/");
45
46                         if (pos != -1)
47                                 return name.Substring (0, pos);
48
49                         pos = name.LastIndexOf ("\\");
50                         if (pos != -1)
51                                 return name.Substring (0, pos);
52
53                         return ".";
54                 }
55
56                 static string TrimExt (string name)
57                 {
58                         int pos = name.LastIndexOf (".");
59
60                         return name.Substring (0, pos);
61                 }
62
63                 static public string FileName;
64
65                 //
66                 // Initializes the symbol writer
67                 //
68                 static void InitializeSymbolWriter ()
69                 {
70                         SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
71
72                         //
73                         // If we got an ISymbolWriter instance, initialize it.
74                         //
75                         if (SymbolWriter == null) {
76                                 Report.Warning (
77                                         -18, "Could not find the symbol writer assembly (Mono.CSharp.Debugger.dll). This is normally an installation problem. Please make sure to compile and install the mcs/class/Mono.CSharp.Debugger directory.");
78                                 return;
79                         }
80                 }
81
82                 //
83                 // Initializes the code generator variables
84                 //
85                 static public void Init (string name, string output, bool want_debugging_support)
86                 {
87                         AssemblyName an;
88
89                         FileName = output;
90                         an = new AssemblyName ();
91                         an.Name = Path.GetFileNameWithoutExtension (name);
92                         
93                         current_domain = AppDomain.CurrentDomain;
94                         AssemblyBuilder = current_domain.DefineDynamicAssembly (
95                                 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
96
97                         //
98                         // Pass a path-less name to DefineDynamicModule.  Wonder how
99                         // this copes with output in different directories then.
100                         // FIXME: figure out how this copes with --output /tmp/blah
101                         //
102                         // If the third argument is true, the ModuleBuilder will dynamically
103                         // load the default symbol writer.
104                         //
105                         ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
106                                 Basename (name), Basename (output), want_debugging_support);
107
108                         if (want_debugging_support)
109                                 InitializeSymbolWriter ();
110                 }
111
112                 static public void Save (string name)
113                 {
114                         try {
115                                 AssemblyBuilder.Save (Basename (name));
116                         } catch (System.IO.IOException io){
117                                 Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
118                         }
119                 }
120         }
121
122         /// <summary>
123         ///   An Emit Context is created for each body of code (from methods,
124         ///   properties bodies, indexer bodies or constructor bodies)
125         /// </summary>
126         public class EmitContext {
127                 public DeclSpace DeclSpace;
128                 public DeclSpace TypeContainer;
129                 public ILGenerator   ig;
130
131                 /// <summary>
132                 ///   This variable tracks the `checked' state of the compilation,
133                 ///   it controls whether we should generate code that does overflow
134                 ///   checking, or if we generate code that ignores overflows.
135                 ///
136                 ///   The default setting comes from the command line option to generate
137                 ///   checked or unchecked code plus any source code changes using the
138                 ///   checked/unchecked statements or expressions.   Contrast this with
139                 ///   the ConstantCheckState flag.
140                 /// </summary>
141                 
142                 public bool CheckState;
143
144                 /// <summary>
145                 ///   The constant check state is always set to `true' and cant be changed
146                 ///   from the command line.  The source code can change this setting with
147                 ///   the `checked' and `unchecked' statements and expressions. 
148                 /// </summary>
149                 public bool ConstantCheckState;
150
151                 /// <summary>
152                 ///   Whether we are emitting code inside a static or instance method
153                 /// </summary>
154                 public bool IsStatic;
155
156                 /// <summary>
157                 ///   Whether we are emitting a field initializer
158                 /// </summary>
159                 public bool IsFieldInitializer;
160
161                 /// <summary>
162                 ///   The value that is allowed to be returned or NULL if there is no
163                 ///   return type.
164                 /// </summary>
165                 public Type ReturnType;
166
167                 /// <summary>
168                 ///   Points to the Type (extracted from the TypeContainer) that
169                 ///   declares this body of code
170                 /// </summary>
171                 public Type ContainerType;
172                 
173                 /// <summary>
174                 ///   Whether this is generating code for a constructor
175                 /// </summary>
176                 public bool IsConstructor;
177
178                 /// <summary>
179                 ///   Whether we're control flow analysis enabled
180                 /// </summary>
181                 public bool DoFlowAnalysis;
182                 
183                 /// <summary>
184                 ///   Keeps track of the Type to LocalBuilder temporary storage created
185                 ///   to store structures (used to compute the address of the structure
186                 ///   value on structure method invocations)
187                 /// </summary>
188                 public Hashtable temporary_storage;
189
190                 public Block CurrentBlock;
191
192                 public int CurrentFile;
193
194                 /// <summary>
195                 ///   The location where we store the return value.
196                 /// </summary>
197                 LocalBuilder return_value;
198
199                 /// <summary>
200                 ///   The location where return has to jump to return the
201                 ///   value
202                 /// </summary>
203                 public Label ReturnLabel;
204
205                 /// <summary>
206                 ///   If we already defined the ReturnLabel
207                 /// </summary>
208                 public bool HasReturnLabel;
209
210                 /// <summary>
211                 ///   Whether we are in a Finally block
212                 /// </summary>
213                 public bool InFinally;
214
215                 /// <summary>
216                 ///   Whether we are in a Try block
217                 /// </summary>
218                 public bool InTry;
219
220                 /// <summary>
221                 ///   Whether we are inside an iterator block.
222                 /// </summary>
223                 public bool InIterator;
224                 
225                 /// <summary>
226                 ///   Whether remapping of locals, parameters and fields is turned on.
227                 ///   Used by iterators and anonymous methods.
228                 /// </summary>
229                 public bool RemapToProxy;
230
231                 /// <summary>
232                 ///   Whether we are in a Catch block
233                 /// </summary>
234                 public bool InCatch;
235
236                 /// <summary>
237                 ///  Whether we are inside an unsafe block
238                 /// </summary>
239                 public bool InUnsafe;
240
241                 /// <summary>
242                 ///  Whether we are inside an anonymous method.
243                 /// </summary>
244                 public bool InAnonymousMethod;
245                 
246                 /// <summary>
247                 ///   Location for this EmitContext
248                 /// </summary>
249                 public Location loc;
250
251                 /// <summary>
252                 ///   Used to flag that it is ok to define types recursively, as the
253                 ///   expressions are being evaluated as part of the type lookup
254                 ///   during the type resolution process
255                 /// </summary>
256                 public bool ResolvingTypeTree;
257                 
258                 /// <summary>
259                 ///   Inside an enum definition, we do not resolve enumeration values
260                 ///   to their enumerations, but rather to the underlying type/value
261                 ///   This is so EnumVal + EnumValB can be evaluated.
262                 ///
263                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
264                 ///   we relax the rules
265                 /// </summary>
266                 public bool InEnumContext;
267
268                 protected Stack FlowStack;
269                 
270                 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
271                                     Type return_type, int code_flags, bool is_constructor)
272                 {
273                         this.ig = ig;
274
275                         TypeContainer = parent;
276                         DeclSpace = ds;
277                         CheckState = RootContext.Checked;
278                         ConstantCheckState = true;
279                         
280                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
281                         InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
282                         RemapToProxy = InIterator;
283                         ReturnType = return_type;
284                         IsConstructor = is_constructor;
285                         CurrentBlock = null;
286                         CurrentFile = 0;
287                         
288                         if (parent != null){
289                                 // Can only be null for the ResolveType contexts.
290                                 ContainerType = parent.TypeBuilder;
291                                 if (parent.UnsafeContext)
292                                         InUnsafe = true;
293                                 else
294                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
295                         }
296                         loc = l;
297
298                         FlowStack = new Stack ();
299                         
300                         if (ReturnType == TypeManager.void_type)
301                                 ReturnType = null;
302                 }
303
304                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
305                                     Type return_type, int code_flags, bool is_constructor)
306                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
307                 {
308                 }
309
310                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
311                                     Type return_type, int code_flags)
312                         : this (tc, tc, l, ig, return_type, code_flags, false)
313                 {
314                 }
315
316                 public FlowBranching CurrentBranching {
317                         get {
318                                 return (FlowBranching) FlowStack.Peek ();
319                         }
320                 }
321
322                 // <summary>
323                 //   Starts a new code branching.  This inherits the state of all local
324                 //   variables and parameters from the current branching.
325                 // </summary>
326                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
327                 {
328                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
329
330                         FlowStack.Push (cfb);
331
332                         return cfb;
333                 }
334
335                 // <summary>
336                 //   Starts a new code branching for block `block'.
337                 // </summary>
338                 public FlowBranching StartFlowBranching (Block block)
339                 {
340                         FlowBranching cfb;
341                         FlowBranchingType type;
342
343                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
344                                 type = FlowBranchingType.SWITCH_SECTION;
345                         else
346                                 type = FlowBranchingType.BLOCK;
347
348                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
349
350                         FlowStack.Push (cfb);
351
352                         return cfb;
353                 }
354
355                 // <summary>
356                 //   Ends a code branching.  Merges the state of locals and parameters
357                 //   from all the children of the ending branching.
358                 // </summary>
359                 public FlowReturns EndFlowBranching ()
360                 {
361                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
362
363                         return CurrentBranching.MergeChild (cfb);
364                 }
365
366                 // <summary>
367                 //   Kills the current code branching.  This throws away any changed state
368                 //   information and should only be used in case of an error.
369                 // </summary>
370                 public void KillFlowBranching ()
371                 {
372                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
373                 }
374
375                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
376                 {
377                         bool has_ret = false;
378
379                         if (!Location.IsNull (loc))
380                                 CurrentFile = loc.File;
381
382                         if (block != null){
383                             try {
384                                 int errors = Report.Errors;
385
386                                 block.EmitMeta (this, ip, block);
387
388                                 if (Report.Errors == errors){
389                                         bool old_do_flow_analysis = DoFlowAnalysis;
390                                         DoFlowAnalysis = true;
391
392                                         FlowBranching cfb = new FlowBranching (block, loc);
393                                         FlowStack.Push (cfb);
394
395                                         if (!block.Resolve (this)) {
396                                                 FlowStack.Pop ();
397                                                 DoFlowAnalysis = old_do_flow_analysis;
398                                                 return;
399                                         }
400
401                                         cfb = (FlowBranching) FlowStack.Pop ();
402                                         FlowReturns returns = cfb.MergeTopBlock ();
403
404                                         DoFlowAnalysis = old_do_flow_analysis;
405
406                                         has_ret = block.Emit (this);
407
408                                         if ((returns == FlowReturns.ALWAYS) ||
409                                             (returns == FlowReturns.EXCEPTION) ||
410                                             (returns == FlowReturns.UNREACHABLE))
411                                                 has_ret = true;
412
413                                         if (Report.Errors == errors){
414                                                 if (RootContext.WarningLevel >= 3)
415                                                         block.UsageWarning ();
416                                         }
417                                 }
418                             } catch {
419                                         Console.WriteLine ("Exception caught by the compiler while compiling:");
420                                         Console.WriteLine ("   Block that caused the problem begin at: " + loc);
421                                         
422                                         if (CurrentBlock != null){
423                                                 Console.WriteLine ("                     Block being compiled: [{0},{1}]",
424                                                                    CurrentBlock.StartLocation, CurrentBlock.EndLocation);
425                                         }
426                                         throw;
427                             }
428                         }
429
430                         if (ReturnType != null && !has_ret){
431                                 //
432                                 // FIXME: we need full flow analysis to implement this
433                                 // correctly and emit an error instead of a warning.
434                                 //
435                                 //
436                                 if (!InIterator){
437                                         Report.Error (161, loc, "Not all code paths return a value");
438                                         return;
439                                 }
440                         }
441
442                         if (HasReturnLabel)
443                                 ig.MarkLabel (ReturnLabel);
444                         if (return_value != null){
445                                 ig.Emit (OpCodes.Ldloc, return_value);
446                                 ig.Emit (OpCodes.Ret);
447                         } else {
448                                 if (!InTry){
449                                         if (InIterator)
450                                                 has_ret = true;
451                                         
452                                         if (!has_ret || HasReturnLabel)
453                                                 ig.Emit (OpCodes.Ret);
454                                 }
455                         }
456                 }
457
458                 /// <summary>
459                 ///   This is called immediately before emitting an IL opcode to tell the symbol
460                 ///   writer to which source line this opcode belongs.
461                 /// </summary>
462                 public void Mark (Location loc, bool check_file)
463                 {
464                         if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
465                                 return;
466
467                         if (check_file && (CurrentFile != loc.File))
468                                 return;
469
470                         ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
471                 }
472
473                 /// <summary>
474                 ///   Returns a temporary storage for a variable of type t as 
475                 ///   a local variable in the current body.
476                 /// </summary>
477                 public LocalBuilder GetTemporaryStorage (Type t)
478                 {
479                         LocalBuilder location;
480                         
481                         if (temporary_storage != null){
482                                 location = (LocalBuilder) temporary_storage [t];
483                                 if (location != null)
484                                         return location;
485                         }
486                         
487                         location = ig.DeclareLocal (t);
488                         
489                         return location;
490                 }
491
492                 public void FreeTemporaryStorage (LocalBuilder b)
493                 {
494                         // Empty for now.
495                 }
496
497                 /// <summary>
498                 ///   Current loop begin and end labels.
499                 /// </summary>
500                 public Label LoopBegin, LoopEnd;
501
502                 /// <summary>
503                 ///   Whether we are inside a loop and break/continue are possible.
504                 /// </summary>
505                 public bool  InLoop;
506
507                 /// <summary>
508                 ///   This is incremented each time we enter a try/catch block and
509                 ///   decremented if we leave it.
510                 /// </summary>
511                 public int   TryCatchLevel;
512
513                 /// <summary>
514                 ///   The TryCatchLevel at the begin of the current loop.
515                 /// </summary>
516                 public int   LoopBeginTryCatchLevel;
517
518                 /// <summary>
519                 ///   Default target in a switch statement.   Only valid if
520                 ///   InSwitch is true
521                 /// </summary>
522                 public Label DefaultTarget;
523
524                 /// <summary>
525                 ///   If this is non-null, points to the current switch statement
526                 /// </summary>
527                 public Switch Switch;
528
529                 /// <summary>
530                 ///   ReturnValue creates on demand the LocalBuilder for the
531                 ///   return value from the function.  By default this is not
532                 ///   used.  This is only required when returns are found inside
533                 ///   Try or Catch statements.
534                 /// </summary>
535                 public LocalBuilder TemporaryReturn ()
536                 {
537                         if (return_value == null){
538                                 return_value = ig.DeclareLocal (ReturnType);
539                                 ReturnLabel = ig.DefineLabel ();
540                                 HasReturnLabel = true;
541                         }
542
543                         return return_value;
544                 }
545
546                 //
547                 // Creates a field `name' with the type `t' on the proxy class
548                 //
549                 public FieldBuilder MapVariable (string name, Type t)
550                 {
551                         if (InIterator){
552                                 return IteratorHandler.Current.MapVariable (name, t);
553                         }
554
555                         throw new Exception ("MapVariable for an unknown state");
556                 }
557
558                 //
559                 // Emits the proper object to address fields on a remapped
560                 // variable/parameter to field in anonymous-method/iterator proxy classes.
561                 //
562                 public void EmitThis ()
563                 {
564                         ig.Emit (OpCodes.Ldarg_0);
565
566                         if (!IsStatic){
567                                 if (InIterator)
568                                         ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
569                                 else
570                                         throw new Exception ("EmitThis for an unknown state");
571                         }
572                 }
573
574                 public void EmitArgument (int idx)
575                 {
576                         if (InIterator)
577                                 ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.parameter_fields [idx]);
578                         else
579                                 throw new Exception ("EmitStoreArgument for an unknown state");
580                 }
581                 
582                 public void EmitStoreArgument (int idx)
583                 {
584                         if (InIterator)
585                                 ig.Emit (OpCodes.Stfld, IteratorHandler.Current.parameter_fields [idx]);
586                         else
587                                 throw new Exception ("EmitStoreArgument for an unknown state");
588                 }
589                 
590                 /// <summary>
591                 ///   A dynamic This that is shared by all variables in a emitcontext.
592                 ///   Created on demand.
593                 /// </summary>
594                 public Expression my_this;
595                 public Expression This {
596                         get {
597                                 if (my_this == null) {
598                                         if (CurrentBlock != null)
599                                                 my_this = new This (CurrentBlock, loc);
600                                         else
601                                                 my_this = new This (loc);
602
603                                         my_this = my_this.Resolve (this);
604                                 }
605
606                                 return my_this;
607                         }
608                 }
609         }
610 }