// // decl.cs: Declaration base class for structs, classes, enums and interfaces. // // Author: Miguel de Icaza (miguel@gnu.org) // // Licensed under the terms of the GNU GPL // // (C) 2001 Ximian, Inc (http://www.ximian.com) // // TODO: Move the method verification stuff from the class.cs and interface.cs here // using System; using System.Collections; using System.Reflection.Emit; using System.Reflection; namespace Mono.CSharp { /// /// Base representation for members. This is only used to keep track /// of Name, Location and Modifier flags. /// public abstract class MemberCore { /// /// Public name /// public string Name; /// /// Modifier flags that the user specified in the source code /// public int ModFlags; /// /// Location where this declaration happens /// public readonly Location Location; public MemberCore (string name, Location loc) { Name = name; Location = loc; } protected void WarningNotHiding (TypeContainer parent) { Report.Warning ( 109, Location, "The member `" + parent.Name + "." + Name + "' does not hide an " + "inherited member. The keyword new is not required"); } static string MethodBaseName (MethodBase mb) { return "`" + mb.ReflectedType.Name + "." + mb.Name + "'"; } void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method) { // // FIXME: report the old/new permissions? // Report.Error ( 507, "`" + parent_method + "." + Name + ": can't change the access modifiers from `" + parent_method.DeclaringType.Name + "." + parent_method.Name + "'"); } // // Performs various checks on the MethodInfo `mb' regarding the modifier flags // that have been defined. // // `name' is the user visible name for reporting errors (this is used to // provide the right name regarding method names and properties) // protected bool CheckMethodAgainstBase (TypeContainer parent, MethodAttributes my_attrs, MethodInfo mb) { bool ok = true; if ((ModFlags & Modifiers.OVERRIDE) != 0){ if (!(mb.IsAbstract || mb.IsVirtual)){ Report.Error ( 506, Location, parent.MakeName (Name) + ": cannot override inherited member " + MethodBaseName (mb) + " because it is not " + "virtual, abstract or override"); ok = false; } // Now we check that the overriden method is not final if (mb.IsFinal) { Report.Error (239, Location, parent.MakeName (Name) + " : cannot " + "override inherited member " + MethodBaseName (mb) + " because it is sealed."); ok = false; } // // Check that the permissions are not being changed // MethodAttributes thisp = my_attrs & MethodAttributes.MemberAccessMask; MethodAttributes parentp = mb.Attributes & MethodAttributes.MemberAccessMask; if (thisp != parentp){ Error_CannotChangeAccessModifiers (parent, mb); ok = false; } } if (mb.IsVirtual || mb.IsAbstract){ if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){ if (Name != "Finalize" && (RootContext.WarningLevel >= 2)){ Report.Warning ( 114, Location, parent.MakeName (Name) + " hides inherited member " + MethodBaseName (mb) + ". To make the current member override that " + "implementation, add the override keyword, " + "otherwise use the new keyword"); } } } return ok; } public abstract bool Define (TypeContainer parent); // // Whehter is it ok to use an unsafe pointer in this type container // public bool UnsafeOK (DeclSpace parent) { // // First check if this MemberCore modifier flags has unsafe set // if ((ModFlags & Modifiers.UNSAFE) != 0) return true; if (parent.UnsafeContext) return true; Expression.UnsafeError (Location); return false; } } // // FIXME: This is temporary outside DeclSpace, because I have to fix a bug // in MCS that makes it fail the lookup for the enum // /// /// The result value from adding an declaration into /// a struct or a class /// public enum AdditionResult { /// /// The declaration has been successfully /// added to the declation space. /// Success, /// /// The symbol has already been defined. /// NameExists, /// /// Returned if the declation being added to the /// name space clashes with its container name. /// /// The only exceptions for this are constructors /// and static constructors /// EnclosingClash, /// /// Returned if a constructor was created (because syntactically /// it looked like a constructor) but was not (because the name /// of the method is not the same as the container class /// NotAConstructor, /// /// This is only used by static constructors to emit the /// error 111, but this error for other things really /// happens at another level for other functions. /// MethodExists } /// /// Base class for structs, classes, enumerations and interfaces. /// /// /// They all create new declaration spaces. This /// provides the common foundation for managing those name /// spaces. /// public abstract class DeclSpace : MemberCore { /// /// this points to the actual definition that is being /// created with System.Reflection.Emit /// public TypeBuilder TypeBuilder; /// /// This variable tracks whether we have Closed the type /// public bool Created = false; // // This is the namespace in which this typecontainer // was declared. We use this to resolve names. // public Namespace Namespace; public Hashtable Cache = new Hashtable (); public string Basename; /// /// defined_names is used for toplevel objects /// protected Hashtable defined_names; TypeContainer parent; public DeclSpace (TypeContainer parent, string name, Location l) : base (name, l) { Basename = name.Substring (1 + name.LastIndexOf ('.')); defined_names = new Hashtable (); this.parent = parent; } /// /// Returns a status code based purely on the name /// of the member being added /// protected AdditionResult IsValid (string name) { if (name == Basename) return AdditionResult.EnclosingClash; if (defined_names.Contains (name)) return AdditionResult.NameExists; return AdditionResult.Success; } /// /// Introduce @name into this declaration space and /// associates it with the object @o. Note that for /// methods this will just point to the first method. o /// protected void DefineName (string name, object o) { defined_names.Add (name, o); } /// /// Returns the object associated with a given name in the declaration /// space. This is the inverse operation of `DefineName' /// public object GetDefinition (string name) { return defined_names [name]; } bool in_transit = false; /// /// This function is used to catch recursive definitions /// in declarations. /// public bool InTransit { get { return in_transit; } set { in_transit = value; } } public TypeContainer Parent { get { return parent; } } // // root_types contains all the types. All TopLevel types // hence have a parent that points to `root_types', that is // why there is a non-obvious test down here. // public bool IsTopLevel { get { if (parent != null){ if (parent.parent == null) return true; } return false; } } public virtual void CloseType () { if (!Created){ try { TypeBuilder.CreateType (); } catch { // // The try/catch is needed because // nested enumerations fail to load when they // are defined. // // Even if this is the right order (enumerations // declared after types). // // Note that this still creates the type and // it is possible to save it } Created = true; } } /// /// Should be overriten by the appropriate declaration space /// public abstract TypeBuilder DefineType (); // // Whether this is an `unsafe context' // public bool UnsafeContext { get { if ((ModFlags & Modifiers.UNSAFE) != 0) return true; if (parent != null) return parent.UnsafeContext; return false; } } public static string MakeFQN (string nsn, string name) { string prefix = (nsn == "" ? "" : nsn + "."); return prefix + name; } Type LookupInterfaceOrClass (string ns, string name, out bool error) { DeclSpace parent; Type t; error = false; name = MakeFQN (ns, name); t = TypeManager.LookupType (name); if (t != null) return t; parent = (DeclSpace) RootContext.Tree.Decls [name]; if (parent == null) return null; t = parent.DefineType (); if (t == null){ Report.Error (146, "Class definition is circular: `"+name+"'"); error = true; return null; } return t; } /// /// GetType is used to resolve type names at the DeclSpace level. /// Use this to lookup class/struct bases, interface bases or /// delegate type references /// /// /// /// Contrast this to LookupType which is used inside method bodies to /// lookup types that have already been defined. GetType is used /// during the tree resolution process and potentially define /// recursively the type /// public Type FindType (string name) { Type t; bool error; // // For the case the type we are looking for is nested within this one // or is in any base class // DeclSpace containing_ds = this; while (containing_ds != null){ Type current_type = containing_ds.TypeBuilder; while (current_type != null) { string pre = current_type.FullName; t = LookupInterfaceOrClass (pre, name, out error); if (error) return null; if (t != null) return t; current_type = current_type.BaseType; } containing_ds = containing_ds.Parent; } // // Attempt to lookup the class on our namespace and all it's implicit parents // for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) { t = LookupInterfaceOrClass (ns, name, out error); if (error) return null; if (t != null) return t; } // // Attempt to do a direct unqualified lookup // t = LookupInterfaceOrClass ("", name, out error); if (error) return null; if (t != null) return t; // // Attempt to lookup the class on any of the `using' // namespaces // for (Namespace ns = Namespace; ns != null; ns = ns.Parent){ t = LookupInterfaceOrClass (ns.Name, name, out error); if (error) return null; if (t != null) return t; // // Now check the using clause list // ArrayList using_list = ns.UsingTable; if (using_list == null) continue; foreach (string n in using_list){ t = LookupInterfaceOrClass (n, name, out error); if (error) return null; if (t != null) return t; } } Report.Error (246, Location, "Can not find type `"+name+"'"); return null; } } }