2003-04-28 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / codegen.cs
index fb51eb954cd1d31ca3f9d163a6736e69a6d6506b..b10dc6be02c81b9267e52687c8eb5461763bdb49 100755 (executable)
@@ -8,6 +8,7 @@
 //
 
 using System;
+using System.IO;
 using System.Collections;
 using System.Reflection;
 using System.Reflection.Emit;
@@ -18,11 +19,13 @@ namespace Mono.CSharp {
        ///    Code generator class.
        /// </summary>
        public class CodeGen {
-               AppDomain current_domain;
-               AssemblyBuilder assembly_builder;
-               ModuleBuilder   module_builder;
+               static AppDomain current_domain;
+               public static AssemblyBuilder AssemblyBuilder;
+               public static ModuleBuilder   ModuleBuilder;
 
-               string Basename (string name)
+               static public SymbolWriter SymbolWriter;
+
+               public static string Basename (string name)
                {
                        int pos = name.LastIndexOf ("/");
 
@@ -36,55 +39,83 @@ namespace Mono.CSharp {
                        return name;
                }
 
-               string TrimExt (string name)
+               public static string Dirname (string name)
+               {
+                       int pos = name.LastIndexOf ("/");
+
+                       if (pos != -1)
+                               return name.Substring (0, pos);
+
+                       pos = name.LastIndexOf ("\\");
+                       if (pos != -1)
+                               return name.Substring (0, pos);
+
+                       return ".";
+               }
+
+               static string TrimExt (string name)
                {
                        int pos = name.LastIndexOf (".");
 
                        return name.Substring (0, pos);
                }
 
-               public string FileName;
-               
-               public CodeGen (string name, string output)
+               static public string FileName;
+
+               //
+               // Initializes the symbol writer
+               //
+               static void InitializeSymbolWriter ()
+               {
+                       SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
+
+                       //
+                       // If we got an ISymbolWriter instance, initialize it.
+                       //
+                       if (SymbolWriter == null) {
+                               Report.Warning (
+                                       -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.");
+                               return;
+                       }
+               }
+
+               //
+               // Initializes the code generator variables
+               //
+               static public void Init (string name, string output, bool want_debugging_support)
                {
                        AssemblyName an;
 
                        FileName = output;
                        an = new AssemblyName ();
-                       an.Name = TrimExt (Basename (name));
+                       an.Name = Path.GetFileNameWithoutExtension (name);
+                       
                        current_domain = AppDomain.CurrentDomain;
-                       assembly_builder = current_domain.DefineDynamicAssembly (
-                               an, AssemblyBuilderAccess.RunAndSave);
+                       AssemblyBuilder = current_domain.DefineDynamicAssembly (
+                               an, AssemblyBuilderAccess.RunAndSave, Dirname (name));
 
                        //
                        // Pass a path-less name to DefineDynamicModule.  Wonder how
                        // this copes with output in different directories then.
                        // FIXME: figure out how this copes with --output /tmp/blah
                        //
-                       module_builder = assembly_builder.DefineDynamicModule (
-                               Basename (name), Basename (output));
+                       // If the third argument is true, the ModuleBuilder will dynamically
+                       // load the default symbol writer.
+                       //
+                       ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
+                               Basename (name), Basename (output), want_debugging_support);
 
+                       if (want_debugging_support)
+                               InitializeSymbolWriter ();
                }
-               
-               public AssemblyBuilder AssemblyBuilder {
-                       get {
-                               return assembly_builder;
-                       }
-               }
-               
-               public ModuleBuilder ModuleBuilder {
-                       get {
-                               return module_builder;
-                       }
-               }
-               
-               public void Save (string name)
+
+               static public void Save (string name)
                {
                        try {
-                               assembly_builder.Save (Basename (name));
+                               AssemblyBuilder.Save (Basename (name));
                        } catch (System.IO.IOException io){
-                               Report.Error (16, "Coult not write to file `"+name+"', cause: " + io.Message);
-                       } 
+                               Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
+                       }
                }
        }
 
@@ -93,25 +124,61 @@ namespace Mono.CSharp {
        ///   properties bodies, indexer bodies or constructor bodies)
        /// </summary>
        public class EmitContext {
-               public TypeContainer TypeContainer;
+               public DeclSpace DeclSpace;
+               public DeclSpace TypeContainer;
                public ILGenerator   ig;
+
+               /// <summary>
+               ///   This variable tracks the `checked' state of the compilation,
+               ///   it controls whether we should generate code that does overflow
+               ///   checking, or if we generate code that ignores overflows.
+               ///
+               ///   The default setting comes from the command line option to generate
+               ///   checked or unchecked code plus any source code changes using the
+               ///   checked/unchecked statements or expressions.   Contrast this with
+               ///   the ConstantCheckState flag.
+               /// </summary>
+               
                public bool CheckState;
 
+               /// <summary>
+               ///   The constant check state is always set to `true' and cant be changed
+               ///   from the command line.  The source code can change this setting with
+               ///   the `checked' and `unchecked' statements and expressions. 
+               /// </summary>
+               public bool ConstantCheckState;
+
                /// <summary>
                ///   Whether we are emitting code inside a static or instance method
                /// </summary>
                public bool IsStatic;
 
+               /// <summary>
+               ///   Whether we are emitting a field initializer
+               /// </summary>
+               public bool IsFieldInitializer;
+
                /// <summary>
                ///   The value that is allowed to be returned or NULL if there is no
                ///   return type.
                /// </summary>
                public Type ReturnType;
 
+               /// <summary>
+               ///   Points to the Type (extracted from the TypeContainer) that
+               ///   declares this body of code
+               /// </summary>
+               public Type ContainerType;
+               
                /// <summary>
                ///   Whether this is generating code for a constructor
                /// </summary>
                public bool IsConstructor;
+
+               /// <summary>
+               ///   Whether we're control flow analysis enabled
+               /// </summary>
+               public bool DoFlowAnalysis;
                
                /// <summary>
                ///   Keeps track of the Type to LocalBuilder temporary storage created
@@ -122,6 +189,8 @@ namespace Mono.CSharp {
 
                public Block CurrentBlock;
 
+               public int CurrentFile;
+
                /// <summary>
                ///   The location where we store the return value.
                /// </summary>
@@ -133,6 +202,11 @@ namespace Mono.CSharp {
                /// </summary>
                public Label ReturnLabel;
 
+               /// <summary>
+               ///   If we already defined the ReturnLabel
+               /// </summary>
+               public bool HasReturnLabel;
+
                /// <summary>
                ///   Whether we are in a Finally block
                /// </summary>
@@ -143,82 +217,309 @@ namespace Mono.CSharp {
                /// </summary>
                public bool InTry;
 
+               /// <summary>
+               ///   Whether we are inside an iterator block.
+               /// </summary>
+               public bool InIterator;
+               
+               /// <summary>
+               ///   Whether remapping of locals, parameters and fields is turned on.
+               ///   Used by iterators and anonymous methods.
+               /// </summary>
+               public bool RemapToProxy;
+
                /// <summary>
                ///   Whether we are in a Catch block
                /// </summary>
                public bool InCatch;
+
+               /// <summary>
+               ///  Whether we are inside an unsafe block
+               /// </summary>
+               public bool InUnsafe;
+
+               /// <summary>
+               ///  Whether we are inside an anonymous method.
+               /// </summary>
+               public bool InAnonymousMethod;
                
                /// <summary>
-               ///   Whether we are generating code in a finally handler,
-               ///   this disallows `return' statements in there.
+               ///   Location for this EmitContext
+               /// </summary>
+               public Location loc;
+
+               /// <summary>
+               ///   Used to flag that it is ok to define types recursively, as the
+               ///   expressions are being evaluated as part of the type lookup
+               ///   during the type resolution process
                /// </summary>
-               Location loc;
+               public bool ResolvingTypeTree;
                
-               public EmitContext (TypeContainer parent, Location l, ILGenerator ig,
+               /// <summary>
+               ///   Inside an enum definition, we do not resolve enumeration values
+               ///   to their enumerations, but rather to the underlying type/value
+               ///   This is so EnumVal + EnumValB can be evaluated.
+               ///
+               ///   There is no "E operator + (E x, E y)", so during an enum evaluation
+               ///   we relax the rules
+               /// </summary>
+               public bool InEnumContext;
+
+               protected Stack FlowStack;
+               
+               public EmitContext (DeclSpace parent, DeclSpace ds, Location l, ILGenerator ig,
                                    Type return_type, int code_flags, bool is_constructor)
                {
                        this.ig = ig;
 
                        TypeContainer = parent;
+                       DeclSpace = ds;
                        CheckState = RootContext.Checked;
+                       ConstantCheckState = true;
+                       
                        IsStatic = (code_flags & Modifiers.STATIC) != 0;
+                       InIterator = (code_flags & Modifiers.METHOD_YIELDS) != 0;
+                       RemapToProxy = InIterator;
                        ReturnType = return_type;
                        IsConstructor = is_constructor;
                        CurrentBlock = null;
+                       CurrentFile = 0;
+                       
+                       if (parent != null){
+                               // Can only be null for the ResolveType contexts.
+                               ContainerType = parent.TypeBuilder;
+                               if (parent.UnsafeContext)
+                                       InUnsafe = true;
+                               else
+                                       InUnsafe = (code_flags & Modifiers.UNSAFE) != 0;
+                       }
                        loc = l;
 
+                       FlowStack = new Stack ();
+                       
                        if (ReturnType == TypeManager.void_type)
                                ReturnType = null;
                }
 
-               public EmitContext (TypeContainer parent, Location l, ILGenerator ig,
+               public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
+                                   Type return_type, int code_flags, bool is_constructor)
+                       : this (tc, tc, l, ig, return_type, code_flags, is_constructor)
+               {
+               }
+
+               public EmitContext (TypeContainer tc, Location l, ILGenerator ig,
                                    Type return_type, int code_flags)
-                       : this (parent, l, ig, return_type, code_flags, false)
+                       : this (tc, tc, l, ig, return_type, code_flags, false)
+               {
+               }
+
+               public FlowBranching CurrentBranching {
+                       get {
+                               return (FlowBranching) FlowStack.Peek ();
+                       }
+               }
+
+               // <summary>
+               //   Starts a new code branching.  This inherits the state of all local
+               //   variables and parameters from the current branching.
+               // </summary>
+               public FlowBranching StartFlowBranching (FlowBranchingType type, Location loc)
+               {
+                       FlowBranching cfb = new FlowBranching (CurrentBranching, type, null, loc);
+
+                       FlowStack.Push (cfb);
+
+                       return cfb;
+               }
+
+               // <summary>
+               //   Starts a new code branching for block `block'.
+               // </summary>
+               public FlowBranching StartFlowBranching (Block block)
+               {
+                       FlowBranching cfb;
+                       FlowBranchingType type;
+
+                       if (CurrentBranching.Type == FlowBranchingType.SWITCH)
+                               type = FlowBranchingType.SWITCH_SECTION;
+                       else
+                               type = FlowBranchingType.BLOCK;
+
+                       cfb = new FlowBranching (CurrentBranching, type, block, block.StartLocation);
+
+                       FlowStack.Push (cfb);
+
+                       return cfb;
+               }
+
+               // <summary>
+               //   Ends a code branching.  Merges the state of locals and parameters
+               //   from all the children of the ending branching.
+               // </summary>
+               public FlowReturns EndFlowBranching ()
                {
+                       FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
+
+                       return CurrentBranching.MergeChild (cfb);
                }
 
-               public void EmitTopBlock (Block block, Location loc)
+               // <summary>
+               //   Kills the current code branching.  This throws away any changed state
+               //   information and should only be used in case of an error.
+               // </summary>
+               public void KillFlowBranching ()
+               {
+                       FlowBranching cfb = (FlowBranching) FlowStack.Pop ();
+               }
+
+               // <summary>
+               //   Checks whether the local variable `vi' is already initialized
+               //   at the current point of the method's control flow.
+               //   If this method returns false, the caller must report an
+               //   error 165.
+               // </summary>
+               public bool IsVariableAssigned (VariableInfo vi)
+               {
+                       if (DoFlowAnalysis)
+                               return CurrentBranching.IsVariableAssigned (vi);
+                       else
+                               return true;
+               }
+
+               // <summary>
+               //   Marks the local variable `vi' as being initialized at the current
+               //   current point of the method's control flow.
+               // </summary>
+               public void SetVariableAssigned (VariableInfo vi)
+               {
+                       if (DoFlowAnalysis)
+                               CurrentBranching.SetVariableAssigned (vi);
+               }
+
+               // <summary>
+               //   Checks whether the parameter `number' is already initialized
+               //   at the current point of the method's control flow.
+               //   If this method returns false, the caller must report an
+               //   error 165.  This is only necessary for `out' parameters and the
+               //   call will always succeed for non-`out' parameters.
+               // </summary>
+               public bool IsParameterAssigned (int number)
+               {
+                       if (DoFlowAnalysis)
+                               return CurrentBranching.IsParameterAssigned (number);
+                       else
+                               return true;
+               }
+
+               // <summary>
+               //   Marks the parameter `number' as being initialized at the current
+               //   current point of the method's control flow.  This is only necessary
+               //   for `out' parameters.
+               // </summary>
+               public void SetParameterAssigned (int number)
+               {
+                       if (DoFlowAnalysis)
+                               CurrentBranching.SetParameterAssigned (number);
+               }
+
+               public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
                {
                        bool has_ret = false;
 
-//                     Console.WriteLine ("Emitting: " + loc);
+                       if (!Location.IsNull (loc))
+                               CurrentFile = loc.File;
+
                        if (block != null){
+                           try {
                                int errors = Report.Errors;
-                               
-                               block.EmitMeta (TypeContainer, ig, block, 0);
-                               
+
+                               block.EmitMeta (this, block);
+
                                if (Report.Errors == errors){
+                                       bool old_do_flow_analysis = DoFlowAnalysis;
+                                       DoFlowAnalysis = true;
+
+                                       FlowBranching cfb = new FlowBranching (block, ip, loc);
+                                       FlowStack.Push (cfb);
+
+                                       if (!block.Resolve (this)) {
+                                               FlowStack.Pop ();
+                                               DoFlowAnalysis = old_do_flow_analysis;
+                                               return;
+                                       }
+
+                                       cfb = (FlowBranching) FlowStack.Pop ();
+                                       FlowReturns returns = cfb.MergeTopBlock ();
+
+                                       DoFlowAnalysis = old_do_flow_analysis;
+
                                        has_ret = block.Emit (this);
 
+                                       if ((returns == FlowReturns.ALWAYS) ||
+                                           (returns == FlowReturns.EXCEPTION) ||
+                                           (returns == FlowReturns.UNREACHABLE))
+                                               has_ret = true;
+
                                        if (Report.Errors == errors){
                                                if (RootContext.WarningLevel >= 3)
                                                        block.UsageWarning ();
                                        }
                                }
+                           } catch {
+                                       Console.WriteLine ("Exception caught by the compiler while compiling:");
+                                       Console.WriteLine ("   Block that caused the problem begin at: " + loc);
+                                       
+                                       if (CurrentBlock != null){
+                                               Console.WriteLine ("                     Block being compiled: [{0},{1}]",
+                                                                  CurrentBlock.StartLocation, CurrentBlock.EndLocation);
+                                       }
+                                       throw;
+                           }
                        }
 
                        if (ReturnType != null && !has_ret){
                                //
                                // FIXME: we need full flow analysis to implement this
-                               // correctly.
+                               // correctly and emit an error instead of a warning.
                                //
-                               // Otherwise we report a non-existant error on cs-parser.cs
-                               // yyparse routine
                                //
-                               //Report.Error (161, loc, "Not all code paths return a value");
-                               return;
+                               if (!InIterator){
+                                       Report.Error (161, loc, "Not all code paths return a value");
+                                       return;
+                               }
                        }
-                               
-                       if (return_value != null){
+
+                       if (HasReturnLabel)
                                ig.MarkLabel (ReturnLabel);
+                       if (return_value != null){
                                ig.Emit (OpCodes.Ldloc, return_value);
                                ig.Emit (OpCodes.Ret);
                        } else {
-                               if (!has_ret)
-                                       ig.Emit (OpCodes.Ret);
+                               if (!InTry){
+                                       if (InIterator)
+                                               has_ret = true;
+                                       
+                                       if (!has_ret || HasReturnLabel)
+                                               ig.Emit (OpCodes.Ret);
+                               }
                        }
                }
 
+               /// <summary>
+               ///   This is called immediately before emitting an IL opcode to tell the symbol
+               ///   writer to which source line this opcode belongs.
+               /// </summary>
+               public void Mark (Location loc, bool check_file)
+               {
+                       if ((CodeGen.SymbolWriter == null) || Location.IsNull (loc))
+                               return;
+
+                       if (check_file && (CurrentFile != loc.File))
+                               return;
+
+                       ig.MarkSequencePoint (null, loc.Row, 0, 0, 0);
+               }
+
                /// <summary>
                ///   Returns a temporary storage for a variable of type t as 
                ///   a local variable in the current body.
@@ -227,19 +528,22 @@ namespace Mono.CSharp {
                {
                        LocalBuilder location;
                        
-                       if (temporary_storage == null)
-                               temporary_storage = new Hashtable ();
-                       
-                       location = (LocalBuilder) temporary_storage [t];
-                       if (location != null)
-                               return location;
+                       if (temporary_storage != null){
+                               location = (LocalBuilder) temporary_storage [t];
+                               if (location != null)
+                                       return location;
+                       }
                        
                        location = ig.DeclareLocal (t);
-                       temporary_storage.Add (t, location);
                        
                        return location;
                }
 
+               public void FreeTemporaryStorage (LocalBuilder b)
+               {
+                       // Empty for now.
+               }
+
                /// <summary>
                ///   Current loop begin and end labels.
                /// </summary>
@@ -250,6 +554,17 @@ namespace Mono.CSharp {
                /// </summary>
                public bool  InLoop;
 
+               /// <summary>
+               ///   This is incremented each time we enter a try/catch block and
+               ///   decremented if we leave it.
+               /// </summary>
+               public int   TryCatchLevel;
+
+               /// <summary>
+               ///   The TryCatchLevel at the begin of the current loop.
+               /// </summary>
+               public int   LoopBeginTryCatchLevel;
+
                /// <summary>
                ///   Default target in a switch statement.   Only valid if
                ///   InSwitch is true
@@ -272,9 +587,58 @@ namespace Mono.CSharp {
                        if (return_value == null){
                                return_value = ig.DeclareLocal (ReturnType);
                                ReturnLabel = ig.DefineLabel ();
+                               HasReturnLabel = true;
                        }
 
                        return return_value;
                }
+
+               //
+               // Creates a field `name' with the type `t' on the proxy class
+               //
+               public FieldBuilder MapVariable (string name, Type t)
+               {
+                       if (InIterator){
+                               return IteratorHandler.Current.MapVariable (name, t);
+                       }
+
+                       throw new Exception ("MapVariable for an unknown state");
+               }
+
+               //
+               // Emits the proper object to address fields on a remapped
+               // variable/parameter to field in anonymous-method/iterator proxy classes.
+               //
+               public void EmitThis ()
+               {
+                       ig.Emit (OpCodes.Ldarg_0);
+
+                       if (!IsStatic){
+                               if (InIterator)
+                                       ig.Emit (OpCodes.Ldfld, IteratorHandler.Current.this_field);
+                               else
+                                       throw new Exception ("EmitThis for an unknown state");
+                       }
+               }
+
+               /// <summary>
+               ///   A dynamic This that is shared by all variables in a emitcontext.
+               ///   Created on demand.
+               /// </summary>
+               public Expression my_this;
+               public Expression This {
+                       get {
+                               if (my_this == null) {
+                                       if (CurrentBlock != null)
+                                               my_this = new This (CurrentBlock, loc);
+                                       else
+                                               my_this = new This (loc);
+
+                                       my_this = my_this.Resolve (this);
+                               }
+
+                               return my_this;
+                       }
+               }
        }
 }