2004-08-07 Anirban Bhattacharjee <banirban@novell.com>
[mono.git] / mcs / mbas / codegen.cs
index 819acf29363f2db6f70ccfcc0163a8337a4b55e8..0ece01942c1f1f45d48a6dce6968b80eedc79609 100644 (file)
@@ -8,12 +8,13 @@
 //
 
 using System;
+using System.IO;
 using System.Collections;
 using System.Reflection;
 using System.Reflection.Emit;
 using System.Diagnostics.SymbolStore;
 
-namespace Mono.CSharp {
+namespace Mono.MonoBASIC {
 
        /// <summary>
        ///    Code generator class.
@@ -65,41 +66,47 @@ namespace Mono.CSharp {
                //
                // This routine initializes the Mono runtime SymbolWriter.
                //
-               static void InitMonoSymbolWriter (string basename, string[] debug_args)
+               static bool InitMonoSymbolWriter (string basename, string symbol_output,
+                                                 string exe_output_file, string[] debug_args)
                {
-                       string symbol_output = basename + "-debug.s";
-
                        Type itype = SymbolWriter.GetType ();
                        if (itype == null)
-                               return;
+                               return false;
 
-                       Type[] arg_types = new Type [2];
+                       Type[] arg_types = new Type [3];
                        arg_types [0] = typeof (string);
-                       arg_types [1] = typeof (string[]);
+                       arg_types [1] = typeof (string);
+                       arg_types [2] = typeof (string[]);
 
                        MethodInfo initialize = itype.GetMethod ("Initialize", arg_types);
                        if (initialize == null)
-                               return;
+                               return false;
 
-                       object[] args = new object [2];
-                       args [0] = symbol_output;
-                       args [1] = debug_args;
+                       object[] args = new object [3];
+                       args [0] = exe_output_file;
+                       args [1] = symbol_output;
+                       args [2] = debug_args;
 
                        initialize.Invoke (SymbolWriter, args);
+                       return true;
                }
 
                //
                // Initializes the symbol writer
                //
-               static void InitializeSymbolWriter (string basename, string[] args)
+               static void InitializeSymbolWriter (string basename, string symbol_output,
+                                                   string exe_output_file, string[] args)
                {
                        SymbolWriter = ModuleBuilder.GetSymWriter ();
 
                        //
                        // If we got an ISymbolWriter instance, initialize it.
                        //
-                       if (SymbolWriter == null)
+                       if (SymbolWriter == null) {
+                               Report.Warning (
+                                       -18, "Cannot find any symbol writer");
                                return;
+                       }
                        
                        //
                        // Due to lacking documentation about the first argument of the
@@ -115,11 +122,14 @@ namespace Mono.CSharp {
                        
                        switch (sym_type.Name){
                        case "MonoSymbolWriter":
-                               InitMonoSymbolWriter (basename, args);
+                               if (!InitMonoSymbolWriter (basename, symbol_output,
+                                                          exe_output_file, args))
+                                       Report.Warning (
+                                               -18, "Cannot initialize the symbol writer");
                                break;
 
                        default:
-                               Report.Error (
+                               Report.Warning (
                                        -18, "Cannot generate debugging information on this platform");
                                break;
                        }
@@ -151,8 +161,25 @@ namespace Mono.CSharp {
                        ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
                                Basename (name), Basename (output), want_debugging_support);
 
+                       int pos = output.LastIndexOf (".");
+
+                       string basename;
+                       if (pos > 0)
+                               basename = output.Substring (0, pos);
+                       else
+                               basename = output;
+
+                       string symbol_output = basename + ".dbg";
+
                        if (want_debugging_support)
-                               InitializeSymbolWriter (an.Name, debug_args);
+                               InitializeSymbolWriter (basename, symbol_output, output, debug_args);
+                       else {
+                               try {
+                                       File.Delete (symbol_output);
+                               } catch {
+                                       // Ignore errors.
+                               }
+                       }
                }
 
                static public void Save (string name)
@@ -212,6 +239,11 @@ namespace Mono.CSharp {
                /// </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.
@@ -228,6 +260,11 @@ namespace Mono.CSharp {
                ///   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
@@ -249,6 +286,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>
@@ -269,18 +311,23 @@ namespace Mono.CSharp {
                /// </summary>
                public bool InUnsafe;
                
+               /// <summary>
+               ///  Whether we are inside an unsafe block
+               /// </summary>
+               public bool InvokingOwnOverload;                
+
                /// <summary>
                ///   Location for this EmitContext
                /// </summary>
                public Location loc;
 
                /// <summary>
-               ///   Used to "flag" the resolution process to only lookup types,
-               ///   and nothing else.  This is an out-of-band communication
-               ///   path to SimpleName from the cast operation.
+               ///   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>
-               public bool OnlyLookupTypes;
-
+               public bool ResolvingTypeTree;
+               
                /// <summary>
                ///   Inside an enum definition, we do not resolve enumeration values
                ///   to their enumerations, but rather to the underlying type/value
@@ -291,6 +338,10 @@ namespace Mono.CSharp {
                /// </summary>
                public bool InEnumContext;
                
+               public string BlockName;
+
+               protected Stack FlowStack;
+               
                public EmitContext (TypeContainer parent, DeclSpace ds, Location l, ILGenerator ig,
                                    Type return_type, int code_flags, bool is_constructor)
                {
@@ -305,10 +356,20 @@ namespace Mono.CSharp {
                        ReturnType = return_type;
                        IsConstructor = is_constructor;
                        CurrentBlock = null;
-                       ContainerType = parent.TypeBuilder;
-                       InUnsafe = ((parent.ModFlags | code_flags) & Modifiers.UNSAFE) != 0;
-                       OnlyLookupTypes = false;
+                       BlockName = "";
+                       InvokingOwnOverload = false;
+                       
+                       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;
@@ -326,12 +387,131 @@ namespace Mono.CSharp {
                {
                }
 
-               public void EmitTopBlock (Block block, Location loc)
+               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)
                {
-                       bool has_ret = false;
+                       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);
+               }
+
+               // <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);
+               }
 
-//                     Console.WriteLine ("Emitting: " + loc);
+               // <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);
+               }
+
+               // These are two overloaded methods for EmitTopBlock
+               // since in MonoBasic functions we need the Function name
+               // along with its top block, in order to be able to
+               // retrieve the return value when there is no explicit 
+               // 'Return' statement
+               public void EmitTopBlock (Block block, InternalParameters ip, Location loc)
+               {
+                       EmitTopBlock (block, "", ip, loc);
+               }
+
+               public void EmitTopBlock (Block block, string bname, InternalParameters ip, Location loc)
+               {
+                       bool has_ret = false;
 
+                       //Console.WriteLine ("Emitting: '{0}", bname);
+                       BlockName = bname;
                        if (CodeGen.SymbolWriter != null)
                                Mark (loc);
 
@@ -341,11 +521,30 @@ namespace Mono.CSharp {
                                block.EmitMeta (this, block);
 
                                if (Report.Errors == errors){
-                                       if (!block.Resolve (this))
+                                       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 ();
@@ -355,21 +554,29 @@ namespace Mono.CSharp {
 
                        if (ReturnType != null && !has_ret){
                                //
-                               // FIXME: we need full flow analysis to implement this
-                               // correctly and emit an error instead of a warning.
-                               //
+                               // mcs here would report an error (and justly so), but functions without
+                               // an explicit return value are perfectly legal in MonoBasic
                                //
-                               Report.Error (161, loc, "Not all code paths return a value");
+                               
+                               VariableInfo vi = block.GetVariableInfo (bname);
+                               if (vi != null) 
+                               {
+                                       ig.Emit (OpCodes.Ldloc, vi.LocalBuilder);
+                                       ig.Emit (OpCodes.Ret);
+                               }
+                               else
+                                       Report.Error (-200, "This is not supposed to happen !");
                                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){
-                                       if (!InTry)
+                               if (!InTry){
+                                       if (!has_ret || HasReturnLabel)
                                                ig.Emit (OpCodes.Ret);
                                }
                        }
@@ -381,13 +588,13 @@ namespace Mono.CSharp {
                /// </summary>
                public void Mark (Location loc)
                {
-                       if (!Location.IsNull (loc)) {
+                       if ((CodeGen.SymbolWriter != null) && !Location.IsNull (loc)) {
                                ISymbolDocumentWriter doc = loc.SymbolDocument;
 
                                if (doc != null)
                                        ig.MarkSequencePoint (doc, loc.Row, 0,  loc.Row, 0);
-                       }               }
-
+                       }
+               }
 
                /// <summary>
                ///   Returns a temporary storage for a variable of type t as 
@@ -423,6 +630,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
@@ -445,6 +663,7 @@ namespace Mono.CSharp {
                        if (return_value == null){
                                return_value = ig.DeclareLocal (ReturnType);
                                ReturnLabel = ig.DefineLabel ();
+                               HasReturnLabel = true;
                        }
 
                        return return_value;
@@ -457,8 +676,14 @@ namespace Mono.CSharp {
                public Expression my_this;
                public Expression This {
                        get {
-                               if (my_this == null)
-                                       my_this = new This (loc).Resolve (this);
+                               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;
                        }