//
using System;
+using System.IO;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
-using System.Diagnostics.SymbolStore;
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;
- public ISymbolWriter SymbolWriter;
+ static public SymbolWriter SymbolWriter;
public static string Basename (string name)
{
return name;
}
- string TrimExt (string name)
+ public static string Dirname (string name)
{
- int pos = name.LastIndexOf (".");
+ int pos = name.LastIndexOf ("/");
- return name.Substring (0, pos);
- }
+ if (pos != -1)
+ return name.Substring (0, pos);
- public string FileName;
+ pos = name.LastIndexOf ("\\");
+ if (pos != -1)
+ return name.Substring (0, pos);
- //
- // This routine initializes the Mono runtime SymbolWriter.
- //
- void InitMonoSymbolWriter (string basename)
+ return ".";
+ }
+
+ static string TrimExt (string name)
{
- string symbol_output = basename + "-debug.s";
+ int pos = name.LastIndexOf (".");
- //
- // Mono's default symbol writer ignores the first and third argument
- // of this method.
- //
- SymbolWriter.Initialize (new IntPtr (0), symbol_output, true);
+ return name.Substring (0, pos);
}
+ static public string FileName;
+
//
// Initializes the symbol writer
//
- void InitializeSymbolWriter (string basename)
+ static void InitializeSymbolWriter ()
{
- SymbolWriter = module_builder.GetSymWriter ();
+ SymbolWriter = SymbolWriter.GetSymbolWriter (ModuleBuilder);
//
// If we got an ISymbolWriter instance, initialize it.
//
- if (SymbolWriter == null)
+ 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;
-
- //
- // Due to lacking documentation about the first argument of the
- // Initialize method, we cannot use Microsoft's default symbol
- // writer yet.
- //
- // If we're using the mono symbol writer, the SymbolWriter object
- // is of type MonoSymbolWriter - but that's defined in a dynamically
- // loaded DLL - that's why we're doing a comparision based on the type
- // name here instead of using `SymbolWriter is MonoSymbolWriter'.
- //
- Type sym_type = ((object) SymbolWriter).GetType ();
-
- switch (sym_type.Name){
- case "MonoSymbolWriter":
- InitMonoSymbolWriter (basename);
- break;
-
- default:
- Report.Error (
- -18, "Cannot generate debugging information on this platform");
- break;
}
}
-
- public CodeGen (string name, string output, bool want_debugging_support)
+
+ //
+ // 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
// If the third argument is true, the ModuleBuilder will dynamically
// load the default symbol writer.
//
- module_builder = assembly_builder.DefineDynamicModule (
+ ModuleBuilder = AssemblyBuilder.DefineDynamicModule (
Basename (name), Basename (output), want_debugging_support);
if (want_debugging_support)
- InitializeSymbolWriter (an.Name);
- }
-
- public AssemblyBuilder AssemblyBuilder {
- get {
- return assembly_builder;
- }
- }
-
- public ModuleBuilder ModuleBuilder {
- get {
- return module_builder;
- }
+ InitializeSymbolWriter ();
}
- 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);
- }
-
- if (SymbolWriter != null) {
- // If we have a symbol writer, call its Close() method to write
- // the symbol file to disk.
- //
- // When using Mono's default symbol writer, the Close() method must
- // be called after the assembly has already been written to disk since
- // it opens the assembly and reads its metadata.
- SymbolWriter.Close ();
+ Report.Error (16, "Could not write to file `"+name+"', cause: " + io.Message);
}
}
}
/// </summary>
public class EmitContext {
public DeclSpace DeclSpace;
- public TypeContainer TypeContainer;
+ public DeclSpace TypeContainer;
public ILGenerator ig;
/// <summary>
/// </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
public Block CurrentBlock;
+ public int CurrentFile;
+
/// <summary>
/// The location where we store the return value.
/// </summary>
/// </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 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>
/// Whether we are inside an unsafe block
/// </summary>
public bool InUnsafe;
+
+ /// <summary>
+ /// Whether we are inside an anonymous method.
+ /// </summary>
+ public bool InAnonymousMethod;
/// <summary>
/// Location for this EmitContext
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;
- public EmitContext (TypeContainer parent, DeclSpace ds, 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;
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;
- ContainerType = parent.TypeBuilder;
- InUnsafe = ((parent.ModFlags | code_flags) & Modifiers.UNSAFE) != 0;
- OnlyLookupTypes = false;
+ 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 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)
+ {
+ 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);
+ }
+
+ // <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 (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){
// correctly and emit an error instead of a warning.
//
//
- 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){
- if (!InTry)
+ 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.
/// </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;
}
+ //
+ // 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.
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;
}