//
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.
//
// 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
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;
}
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)
/// </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.
/// 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
/// </summary>
public Label ReturnLabel;
+ /// <summary>
+ /// If we already defined the ReturnLabel
+ /// </summary>
+ public bool HasReturnLabel;
+
/// <summary>
/// Whether we are in a Finally block
/// </summary>
/// </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
/// </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)
{
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;
{
}
- 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);
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 ();
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);
}
}
/// </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
/// </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
if (return_value == null){
return_value = ig.DeclareLocal (ReturnType);
ReturnLabel = ig.DefineLabel ();
+ HasReturnLabel = true;
}
return return_value;
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;
}