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