Oops, default to public if no attribute is given.
[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.Collections;
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Diagnostics.SymbolStore;
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 ISymbolWriter 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                 // This routine initializes the Mono runtime SymbolWriter.
67                 //
68                 static bool InitMonoSymbolWriter (string basename, string output_file,
69                                                   string[] debug_args)
70                 {
71                         string symbol_output = basename + ".dbg";
72
73                         Type itype = SymbolWriter.GetType ();
74                         if (itype == null)
75                                 return false;
76
77                         Type[] arg_types = new Type [3];
78                         arg_types [0] = typeof (string);
79                         arg_types [1] = typeof (string);
80                         arg_types [2] = typeof (string[]);
81
82                         MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
83                         if (initialize == null)
84                                 return false;
85
86                         object[] args = new object [3];
87                         args [0] = output_file;
88                         args [1] = symbol_output;
89                         args [2] = debug_args;
90
91                         initialize.Invoke (SymbolWriter, args);
92                         return true;
93                 }
94
95                 //
96                 // Initializes the symbol writer
97                 //
98                 static void InitializeSymbolWriter (string basename, string output_file,
99                                                     string[] args)
100                 {
101                         SymbolWriter = ModuleBuilder.GetSymWriter ();
102
103                         //
104                         // If we got an ISymbolWriter instance, initialize it.
105                         //
106                         if (SymbolWriter == null) {
107                                 Report.Error (
108                                         -18, "Cannot find any symbol writer");
109                                 return;
110                         }
111                         
112                         //
113                         // Due to lacking documentation about the first argument of the
114                         // Initialize method, we cannot use Microsoft's default symbol
115                         // writer yet.
116                         //
117                         // If we're using the mono symbol writer, the SymbolWriter object
118                         // is of type MonoSymbolWriter - but that's defined in a dynamically
119                         // loaded DLL - that's why we're doing a comparision based on the type
120                         // name here instead of using `SymbolWriter is MonoSymbolWriter'.
121                         //
122                         Type sym_type = ((object) SymbolWriter).GetType ();
123                         
124                         switch (sym_type.Name){
125                         case "MonoSymbolWriter":
126                                 if (!InitMonoSymbolWriter (basename, output_file, args))
127                                         Report.Error (
128                                                 -18, "Cannot initialize the symbol writer");
129                                 break;
130
131                         default:
132                                 Report.Error (
133                                         -18, "Cannot generate debugging information on this platform");
134                                 break;
135                         }
136                 }
137
138                 //
139                 // Initializes the code generator variables
140                 //
141                 static public void Init (string name, string output, bool want_debugging_support,
142                                          string[] debug_args)
143                 {
144                         AssemblyName an;
145
146                         FileName = output;
147                         an = new AssemblyName ();
148                         an.Name = TrimExt (Basename (name));
149                         current_domain = AppDomain.CurrentDomain;
150                         AssemblyBuilder = current_domain.DefineDynamicAssembly (
151                                 an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
152
153                         //
154                         // Pass a path-less name to DefineDynamicModule.  Wonder how
155                         // this copes with output in different directories then.
156                         // FIXME: figure out how this copes with --output /tmp/blah
157                         //
158                         // If the third argument is true, the ModuleBuilder will dynamically
159                         // load the default symbol writer.
160                         //
161                         ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
162                                 Basename (name), Basename (output), want_debugging_support);
163
164                         if (want_debugging_support) {
165                                 int pos = output.LastIndexOf (".");
166
167                                 string basename;
168                                 if (pos > 0)
169                                         basename = output.Substring (0, pos);
170                                 else
171                                         basename = output;
172
173                                 InitializeSymbolWriter (basename, output, debug_args);
174                         }
175                 }
176
177                 static public void Save (string name)
178                 {
179                         try {
180                                 AssemblyBuilder.Save (Basename (name));
181                         } catch (System.IO.IOException io){
182                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
183                         }
184                 }
185
186                 static public void SaveSymbols ()
187                 {
188                         if (SymbolWriter != null) {
189                                 // If we have a symbol writer, call its Close() method to write
190                                 // the symbol file to disk.
191                                 //
192                                 // When using Mono's default symbol writer, the Close() method must
193                                 // be called after the assembly has already been written to disk since
194                                 // it opens the assembly and reads its metadata.
195                                 SymbolWriter.Close ();
196                         }
197                 }
198         }
199
200         /// <summary>
201         ///   An Emit Context is created for each body of code (from methods,
202         ///   properties bodies, indexer bodies or constructor bodies)
203         /// </summary>
204         public class EmitContext {
205                 public DeclSpace DeclSpace;
206                 public TypeContainer TypeContainer;
207                 public ILGenerator   ig;
208
209                 /// <summary>
210                 ///   This variable tracks the `checked' state of the compilation,
211                 ///   it controls whether we should generate code that does overflow
212                 ///   checking, or if we generate code that ignores overflows.
213                 ///
214                 ///   The default setting comes from the command line option to generate
215                 ///   checked or unchecked code plus any source code changes using the
216                 ///   checked/unchecked statements or expressions.   Contrast this with
217                 ///   the ConstantCheckState flag.
218                 /// </summary>
219                 
220                 public bool CheckState;
221
222                 /// <summary>
223                 ///   The constant check state is always set to `true' and cant be changed
224                 ///   from the command line.  The source code can change this setting with
225                 ///   the `checked' and `unchecked' statements and expressions. 
226                 /// </summary>
227                 public bool ConstantCheckState;
228
229                 /// <summary>
230                 ///   Whether we are emitting code inside a static or instance method
231                 /// </summary>
232                 public bool IsStatic;
233
234                 /// <summary>
235                 ///   Whether we are emitting a field initializer
236                 /// </summary>
237                 public bool IsFieldInitializer;
238
239                 /// <summary>
240                 ///   The value that is allowed to be returned or NULL if there is no
241                 ///   return type.
242                 /// </summary>
243                 public Type ReturnType;
244
245                 /// <summary>
246                 ///   Points to the Type (extracted from the TypeContainer) that
247                 ///   declares this body of code
248                 /// </summary>
249                 public Type ContainerType;
250                 
251                 /// <summary>
252                 ///   Whether this is generating code for a constructor
253                 /// </summary>
254                 public bool IsConstructor;
255
256                 /// <summary>
257                 ///   Whether we're control flow analysis enabled
258                 /// </summary>
259                 public bool DoFlowAnalysis;
260                 
261                 /// <summary>
262                 ///   Keeps track of the Type to LocalBuilder temporary storage created
263                 ///   to store structures (used to compute the address of the structure
264                 ///   value on structure method invocations)
265                 /// </summary>
266                 public Hashtable temporary_storage;
267
268                 public Block CurrentBlock;
269
270                 /// <summary>
271                 ///   The location where we store the return value.
272                 /// </summary>
273                 LocalBuilder return_value;
274
275                 /// <summary>
276                 ///   The location where return has to jump to return the
277                 ///   value
278                 /// </summary>
279                 public Label ReturnLabel;
280
281                 /// <summary>
282                 ///   If we already defined the ReturnLabel
283                 /// </summary>
284                 public bool HasReturnLabel;
285
286                 /// <summary>
287                 ///   Whether we are in a Finally block
288                 /// </summary>
289                 public bool InFinally;
290
291                 /// <summary>
292                 ///   Whether we are in a Try block
293                 /// </summary>
294                 public bool InTry;
295
296                 /// <summary>
297                 ///   Whether we are in a Catch block
298                 /// </summary>
299                 public bool InCatch;
300
301                 /// <summary>
302                 ///  Whether we are inside an unsafe block
303                 /// </summary>
304                 public bool InUnsafe;
305
306                 /// <summary>
307                 ///   Whether we break from a loop or not
308                 /// </summary>
309                 public bool Breaks;
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                 protected Stack FlowStack;
334                 
335                 public EmitContext (TypeContainer 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                         ReturnType = return_type;
347                         IsConstructor = is_constructor;
348                         CurrentBlock = null;
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                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
367                                     Type return_type, int code_flags, bool is_constructor)
368                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
369                 {
370                 }
371
372                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
373                                     Type return_type, int code_flags)
374                         : this (tc, tc, l, ig, return_type, code_flags, false)
375                 {
376                 }
377
378                 public FlowBranching CurrentBranching {
379                         get {
380                                 return (FlowBranching) FlowStack.Peek ();
381                         }
382                 }
383
384                 // <summary>
385                 //   Starts a new code branching.  This inherits the state of all local
386                 //   variables and parameters from the current branching.
387                 // </summary>
388                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
389                 {
390                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
391
392                         FlowStack.Push (cfb);
393
394                         return cfb;
395                 }
396
397                 // <summary>
398                 //   Starts a new code branching for block `block'.
399                 // </summary>
400                 public FlowBranching StartFlowBranching (Block block)
401                 {
402                         FlowBranching cfb;
403                         FlowBranchingType type;
404
405                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
406                                 type = FlowBranchingType.SWITCH_SECTION;
407                         else
408                                 type = FlowBranchingType.BLOCK;
409
410                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
411
412                         FlowStack.Push (cfb);
413
414                         return cfb;
415                 }
416
417                 // <summary>
418                 //   Ends a code branching.  Merges the state of locals and parameters
419                 //   from all the children of the ending branching.
420                 // </summary>
421                 public FlowReturns EndFlowBranching ()
422                 {
423                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
424
425                         return CurrentBranching.MergeChild (cfb);
426                 }
427
428                 // <summary>
429                 //   Kills the current code branching.  This throws away any changed state
430                 //   information and should only be used in case of an error.
431                 // </summary>
432                 public void KillFlowBranching ()
433                 {
434                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
435                 }
436
437                 // <summary>
438                 //   Checks whether the local variable `vi' is already initialized
439                 //   at the current point of the method's control flow.
440                 //   If this method returns false, the caller must report an
441                 //   error 165.
442                 // </summary>
443                 public bool IsVariableAssigned (VariableInfo vi)
444                 {
445                         if (DoFlowAnalysis)
446                                 return CurrentBranching.IsVariableAssigned (vi);
447                         else
448                                 return true;
449                 }
450
451                 // <summary>
452                 //   Marks the local variable `vi' as being initialized at the current
453                 //   current point of the method's control flow.
454                 // </summary>
455                 public void SetVariableAssigned (VariableInfo vi)
456                 {
457                         if (DoFlowAnalysis)
458                                 CurrentBranching.SetVariableAssigned (vi);
459                 }
460
461                 // <summary>
462                 //   Checks whether the parameter `number' is already initialized
463                 //   at the current point of the method's control flow.
464                 //   If this method returns false, the caller must report an
465                 //   error 165.  This is only necessary for `out' parameters and the
466                 //   call will always succeed for non-`out' parameters.
467                 // </summary>
468                 public bool IsParameterAssigned (int number)
469                 {
470                         if (DoFlowAnalysis)
471                                 return CurrentBranching.IsParameterAssigned (number);
472                         else
473                                 return true;
474                 }
475
476                 // <summary>
477                 //   Marks the parameter `number' as being initialized at the current
478                 //   current point of the method's control flow.  This is only necessary
479                 //   for `out' parameters.
480                 // </summary>
481                 public void SetParameterAssigned (int number)
482                 {
483                         if (DoFlowAnalysis)
484                                 CurrentBranching.SetParameterAssigned (number);
485                 }
486
487                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
488                 {
489                         bool has_ret = false;
490
491 //                      Console.WriteLine ("Emitting: " + loc);
492
493                         if (CodeGen.SymbolWriter != null)
494                                 Mark (loc);
495
496                         if (block != null){
497                                 int errors = Report.Errors;
498
499                                 block.EmitMeta (this, block);
500
501                                 if (Report.Errors == errors){
502                                         bool old_do_flow_analysis = DoFlowAnalysis;
503                                         DoFlowAnalysis = true;
504
505                                         FlowBranching cfb = new FlowBranching (block, ip, loc);
506                                         FlowStack.Push (cfb);
507
508                                         if (!block.Resolve (this)) {
509                                                 FlowStack.Pop ();
510                                                 DoFlowAnalysis = old_do_flow_analysis;
511                                                 return;
512                                         }
513
514                                         cfb = (FlowBranching) FlowStack.Pop ();
515                                         cfb.MergeTopBlock ();
516
517                                         DoFlowAnalysis = old_do_flow_analysis;
518
519                                         has_ret = block.Emit (this);
520
521                                         if (Report.Errors == errors){
522                                                 if (RootContext.WarningLevel >= 3)
523                                                         block.UsageWarning ();
524                                         }
525                                 }
526                         }
527
528                         if (ReturnType != null && !has_ret){
529                                 //
530                                 // FIXME: we need full flow analysis to implement this
531                                 // correctly and emit an error instead of a warning.
532                                 //
533                                 //
534                                 Report.Error (161, loc, "Not all code paths return a value");
535                                 return;
536                         }
537
538                         if (HasReturnLabel)
539                                 ig.MarkLabel (ReturnLabel);
540                         if (return_value != null){
541                                 ig.Emit (OpCodes.Ldloc, return_value);
542                                 ig.Emit (OpCodes.Ret);
543                         } else {
544                                 if (!InTry){
545                                         if (!has_ret || HasReturnLabel)
546                                                 ig.Emit (OpCodes.Ret);
547                                 }
548                         }
549                 }
550
551                 /// <summary>
552                 ///   This is called immediately before emitting an IL opcode to tell the symbol
553                 ///   writer to which source line this opcode belongs.
554                 /// </summary>
555                 public void Mark (Location loc)
556                 {
557                         if (!Location.IsNull (loc)) {
558                                 ISymbolDocumentWriter doc = loc.SymbolDocument;
559
560                                 if (doc != null)
561                                         ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
562                         }               }
563
564
565                 /// <summary>
566                 ///   Returns a temporary storage for a variable of type t as 
567                 ///   a local variable in the current body.
568                 /// </summary>
569                 public LocalBuilder GetTemporaryStorage (Type t)
570                 {
571                         LocalBuilder location;
572                         
573                         if (temporary_storage != null){
574                                 location = (LocalBuilder) temporary_storage [t];
575                                 if (location != null)
576                                         return location;
577                         }
578                         
579                         location = ig.DeclareLocal (t);
580                         
581                         return location;
582                 }
583
584                 public void FreeTemporaryStorage (LocalBuilder b)
585                 {
586                         // Empty for now.
587                 }
588
589                 /// <summary>
590                 ///   Current loop begin and end labels.
591                 /// </summary>
592                 public Label LoopBegin, LoopEnd;
593
594                 /// <summary>
595                 ///   Whether we are inside a loop and break/continue are possible.
596                 /// </summary>
597                 public bool  InLoop;
598
599                 /// <summary>
600                 ///   This is incremented each time we enter a try/catch block and
601                 ///   decremented if we leave it.
602                 /// </summary>
603                 public int   TryCatchLevel;
604
605                 /// <summary>
606                 ///   The TryCatchLevel at the begin of the current loop.
607                 /// </summary>
608                 public int   LoopBeginTryCatchLevel;
609
610                 /// <summary>
611                 ///   Default target in a switch statement.   Only valid if
612                 ///   InSwitch is true
613                 /// </summary>
614                 public Label DefaultTarget;
615
616                 /// <summary>
617                 ///   If this is non-null, points to the current switch statement
618                 /// </summary>
619                 public Switch Switch;
620
621                 /// <summary>
622                 ///   ReturnValue creates on demand the LocalBuilder for the
623                 ///   return value from the function.  By default this is not
624                 ///   used.  This is only required when returns are found inside
625                 ///   Try or Catch statements.
626                 /// </summary>
627                 public LocalBuilder TemporaryReturn ()
628                 {
629                         if (return_value == null){
630                                 return_value = ig.DeclareLocal (ReturnType);
631                                 ReturnLabel = ig.DefineLabel ();
632                                 HasReturnLabel = true;
633                         }
634
635                         return return_value;
636                 }
637
638                 /// <summary>
639                 ///   A dynamic This that is shared by all variables in a emitcontext.
640                 ///   Created on demand.
641                 /// </summary>
642                 public Expression my_this;
643                 public Expression This {
644                         get {
645                                 if (my_this == null) {
646                                         if (CurrentBlock != null)
647                                                 my_this = new This (CurrentBlock, loc);
648                                         else
649                                                 my_this = new This (loc);
650
651                                         my_this = my_this.Resolve (this);
652                                 }
653
654                                 return my_this;
655                         }
656                 }
657         }
658 }