// // codegen.cs: The code generator // // Author: // Miguel de Icaza (miguel@ximian.com) // // (C) 2001 Ximian, Inc. // using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics.SymbolStore; namespace Mono.CSharp { /// /// Code generator class. /// public class CodeGen { AppDomain current_domain; AssemblyBuilder assembly_builder; ModuleBuilder module_builder; public ISymbolWriter SymbolWriter; public static string Basename (string name) { int pos = name.LastIndexOf ("/"); if (pos != -1) return name.Substring (pos + 1); pos = name.LastIndexOf ("\\"); if (pos != -1) return name.Substring (pos + 1); return name; } string TrimExt (string name) { int pos = name.LastIndexOf ("."); return name.Substring (0, pos); } public string FileName; public CodeGen (string name, string output, bool want_debugging_support) { AssemblyName an; FileName = output; an = new AssemblyName (); an.Name = TrimExt (Basename (name)); current_domain = AppDomain.CurrentDomain; assembly_builder = current_domain.DefineDynamicAssembly ( an, AssemblyBuilderAccess.RunAndSave); // // 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 // // If the third argument is true, the ModuleBuilder will dynamically // load the default symbol writer. module_builder = assembly_builder.DefineDynamicModule ( Basename (name), Basename (output), want_debugging_support); // If we want debugging support, get the default symbol writer // from the ModuleBuilder, it has already been dynamically loaded // by the ModuleBuilder's constructor. if (want_debugging_support) SymbolWriter = module_builder.GetSymWriter (); // If we got an ISymbolWriter instance, initialize it. if (SymbolWriter != null) { // 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'. if (SymbolWriter.GetType ().Name != "MonoSymbolWriter") Report.Error (16, "Cannot generate debugging information " + "on this platform"); string symbol_output = an.Name + "-debug.s"; // Mono's default symbol writer ignores the first and third argument // of this method. SymbolWriter.Initialize (new IntPtr (0), symbol_output, true); } } public AssemblyBuilder AssemblyBuilder { get { return assembly_builder; } } public ModuleBuilder ModuleBuilder { get { return module_builder; } } public void Save (string name) { try { assembly_builder.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 (); } } } /// /// An Emit Context is created for each body of code (from methods, /// properties bodies, indexer bodies or constructor bodies) /// public class EmitContext { public DeclSpace DeclSpace; public TypeContainer TypeContainer; public ILGenerator ig; /// /// 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. /// public bool CheckState; /// /// 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. /// public bool ConstantCheckState; /// /// Whether we are emitting code inside a static or instance method /// public bool IsStatic; /// /// The value that is allowed to be returned or NULL if there is no /// return type. /// public Type ReturnType; /// /// Points to the Type (extracted from the TypeContainer) that /// declares this body of code /// public Type ContainerType; /// /// Whether this is generating code for a constructor /// public bool IsConstructor; /// /// Keeps track of the Type to LocalBuilder temporary storage created /// to store structures (used to compute the address of the structure /// value on structure method invocations) /// public Hashtable temporary_storage; public Block CurrentBlock; /// /// The location where we store the return value. /// LocalBuilder return_value; /// /// The location where return has to jump to return the /// value /// public Label ReturnLabel; /// /// Whether we are in a Finally block /// public bool InFinally; /// /// Whether we are in a Try block /// public bool InTry; /// /// Whether we are in a Catch block /// public bool InCatch; /// /// Whether we are inside an unsafe block /// public bool InUnsafe; /// /// Location for this EmitContext /// public Location loc; /// /// 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. /// public bool OnlyLookupTypes; public EmitContext (TypeContainer 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; ReturnType = return_type; IsConstructor = is_constructor; CurrentBlock = null; ContainerType = parent.TypeBuilder; InUnsafe = ((parent.ModFlags | code_flags) & Modifiers.UNSAFE) != 0; OnlyLookupTypes = false; loc = l; if (ReturnType == TypeManager.void_type) ReturnType = null; } 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 (tc, tc, l, ig, return_type, code_flags, false) { } public void EmitTopBlock (Block block, Location loc) { bool has_ret = false; // Console.WriteLine ("Emitting: " + loc); if (block != null){ int errors = Report.Errors; block.EmitMeta (this, block); if (Report.Errors == errors){ has_ret = block.Emit (this); 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. // // Report.Error (161, loc, "Not all code paths return a value"); return; } if (return_value != null){ ig.MarkLabel (ReturnLabel); ig.Emit (OpCodes.Ldloc, return_value); ig.Emit (OpCodes.Ret); } else { if (!has_ret){ if (!InTry) ig.Emit (OpCodes.Ret); } } } /// /// Returns a temporary storage for a variable of type t as /// a local variable in the current body. /// public LocalBuilder GetTemporaryStorage (Type t) { LocalBuilder location; if (temporary_storage != null){ location = (LocalBuilder) temporary_storage [t]; if (location != null) return location; } location = ig.DeclareLocal (t); return location; } public void FreeTemporaryStorage (LocalBuilder b) { // Empty for now. } /// /// Current loop begin and end labels. /// public Label LoopBegin, LoopEnd; /// /// Whether we are inside a loop and break/continue are possible. /// public bool InLoop; /// /// Default target in a switch statement. Only valid if /// InSwitch is true /// public Label DefaultTarget; /// /// If this is non-null, points to the current switch statement /// public Switch Switch; /// /// ReturnValue creates on demand the LocalBuilder for the /// return value from the function. By default this is not /// used. This is only required when returns are found inside /// Try or Catch statements. /// public LocalBuilder TemporaryReturn () { if (return_value == null){ return_value = ig.DeclareLocal (ReturnType); ReturnLabel = ig.DefineLabel (); } return return_value; } /// /// 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); return my_this; } } } }