// // rootcontext.cs: keeps track of our tree representation, and assemblies loaded. // // Author: Miguel de Icaza (miguel@ximian.com) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using System.Diagnostics; namespace Mono.CSharp { public class RootContext { // // Contains the parsed tree // static Tree tree; // // Contains loaded assemblies and our generated code as we go. // static public TypeManager TypeManager; // // The System.Reflection.Emit CodeGenerator // static CodeGen cg; static public bool Optimize; // // The module builder pointer. // static ModuleBuilder mb; // // The list of global attributes (those that target the assembly) // static Attributes global_attributes; // // Whether we are being linked against the standard libraries. // This is only used to tell whether `System.Object' should // have a parent or not. // public static bool StdLib = true; // // This keeps track of the order in which classes were defined // so that we can poulate them in that order. // // Order is important, because we need to be able to tell by // examining the parent's list of methods which ones are virtual // or abstract as well as the parent names (to implement new, // override). // static ArrayList type_container_resolve_order; static ArrayList interface_resolve_order; // // Holds a reference to the Private Implementation Details // class. // static TypeBuilder impl_details_class; public static int WarningLevel = 2; // // Constructor // static RootContext () { tree = new Tree (); TypeManager = new TypeManager (); } static public Tree Tree { get { return tree; } } static public string MainClass; static public CodeGen CodeGen { get { return cg; } set { // // Temporary hack, we should probably // intialize `cg' rather than depending on // external initialization of it. // cg = value; mb = cg.ModuleBuilder; } } public static void RegisterOrder (Interface iface) { interface_resolve_order.Add (iface); } public static void RegisterOrder (TypeContainer tc) { type_container_resolve_order.Add (tc); } // // The default compiler checked state // static public bool Checked = false; // // Whether to allow Unsafe code // static public bool Unsafe = false; static string MakeFQN (string nsn, string name) { string prefix = (nsn == "" ? "" : nsn + "."); return prefix + name; } // // This function is used to resolve the hierarchy tree. // It processes interfaces, structs and classes in that order. // // It creates the TypeBuilder's as it processes the user defined // types. // static public void ResolveTree () { // // Interfaces are processed first, as classes and // structs might inherit from an object or implement // a set of interfaces, we need to be able to tell // them appart by just using the TypeManager. // TypeContainer root = Tree.Types; ArrayList ifaces = root.Interfaces; if (ifaces != null){ interface_resolve_order = new ArrayList (); foreach (Interface i in ifaces) i.DefineInterface (mb); } type_container_resolve_order = new ArrayList (); foreach (TypeContainer tc in root.Types) tc.DefineType (mb); if (root.Delegates != null) foreach (Delegate d in root.Delegates) d.DefineDelegate (mb); if (root.Enums != null) foreach (Enum e in root.Enums) e.DefineEnum (mb); } // // Closes all open types // // // // We usually use TypeBuilder types. When we are done // creating the type (which will happen after we have added // methods, fields, etc) we need to "Define" them before we // can save the Assembly // static public void CloseTypes () { TypeContainer root = Tree.Types; ArrayList ifaces = root.Interfaces; if (root.Enums != null) foreach (Enum en in root.Enums) en.CloseType (); if (interface_resolve_order != null){ foreach (Interface iface in interface_resolve_order) iface.CloseType (); } // // We do this in two passes, first we close the structs, // then the classes, because it seems the code needs it this // way. If this is really what is going on, we should probably // make sure that we define the structs in order as well. // if (type_container_resolve_order != null){ foreach (TypeContainer tc in type_container_resolve_order){ if (tc is Struct && tc.Parent == tree.Types){ tc.CloseType (); } } foreach (TypeContainer tc in type_container_resolve_order){ if (!(tc is Struct && tc.Parent == tree.Types)) tc.CloseType (); } } if (root.Delegates != null) foreach (Delegate d in root.Delegates) d.CloseDelegate (); // // If we have a class, close it // if (impl_details_class != null){ impl_details_class.CreateType (); } } // // Public function used to locate types, this can only // be used after the ResolveTree function has been invoked. // // Returns: Type or null if they type can not be found. // static public Type LookupType (DeclSpace ds, string name, bool silent, Location loc) { Type t; // // For the case the type we are looking for is nested within this one // or any base class // DeclSpace containing_ds = ds; while (containing_ds != null){ Type current_type = containing_ds.TypeBuilder; while (current_type != null) { t = TypeManager.LookupType (current_type.FullName + "+" + name); if (t != null) return t; current_type = current_type.BaseType; } containing_ds = containing_ds.Parent; } t = TypeManager.LookupType (MakeFQN (ds.Namespace.Name, name)); if (t != null) return t; // It's possible that name already is fully qualified. So we do // a simple direct lookup without adding any namespace names t = TypeManager.LookupType (name); if (t != null) return t; for (Namespace ns = ds.Namespace; ns != null; ns = ns.Parent){ ArrayList using_list = ns.UsingTable; if (using_list == null) continue; foreach (string n in using_list){ t = TypeManager.LookupType (MakeFQN (n, name)); if (t != null) return t; } } if (!silent) Report.Error (246, loc, "Cannot find type `"+name+"'"); return null; } // // This is the silent version of LookupType, you can use this // to `probe' for a type // static public Type LookupType (TypeContainer tc, string name, Location loc) { return LookupType (tc, name, true, loc); } static public bool IsNamespace (string name) { Namespace ns; if (tree.Namespaces != null){ ns = (Namespace) tree.Namespaces [name]; if (ns != null) return true; } return false; } static void Report1530 (Location loc) { Report.Error (1530, loc, "Keyword new not allowed for namespace elements"); } // // Populates the structs and classes with fields and methods // // // This is invoked after all interfaces, structs and classes // have been defined through `ResolveTree' static public void PopulateTypes () { TypeContainer root = Tree.Types; if (interface_resolve_order != null){ foreach (Interface iface in interface_resolve_order) if ((iface.ModFlags & Modifiers.NEW) == 0) iface.Define (root); else Report1530 (iface.Location); } if (type_container_resolve_order != null){ foreach (TypeContainer tc in type_container_resolve_order) if ((tc.ModFlags & Modifiers.NEW) == 0) tc.Define (root); else Report1530 (tc.Location); } ArrayList delegates = root.Delegates; if (delegates != null){ foreach (Delegate d in delegates) if ((d.ModFlags & Modifiers.NEW) == 0) d.Define (root); else Report1530 (d.Location); } ArrayList enums = root.Enums; if (enums != null){ foreach (Enum en in enums) if ((en.ModFlags & Modifiers.NEW) == 0) en.Define (root); else Report1530 (en.Location); } } static public void EmitCode () { if (type_container_resolve_order != null){ foreach (TypeContainer tc in type_container_resolve_order) tc.EmitConstants (); foreach (TypeContainer tc in type_container_resolve_order) tc.Emit (); } if (global_attributes != null){ EmitContext ec = new EmitContext ( tree.Types, Mono.CSharp.Location.Null, null, null, 0, false); AssemblyBuilder ab = cg.AssemblyBuilder; Attribute.ApplyAttributes (ec, ab, ab, global_attributes, global_attributes.Location); } if (Unsafe) { ConstructorInfo ci = TypeManager.unverifiable_code_type.GetConstructor (new Type [0]); if (ci == null) { Console.WriteLine ("Internal error !"); return; } CustomAttributeBuilder cb = new CustomAttributeBuilder (ci, new object [0]); mb.SetCustomAttribute (cb); } } static public ModuleBuilder ModuleBuilder { get { return mb; } } // // Public Field, used to track which method is the public entry // point. // static public MethodInfo EntryPoint; // // These are used to generate unique names on the structs and fields. // static int field_count; // // Makes an initialized struct, returns the field builder that // references the data. Thanks go to Sergey Chaban for researching // how to do this. And coming up with a shorter mechanism than I // was able to figure out. // // This works but makes an implicit public struct $ArrayType$SIZE and // makes the fields point to it. We could get more control if we did // use instead: // // 1. DefineNestedType on the impl_details_class with our struct. // // 2. Define the field on the impl_details_class // static public FieldBuilder MakeStaticData (byte [] data) { FieldBuilder fb; int size = data.Length; if (impl_details_class == null) impl_details_class = mb.DefineType ( "", TypeAttributes.NotPublic); fb = impl_details_class.DefineInitializedData ( "$$field-" + (field_count++), data, FieldAttributes.Static | FieldAttributes.Assembly); return fb; } static public void AddGlobalAttributes (AttributeSection sect, Location loc) { if (global_attributes == null) global_attributes = new Attributes (sect, loc); global_attributes.AddAttribute (sect); } } }