This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / mbas / 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 using System.Diagnostics.SymbolStore;
16
17 namespace Mono.MonoBASIC {
18
19         /// <summary>
20         ///    Code generator class.
21         /// </summary>
22         public class CodeGen {
23                 static AppDomain current_domain;
24                 public static AssemblyBuilder AssemblyBuilder;
25                 public static ModuleBuilder   ModuleBuilder;
26
27                 static public ISymbolWriter SymbolWriter;
28
29                 public static string Basename (string name)
30                 {
31                         int pos = name.LastIndexOf ("/");
32
33                         if (pos != -1)
34                                 return name.Substring (pos + 1);
35
36                         pos = name.LastIndexOf ("\\");
37                         if (pos != -1)
38                                 return name.Substring (pos + 1);
39
40                         return name;
41                 }
42
43                 public static string Dirname (string name)
44                 {
45                         int pos = name.LastIndexOf ("/");
46
47                         if (pos != -1)
48                                 return name.Substring (0, pos);
49
50                         pos = name.LastIndexOf ("\\");
51                         if (pos != -1)
52                                 return name.Substring (0, pos);
53
54                         return ".";
55                 }
56
57                 static string TrimExt (string name)
58                 {
59                         int pos = name.LastIndexOf (".");
60
61                         return name.Substring (0, pos);
62                 }
63
64                 static public string FileName;
65
66                 //
67                 // This routine initializes the Mono runtime SymbolWriter.
68                 //
69                 static bool InitMonoSymbolWriter (string basename, string symbol_output,
70                                                   string exe_output_file, string[] debug_args)
71                 {
72                         Type itype = SymbolWriter.GetType ();
73                         if (itype == null)
74                                 return false;
75
76                         Type[] arg_types = new Type [3];
77                         arg_types [0] = typeof (string);
78                         arg_types [1] = typeof (string);
79                         arg_types [2] = typeof (string[]);
80
81                         MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
82                         if (initialize == null)
83                                 return false;
84
85                         object[] args = new object [3];
86                         args [0] = exe_output_file;
87                         args [1] = symbol_output;
88                         args [2] = debug_args;
89
90                         initialize.Invoke (SymbolWriter, args);
91                         return true;
92                 }
93
94                 //
95                 // Initializes the symbol writer
96                 //
97                 static void InitializeSymbolWriter (string basename, string symbol_output,
98                                                     string exe_output_file, string[] args)
99                 {
100                         SymbolWriter = ModuleBuilder.GetSymWriter ();
101
102                         //
103                         // If we got an ISymbolWriter instance, initialize it.
104                         //
105                         if (SymbolWriter == null) {
106                                 Report.Warning (
107                                         -18, "Cannot find any symbol writer");
108                                 return;
109                         }
110                         
111                         //
112                         // Due to lacking documentation about the first argument of the
113                         // Initialize method, we cannot use Microsoft's default symbol
114                         // writer yet.
115                         //
116                         // If we're using the mono symbol writer, the SymbolWriter object
117                         // is of type MonoSymbolWriter - but that's defined in a dynamically
118                         // loaded DLL - that's why we're doing a comparision based on the type
119                         // name here instead of using `SymbolWriter is MonoSymbolWriter'.
120                         //
121                         Type sym_type = ((object) SymbolWriter).GetType ();
122                         
123                         switch (sym_type.Name){
124                         case "MonoSymbolWriter":
125                                 if (!InitMonoSymbolWriter (basename, symbol_output,
126                                                            exe_output_file, args))
127                                         Report.Warning (
128                                                 -18, "Cannot initialize the symbol writer");
129                                 break;
130
131                         default:
132                                 Report.Warning (
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                         int pos = output.LastIndexOf (".");
165
166                         string basename;
167                         if (pos > 0)
168                                 basename = output.Substring (0, pos);
169                         else
170                                 basename = output;
171
172                         string symbol_output = basename + ".dbg";
173
174                         if (want_debugging_support)
175                                 InitializeSymbolWriter (basename, symbol_output, output, debug_args);
176                         else {
177                                 try {
178                                         File.Delete (symbol_output);
179                                 } catch {
180                                         // Ignore errors.
181                                 }
182                         }
183                 }
184
185                 static public void Save (string name)
186                 {
187                         try {
188                                 AssemblyBuilder.Save (Basename (name));
189                         } catch (System.IO.IOException io){
190                                 Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
191                         }
192                 }
193
194                 static public void SaveSymbols ()
195                 {
196                         if (SymbolWriter != null) {
197                                 // If we have a symbol writer, call its Close() method to write
198                                 // the symbol file to disk.
199                                 //
200                                 // When using Mono's default symbol writer, the Close() method must
201                                 // be called after the assembly has already been written to disk since
202                                 // it opens the assembly and reads its metadata.
203                                 SymbolWriter.Close ();
204                         }
205                 }
206         }
207
208         /// <summary>
209         ///   An Emit Context is created for each body of code (from methods,
210         ///   properties bodies, indexer bodies or constructor bodies)
211         /// </summary>
212         public class EmitContext {
213                 public DeclSpace DeclSpace;
214                 public TypeContainer TypeContainer;
215                 public ILGenerator   ig;
216
217                 /// <summary>
218                 ///   This variable tracks the `checked' state of the compilation,
219                 ///   it controls whether we should generate code that does overflow
220                 ///   checking, or if we generate code that ignores overflows.
221                 ///
222                 ///   The default setting comes from the command line option to generate
223                 ///   checked or unchecked code plus any source code changes using the
224                 ///   checked/unchecked statements or expressions.   Contrast this with
225                 ///   the ConstantCheckState flag.
226                 /// </summary>
227                 
228                 public bool CheckState;
229
230                 /// <summary>
231                 ///   The constant check state is always set to `true' and cant be changed
232                 ///   from the command line.  The source code can change this setting with
233                 ///   the `checked' and `unchecked' statements and expressions. 
234                 /// </summary>
235                 public bool ConstantCheckState;
236
237                 /// <summary>
238                 ///   Whether we are emitting code inside a static or instance method
239                 /// </summary>
240                 public bool IsStatic;
241
242                 /// <summary>
243                 ///   Whether we are emitting a field initializer
244                 /// </summary>
245                 public bool IsFieldInitializer;
246
247                 /// <summary>
248                 ///   The value that is allowed to be returned or NULL if there is no
249                 ///   return type.
250                 /// </summary>
251                 public Type ReturnType;
252
253                 /// <summary>
254                 ///   Points to the Type (extracted from the TypeContainer) that
255                 ///   declares this body of code
256                 /// </summary>
257                 public Type ContainerType;
258                 
259                 /// <summary>
260                 ///   Whether this is generating code for a constructor
261                 /// </summary>
262                 public bool IsConstructor;
263
264                 /// <summary>
265                 ///   Whether we're control flow analysis enabled
266                 /// </summary>
267                 public bool DoFlowAnalysis;
268                 
269                 /// <summary>
270                 ///   Keeps track of the Type to LocalBuilder temporary storage created
271                 ///   to store structures (used to compute the address of the structure
272                 ///   value on structure method invocations)
273                 /// </summary>
274                 public Hashtable temporary_storage;
275
276                 public Block CurrentBlock;
277
278                 /// <summary>
279                 ///   The location where we store the return value.
280                 /// </summary>
281                 LocalBuilder return_value;
282
283                 /// <summary>
284                 ///   The location where return has to jump to return the
285                 ///   value
286                 /// </summary>
287                 public Label ReturnLabel;
288
289                 /// <summary>
290                 ///   If we already defined the ReturnLabel
291                 /// </summary>
292                 public bool HasReturnLabel;
293
294                 /// <summary>
295                 ///   Whether we are in a Finally block
296                 /// </summary>
297                 public bool InFinally;
298
299                 /// <summary>
300                 ///   Whether we are in a Try block
301                 /// </summary>
302                 public bool InTry;
303
304                 /// <summary>
305                 ///   Whether we are in a Catch block
306                 /// </summary>
307                 public bool InCatch;
308
309                 /// <summary>
310                 ///  Whether we are inside an unsafe block
311                 /// </summary>
312                 public bool InUnsafe;
313                 
314                 /// <summary>
315                 ///  Whether we are inside an unsafe block
316                 /// </summary>
317                 public bool InvokingOwnOverload;                
318
319                 /// <summary>
320                 ///   Location for this EmitContext
321                 /// </summary>
322                 public Location loc;
323
324                 /// <summary>
325                 ///   Used to flag that it is ok to define types recursively, as the
326                 ///   expressions are being evaluated as part of the type lookup
327                 ///   during the type resolution process
328                 /// </summary>
329                 public bool ResolvingTypeTree;
330                 
331                 /// <summary>
332                 ///   Inside an enum definition, we do not resolve enumeration values
333                 ///   to their enumerations, but rather to the underlying type/value
334                 ///   This is so EnumVal + EnumValB can be evaluated.
335                 ///
336                 ///   There is no "E operator + (E x, E y)", so during an enum evaluation
337                 ///   we relax the rules
338                 /// </summary>
339                 public bool InEnumContext;
340                 
341                 public string BlockName;
342
343                 protected Stack FlowStack;
344                 
345                 public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
346                                     Type return_type, int code_flags, bool is_constructor)
347                 {
348                         this.ig = ig;
349
350                         TypeContainer = parent;
351                         DeclSpace = ds;
352                         CheckState = RootContext.Checked;
353                         ConstantCheckState = true;
354                         
355                         IsStatic = (code_flags & Modifiers.STATIC) != 0;
356                         ReturnType = return_type;
357                         IsConstructor = is_constructor;
358                         CurrentBlock = null;
359                         BlockName = "";
360                         InvokingOwnOverload = false;
361                         
362                         if (parent != null){
363                                 // Can only be null for the ResolveType contexts.
364                                 ContainerType = parent.TypeBuilder;
365                                 if (parent.UnsafeContext)
366                                         InUnsafe = true;
367                                 else
368                                         InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
369                         }
370                         loc = l;
371
372                         FlowStack = new Stack ();
373                         
374                         if (ReturnType == TypeManager.void_type)
375                                 ReturnType = null;
376                 }
377
378                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
379                                     Type return_type, int code_flags, bool is_constructor)
380                         : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
381                 {
382                 }
383
384                 public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
385                                     Type return_type, int code_flags)
386                         : this (tc, tc, l, ig, return_type, code_flags, false)
387                 {
388                 }
389
390                 public FlowBranching CurrentBranching {
391                         get {
392                                 return (FlowBranching) FlowStack.Peek ();
393                         }
394                 }
395
396                 // <summary>
397                 //   Starts a new code branching.  This inherits the state of all local
398                 //   variables and parameters from the current branching.
399                 // </summary>
400                 public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
401                 {
402                         FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
403
404                         FlowStack.Push (cfb);
405
406                         return cfb;
407                 }
408
409                 // <summary>
410                 //   Starts a new code branching for block `block'.
411                 // </summary>
412                 public FlowBranching StartFlowBranching (Block block)
413                 {
414                         FlowBranching cfb;
415                         FlowBranchingType type;
416
417                         if (CurrentBranching.Type == FlowBranchingType.SWITCH)
418                                 type = FlowBranchingType.SWITCH_SECTION;
419                         else
420                                 type = FlowBranchingType.BLOCK;
421
422                         cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
423
424                         FlowStack.Push (cfb);
425
426                         return cfb;
427                 }
428
429                 // <summary>
430                 //   Ends a code branching.  Merges the state of locals and parameters
431                 //   from all the children of the ending branching.
432                 // </summary>
433                 public FlowReturns EndFlowBranching ()
434                 {
435                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
436
437                         return CurrentBranching.MergeChild (cfb);
438                 }
439
440                 // <summary>
441                 //   Kills the current code branching.  This throws away any changed state
442                 //   information and should only be used in case of an error.
443                 // </summary>
444                 public void KillFlowBranching ()
445                 {
446                         FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
447                 }
448
449                 // <summary>
450                 //   Checks whether the local variable `vi' is already initialized
451                 //   at the current point of the method's control flow.
452                 //   If this method returns false, the caller must report an
453                 //   error 165.
454                 // </summary>
455                 public bool IsVariableAssigned (VariableInfo vi)
456                 {
457                         if (DoFlowAnalysis)
458                                 return CurrentBranching.IsVariableAssigned (vi);
459                         else
460                                 return true;
461                 }
462
463                 // <summary>
464                 //   Marks the local variable `vi' as being initialized at the current
465                 //   current point of the method's control flow.
466                 // </summary>
467                 public void SetVariableAssigned (VariableInfo vi)
468                 {
469                         if (DoFlowAnalysis)
470                                 CurrentBranching.SetVariableAssigned (vi);
471                 }
472
473                 // <summary>
474                 //   Checks whether the parameter `number' is already initialized
475                 //   at the current point of the method's control flow.
476                 //   If this method returns false, the caller must report an
477                 //   error 165.  This is only necessary for `out' parameters and the
478                 //   call will always succeed for non-`out' parameters.
479                 // </summary>
480                 public bool IsParameterAssigned (int number)
481                 {
482                         if (DoFlowAnalysis)
483                                 return CurrentBranching.IsParameterAssigned (number);
484                         else
485                                 return true;
486                 }
487
488                 // <summary>
489                 //   Marks the parameter `number' as being initialized at the current
490                 //   current point of the method's control flow.  This is only necessary
491                 //   for `out' parameters.
492                 // </summary>
493                 public void SetParameterAssigned (int number)
494                 {
495                         if (DoFlowAnalysis)
496                                 CurrentBranching.SetParameterAssigned (number);
497                 }
498
499                 // These are two overloaded methods for EmitTopBlock
500                 // since in MonoBasic functions we need the Function name
501                 // along with its top block, in order to be able to
502                 // retrieve the return value when there is no explicit 
503                 // 'Return' statement
504                 public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
505                 {
506                         EmitTopBlock (block, "", ip, loc);
507                 }
508
509                 public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc)
510                 {
511                         bool has_ret = false;
512
513                         //Console.WriteLine ("Emitting: '{0}", bname);
514                         BlockName = bname;
515                         if (CodeGen.SymbolWriter != null)
516                                 Mark (loc);
517
518                         if (block != null){
519                                 int errors = Report.Errors;
520
521                                 block.EmitMeta (this, block);
522
523                                 if (Report.Errors == errors){
524                                         bool old_do_flow_analysis = DoFlowAnalysis;
525                                         DoFlowAnalysis = true;
526
527                                         FlowBranching cfb = new FlowBranching (block, ip, loc);
528                                         FlowStack.Push (cfb);
529
530                                         if (!block.Resolve (this)) {
531                                                 FlowStack.Pop ();
532                                                 DoFlowAnalysis = old_do_flow_analysis;
533                                                 return;
534                                         }
535
536                                         cfb = (FlowBranching) FlowStack.Pop ();
537                                         FlowReturns returns = cfb.MergeTopBlock ();
538
539                                         DoFlowAnalysis = old_do_flow_analysis;
540
541                                         has_ret = block.Emit (this);
542
543                                         if ((returns == FlowReturns.ALWAYS) ||
544                                             (returns == FlowReturns.EXCEPTION) ||
545                                             (returns == FlowReturns.UNREACHABLE))
546                                                 has_ret = true;
547
548                                         if (Report.Errors == errors){
549                                                 if (RootContext.WarningLevel >= 3)
550                                                         block.UsageWarning ();
551                                         }
552                                 }
553                         }
554
555                         if (ReturnType != null && !has_ret){
556                                 //
557                                 // mcs here would report an error (and justly so), but functions without
558                                 // an explicit return value are perfectly legal in MonoBasic
559                                 //
560                                 
561                                 VariableInfo vi = block.GetVariableInfo (bname);
562                                 if (vi != null) 
563                                 {
564                                         ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
565                                         ig.Emit (OpCodes.Ret);
566                                 }
567                                 else
568                                         Report.Error (-200, "This is not supposed to happen !");
569                                 return;
570                         }
571
572                         if (HasReturnLabel)
573                                 ig.MarkLabel (ReturnLabel);
574                         if (return_value != null){
575                                 ig.Emit (OpCodes.Ldloc, return_value);
576                                 ig.Emit (OpCodes.Ret);
577                         } else {
578                                 if (!InTry){
579                                         if (!has_ret || HasReturnLabel)
580                                                 ig.Emit (OpCodes.Ret);
581                                 }
582                         }
583                 }
584
585                 /// <summary>
586                 ///   This is called immediately before emitting an IL opcode to tell the symbol
587                 ///   writer to which source line this opcode belongs.
588                 /// </summary>
589                 public void Mark (Location loc)
590                 {
591                         if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
592                                 ISymbolDocumentWriter doc = loc.SymbolDocument;
593
594                                 if (doc != null)
595                                         ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
596                         }
597                 }
598
599                 /// <summary>
600                 ///   Returns a temporary storage for a variable of type t as 
601                 ///   a local variable in the current body.
602                 /// </summary>
603                 public LocalBuilder GetTemporaryStorage (Type t)
604                 {
605                         LocalBuilder location;
606                         
607                         if (temporary_storage != null){
608                                 location = (LocalBuilder) temporary_storage [t];
609                                 if (location != null)
610                                         return location;
611                         }
612                         
613                         location = ig.DeclareLocal (t);
614                         
615                         return location;
616                 }
617
618                 public void FreeTemporaryStorage (LocalBuilder b)
619                 {
620                         // Empty for now.
621                 }
622
623                 /// <summary>
624                 ///   Current loop begin and end labels.
625                 /// </summary>
626                 public Label LoopBegin, LoopEnd;
627
628                 /// <summary>
629                 ///   Whether we are inside a loop and break/continue are possible.
630                 /// </summary>
631                 public bool  InLoop;
632
633                 /// <summary>
634                 ///   This is incremented each time we enter a try/catch block and
635                 ///   decremented if we leave it.
636                 /// </summary>
637                 public int   TryCatchLevel;
638
639                 /// <summary>
640                 ///   The TryCatchLevel at the begin of the current loop.
641                 /// </summary>
642                 public int   LoopBeginTryCatchLevel;
643
644                 /// <summary>
645                 ///   Default target in a switch statement.   Only valid if
646                 ///   InSwitch is true
647                 /// </summary>
648                 public Label DefaultTarget;
649
650                 /// <summary>
651                 ///   If this is non-null, points to the current switch statement
652                 /// </summary>
653                 public Switch Switch;
654
655                 /// <summary>
656                 ///   ReturnValue creates on demand the LocalBuilder for the
657                 ///   return value from the function.  By default this is not
658                 ///   used.  This is only required when returns are found inside
659                 ///   Try or Catch statements.
660                 /// </summary>
661                 public LocalBuilder TemporaryReturn ()
662                 {
663                         if (return_value == null){
664                                 return_value = ig.DeclareLocal (ReturnType);
665                                 ReturnLabel = ig.DefineLabel ();
666                                 HasReturnLabel = true;
667                         }
668
669                         return return_value;
670                 }
671
672                 /// <summary>
673                 ///   A dynamic This that is shared by all variables in a emitcontext.
674                 ///   Created on demand.
675                 /// </summary>
676                 public Expression my_this;
677                 public Expression This {
678                         get {
679                                 if (my_this == null) {
680                                         if (CurrentBlock != null)
681                                                 my_this = new This (CurrentBlock, loc);
682                                         else
683                                                 my_this = new This (loc);
684
685                                         my_this = my_this.Resolve (this);
686                                 }
687
688                                 return my_this;
689                         }
690                 }
691         }
692 }