2004-02-17 Carlos Guzm��n ��lvarez <carlosga@telefonica.net>
[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.Save, 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         //
123         // Provides "local" store across code that can yield: locals
124         // or fields, notice that this should not be used by anonymous
125         // methods to create local storage, those only require
126         // variable mapping.
127         //
128         public class VariableStorage {
129                 ILGenerator ig;
130                 FieldBuilder fb;
131                 LocalBuilder local;
132                 
133                 static int count;
134                 
135                 public VariableStorage (EmitContext ec, Type t)
136                 {
137                         count++;
138                         if (ec.InIterator)
139                                 fb = IteratorHandler.Current.MapVariable ("s_", count.ToString (), t);
140                         else
141                                 local = ec.ig.DeclareLocal (t);
142                         ig = ec.ig;
143                 }
144
145                 public void EmitThis ()
146                 {
147                         if (fb != null)
148                                 ig.Emit (OpCodes.Ldarg_0);
149                 }
150
151                 public void EmitStore ()
152                 {
153                         if (fb == null)
154                                 ig.Emit (OpCodes.Stloc, local);
155                         else
156                                 ig.Emit (OpCodes.Stfld, fb);
157                 }
158
159                 public void EmitLoad ()
160                 {
161                         if (fb == null)
162                                 ig.Emit (OpCodes.Ldloc, local);
163                         else 
164                                 ig.Emit (OpCodes.Ldfld, fb);
165                 }
166                 
167                 public void EmitCall (MethodInfo mi)
168                 {
169                         // FIXME : we should handle a call like tostring
170                         // here, where boxing is needed. However, we will
171                         // never encounter that with the current usage.
172                         
173                         bool value_type_call;
174                         EmitThis ();
175                         if (fb == null) {
176                                 value_type_call = local.LocalType.IsValueType;
177                                 
178                                 if (value_type_call)
179                                         ig.Emit (OpCodes.Ldloca, local);
180                                 else
181                                         ig.Emit (OpCodes.Ldloc, local);
182                         } else {
183                                 value_type_call = fb.FieldType.IsValueType;
184                                 
185                                 if (value_type_call)
186                                         ig.Emit (OpCodes.Ldflda, fb);
187                                 else
188                                         ig.Emit (OpCodes.Ldfld, fb);
189                         }
190                         
191                         ig.Emit (value_type_call ? OpCodes.Call : OpCodes.Callvirt, mi);
192                 }
193         }
194         
195         /// <summary>
196         ///   An Emit Context is created for each body of code (from methods,
197         ///   properties bodies, indexer bodies or constructor bodies)
198         /// </summary>
199         public class EmitContext {
200                 public DeclSpace DeclSpace;
201                 public DeclSpace TypeContainer;
202                 public ILGenerator   ig;
203
204                 /// <summary>
205                 ///   This variable tracks the `checked' state of the compilation,
206                 ///   it controls whether we should generate code that does overflow
207                 ///   checking, or if we generate code that ignores overflows.
208                 ///
209                 ///   The default setting comes from the command line option to generate
210                 ///   checked or unchecked code plus any source code changes using the
211                 ///   checked/unchecked statements or expressions.   Contrast this with
212                 ///   the ConstantCheckState flag.
213                 /// </summary>
214                 
215                 public bool CheckState;
216
217                 /// <summary>
218                 ///   The constant check state is always set to `true' and cant be changed
219                 ///   from the command line.  The source code can change this setting with
220                 ///   the `checked' and `unchecked' statements and expressions. 
221                 /// </summary>
222                 public bool ConstantCheckState;
223
224                 /// <summary>
225                 ///   Whether we are emitting code inside a static or instance method
226                 /// </summary>
227                 public bool IsStatic;
228
229                 /// <summary>
230                 ///   Whether we are emitting a field initializer
231                 /// </summary>
232                 public bool IsFieldInitializer;
233
234                 /// <summary>
235                 ///   The value that is allowed to be returned or NULL if there is no
236                 ///   return type.
237                 /// </summary>
238                 public Type ReturnType;
239
240                 /// <summary>
241                 ///   Points to the Type (extracted from the TypeContainer) that
242                 ///   declares this body of code
243                 /// </summary>
244                 public Type ContainerType;
245                 
246                 /// <summary>
247                 ///   Whether this is generating code for a constructor
248                 /// </summary>
249                 public bool IsConstructor;
250
251                 /// <summary>
252                 ///   Whether we're control flow analysis enabled
253                 /// </summary>
254                 public bool DoFlowAnalysis;
255                 
256                 /// <summary>
257                 ///   Keeps track of the Type to LocalBuilder temporary storage created
258                 ///   to store structures (used to compute the address of the structure
259                 ///   value on structure method invocations)
260                 /// </summary>
261                 public Hashtable temporary_storage;
262
263                 public Block CurrentBlock;
264
265                 public int CurrentFile;
266
267                 /// <summary>
268                 ///   The location where we store the return value.
269                 /// </summary>
270                 LocalBuilder return_value;
271
272                 /// <summary>
273                 ///   The location where return has to jump to return the
274                 ///   value
275                 /// </summary>
276                 public Label ReturnLabel;
277
278                 /// <summary>
279                 ///   If we already defined the ReturnLabel
280                 /// </summary>
281                 public bool HasReturnLabel;
282
283                 /// <summary>
284                 ///   Whether we are inside an iterator block.
285                 /// </summary>
286                 public bool InIterator;
287
288                 public bool IsLastStatement;
289
290                 /// <summary>
291                 ///   Whether remapping of locals, parameters and fields is turned on.
292                 ///   Used by iterators and anonymous methods.
293                 /// </summary>
294                 public bool RemapToProxy;
295
296                 /// <summary>
297                 ///  Whether we are inside an unsafe block
298                 /// </summary>
299                 public bool InUnsafe;
300
301                 /// <summary>
302                 ///  Whether we are in a `fixed' initialization
303                 /// </summary>
304                 public bool InFixedInitializer;
305
306                 /// <summary>
307                 ///  Whether we are inside an anonymous method.
308                 /// </summary>
309                 public bool InAnonymousMethod;
310                 
311                 /// <summary>
312                 ///   Location for this EmitContext
313                 /// </summary>
314                 public Location loc;
315
316                 /// <summary>
317                 ///   Used to flag that it is ok to define types recursively, as the
318                 ///   expressions are being evaluated as part of the type lookup
319                 ///   during the type resolution process
320                 /// </summary>
321                 public bool ResolvingTypeTree;
322                 
323                 /// <summary>
324                 ///   Inside an enum definition, we do not resolve enumeration values
325                 ///   to their enumerations, but rather to the underlying type/value
326                 ///   This is so EnumVal + EnumValB can be evaluated.
327                 ///
328                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
329                 ///   we relax the rules
330                 /// </summary>
331                 public bool InEnumContext;
332
333                 FlowBranching current_flow_branching;
334                 
335                 public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
336                                     Type return_type, int code_flags, bool is_constructor)
337                 {
338                         this.ig = ig;
339
340                         TypeContainer = parent;
341                         DeclSpace = ds;
342                         CheckState = RootContext.Checked;
343                         ConstantCheckState = true;
344                         
345                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
346                         InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
347                         RemapToProxy = InIterator;
348                         ReturnType = return_type;
349                         IsConstructor = is_constructor;
350                         CurrentBlock = null;
351                         CurrentFile = 0;
352                         
353                         if (parent != null){
354                                 // Can only be null for the ResolveType contexts.
355                                 ContainerType = parent.TypeBuilder;
356                                 if (parent.UnsafeContext)
357                                         InUnsafe = true;
358                                 else
359                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
360                         }
361                         loc = l;
362                         
363                         if (ReturnType == TypeManager.void_type)
364                                 ReturnType = null;
365                 }
366
367                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
368                                     Type return_type, int code_flags, bool is_constructor)
369                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
370                 {
371                 }
372
373                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
374                                     Type return_type, int code_flags)
375                         : this (tc, tc, l, ig, return_type, code_flags, false)
376                 {
377                 }
378
379                 public FlowBranching CurrentBranching {
380                         get {
381                                 return current_flow_branching;
382                         }
383                 }
384
385                 // <summary>
386                 //   Starts a new code branching.  This inherits the state of all local
387                 //   variables and parameters from the current branching.
388                 // </summary>
389                 public FlowBranching StartFlowBranching (FlowBranching.BranchingType type, Location loc)
390                 {
391                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, null, loc);
392                         return current_flow_branching;
393                 }
394
395                 // <summary>
396                 //   Starts a new code branching for block `block'.
397                 // </summary>
398                 public FlowBranching StartFlowBranching (Block block)
399                 {
400                         FlowBranching.BranchingType type;
401
402                         if (CurrentBranching.Type == FlowBranching.BranchingType.Switch)
403                                 type = FlowBranching.BranchingType.SwitchSection;
404                         else
405                                 type = FlowBranching.BranchingType.Block;
406
407                         current_flow_branching = FlowBranching.CreateBranching (CurrentBranching, type, block, block.StartLocation);
408                         return current_flow_branching;
409                 }
410
411                 // <summary>
412                 //   Ends a code branching.  Merges the state of locals and parameters
413                 //   from all the children of the ending branching.
414                 // </summary>
415                 public FlowBranching.UsageVector DoEndFlowBranching ()
416                 {
417                         FlowBranching old = current_flow_branching;
418                         current_flow_branching = current_flow_branching.Parent;
419
420                         return current_flow_branching.MergeChild (old);
421                 }
422
423                 // <summary>
424                 //   Ends a code branching.  Merges the state of locals and parameters
425                 //   from all the children of the ending branching.
426                 // </summary>
427                 public FlowBranching.Reachability EndFlowBranching ()
428                 {
429                         FlowBranching.UsageVector vector = DoEndFlowBranching ();
430
431                         return vector.Reachability;
432                 }
433
434                 // <summary>
435                 //   Kills the current code branching.  This throws away any changed state
436                 //   information and should only be used in case of an error.
437                 // </summary>
438                 public void KillFlowBranching ()
439                 {
440                         current_flow_branching = current_flow_branching.Parent;
441                 }
442
443                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
444                 {
445                         bool unreachable = false;
446
447                         if (!Location.IsNull (loc))
448                                 CurrentFile = loc.File;
449
450                         if (block != null){
451                             try {
452                                 int errors = Report.Errors;
453
454                                 block.EmitMeta (this, ip);
455
456                                 if (Report.Errors == errors){
457                                         bool old_do_flow_analysis = DoFlowAnalysis;
458                                         DoFlowAnalysis = true;
459
460                                         current_flow_branching = FlowBranching.CreateBranching (
461                                                 null, FlowBranching.BranchingType.Block, block, loc);
462
463                                         if (!block.Resolve (this)) {
464                                                 current_flow_branching = null;
465                                                 DoFlowAnalysis = old_do_flow_analysis;
466                                                 return;
467                                         }
468
469                                         FlowBranching.Reachability reachability = current_flow_branching.MergeTopBlock ();
470                                         current_flow_branching = null;
471                                         
472                                         DoFlowAnalysis = old_do_flow_analysis;
473
474                                         block.Emit (this);
475
476                                         if (reachability.AlwaysReturns ||
477                                             reachability.AlwaysThrows ||
478                                             reachability.IsUnreachable)
479                                                 unreachable = true;
480                                 }
481                             } catch (Exception e) {
482                                         Console.WriteLine ("Exception caught by the compiler while compiling:");
483                                         Console.WriteLine ("   Block that caused the problem begin at: " + loc);
484                                         
485                                         if (CurrentBlock != null){
486                                                 Console.WriteLine ("                     Block being compiled: [{0},{1}]",
487                                                                    CurrentBlock.StartLocation, CurrentBlock.EndLocation);
488                                         }
489                                         Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
490                                         Console.WriteLine (Report.FriendlyStackTrace (e));
491                                         
492                                         Environment.Exit (1);
493                             }
494                         }
495
496                         if (ReturnType != null && !unreachable){
497                                 if (!InIterator){
498                                         Report.Error (161, loc, "Not all code paths return a value");
499                                         return;
500                                 }
501                         }
502
503                         if (HasReturnLabel)
504                                 ig.MarkLabel (ReturnLabel);
505                         if (return_value != null){
506                                 ig.Emit (OpCodes.Ldloc, return_value);
507                                 ig.Emit (OpCodes.Ret);
508                         } else {
509                                 //
510                                 // If `HasReturnLabel' is set, then we already emitted a
511                                 // jump to the end of the method, so we must emit a `ret'
512                                 // there.
513                                 //
514                                 // Unfortunately, System.Reflection.Emit automatically emits
515                                 // a leave to the end of a finally block.  This is a problem
516                                 // if no code is following the try/finally block since we may
517                                 // jump to a point after the end of the method.
518                                 // As a workaround, we're always creating a return label in
519                                 // this case.
520                                 //
521
522                                 if ((block != null) && block.IsDestructor) {
523                                         // Nothing to do; S.R.E automatically emits a leave.
524                                 } else if (HasReturnLabel || (!unreachable && !InIterator)) {
525                                         if (ReturnType != null)
526                                                 ig.Emit (OpCodes.Ldloc, TemporaryReturn ());
527                                         ig.Emit (OpCodes.Ret);
528                                 }
529                         }
530                 }
531
532                 /// <summary>
533                 ///   This is called immediately before emitting an IL opcode to tell the symbol
534                 ///   writer to which source line this opcode belongs.
535                 /// </summary>
536                 public void Mark (Location loc, bool check_file)
537                 {
538                         if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
539                                 return;
540
541                         if (check_file && (CurrentFile != loc.File))
542                                 return;
543
544                         ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
545                 }
546
547                 /// <summary>
548                 ///   Returns a temporary storage for a variable of type t as 
549                 ///   a local variable in the current body.
550                 /// </summary>
551                 public LocalBuilder GetTemporaryLocal (Type t)
552                 {
553                         LocalBuilder location = null;
554                         
555                         if (temporary_storage != null){
556                                 object o = temporary_storage [t];
557                                 if (o != null){
558                                         if (o is ArrayList){
559                                                 ArrayList al = (ArrayList) o;
560                                                 
561                                                 for (int i = 0; i < al.Count; i++){
562                                                         if (al [i] != null){
563                                                                 location = (LocalBuilder) al [i];
564                                                                 al [i] = null;
565                                                                 break;
566                                                         }
567                                                 }
568                                         } else
569                                                 location = (LocalBuilder) o;
570                                         if (location != null)
571                                                 return location;
572                                 }
573                         }
574                         
575                         return ig.DeclareLocal (t);
576                 }
577
578                 public void FreeTemporaryLocal (LocalBuilder b, Type t)
579                 {
580                         if (temporary_storage == null){
581                                 temporary_storage = new Hashtable ();
582                                 temporary_storage [t] = b;
583                                 return;
584                         }
585                         object o = temporary_storage [t];
586                         if (o == null){
587                                 temporary_storage [t] = b;
588                                 return;
589                         }
590                         if (o is ArrayList){
591                                 ArrayList al = (ArrayList) o;
592                                 for (int i = 0; i < al.Count; i++){
593                                         if (al [i] == null){
594                                                 al [i] = b;
595                                                 return;
596                                         }
597                                 }
598                                 al.Add (b);
599                                 return;
600                         }
601                         ArrayList replacement = new ArrayList ();
602                         replacement.Add (o);
603                         temporary_storage.Remove (t);
604                         temporary_storage [t] = replacement;
605                 }
606
607                 /// <summary>
608                 ///   Current loop begin and end labels.
609                 /// </summary>
610                 public Label LoopBegin, LoopEnd;
611
612                 /// <summary>
613                 ///   Default target in a switch statement.   Only valid if
614                 ///   InSwitch is true
615                 /// </summary>
616                 public Label DefaultTarget;
617
618                 /// <summary>
619                 ///   If this is non-null, points to the current switch statement
620                 /// </summary>
621                 public Switch Switch;
622
623                 /// <summary>
624                 ///   ReturnValue creates on demand the LocalBuilder for the
625                 ///   return value from the function.  By default this is not
626                 ///   used.  This is only required when returns are found inside
627                 ///   Try or Catch statements.
628                 /// </summary>
629                 public LocalBuilder TemporaryReturn ()
630                 {
631                         if (return_value == null){
632                                 return_value = ig.DeclareLocal (ReturnType);
633                                 ReturnLabel = ig.DefineLabel ();
634                                 HasReturnLabel = true;
635                         }
636
637                         return return_value;
638                 }
639
640                 public void NeedReturnLabel ()
641                 {
642                         if (!HasReturnLabel) {
643                                 ReturnLabel = ig.DefineLabel ();
644                                 HasReturnLabel = true;
645                         }
646                 }
647
648                 //
649                 // Creates a field `name' with the type `t' on the proxy class
650                 //
651                 public FieldBuilder MapVariable (string name, Type t)
652                 {
653                         if (InIterator){
654                                 return IteratorHandler.Current.MapVariable ("v_", name, t);
655                         }
656
657                         throw new Exception ("MapVariable for an unknown state");
658                 }
659
660                 //
661                 // Invoke this routine to remap a VariableInfo into the
662                 // proper MemberAccess expression
663                 //
664                 public Expression RemapLocal (LocalInfo local_info)
665                 {
666                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
667                         fe.InstanceExpression = new ProxyInstance ();
668                         return fe.DoResolve (this);
669                 }
670
671                 public Expression RemapLocalLValue (LocalInfo local_info, Expression right_side)
672                 {
673                         FieldExpr fe = new FieldExpr (local_info.FieldBuilder, loc);
674                         fe.InstanceExpression = new ProxyInstance ();
675                         return fe.DoResolveLValue (this, right_side);
676                 }
677
678                 public Expression RemapParameter (int idx)
679                 {
680                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
681                         fe.InstanceExpression = new ProxyInstance ();
682                         return fe.DoResolve (this);
683                 }
684
685                 public Expression RemapParameterLValue (int idx, Expression right_side)
686                 {
687                         FieldExpr fe = new FieldExprNoAddress (IteratorHandler.Current.parameter_fields [idx], loc);
688                         fe.InstanceExpression = new ProxyInstance ();
689                         return fe.DoResolveLValue (this, right_side);
690                 }
691                 
692                 //
693                 // Emits the proper object to address fields on a remapped
694                 // variable/parameter to field in anonymous-method/iterator proxy classes.
695                 //
696                 public void EmitThis ()
697                 {
698                         ig.Emit (OpCodes.Ldarg_0);
699
700                         if (!IsStatic){
701                                 if (InIterator)
702                                         ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
703                                 else
704                                         throw new Exception ("EmitThis for an unknown state");
705                         }
706                 }
707
708                 public Expression GetThis (Location loc)
709                 {
710                         This my_this;
711                         if (CurrentBlock != null)
712                                 my_this = new This (CurrentBlock, loc);
713                         else
714                                 my_this = new This (loc);
715
716                         if (!my_this.ResolveBase (this))
717                                 my_this = null;
718
719                         return my_this;
720                 }
721         }
722 }