X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fdecl.cs;h=1a2c46c2c28643998cfa2733bcbbc868d87c6c8c;hb=9fd98bca7ce45185c86f60f9cbecc8f2691009eb;hp=40fd4dcdca59ab015aa0e15f550503edb2d24183;hpb=630c4a5800a5e64021e11e28473d72a15ac4f202;p=mono.git diff --git a/mcs/mcs/decl.cs b/mcs/mcs/decl.cs index 40fd4dcdca5..1a2c46c2c28 100755 --- a/mcs/mcs/decl.cs +++ b/mcs/mcs/decl.cs @@ -2,6 +2,7 @@ // decl.cs: Declaration base class for structs, classes, enums and interfaces. // // Author: Miguel de Icaza (miguel@gnu.org) +// Marek Safar (marek.safar@seznam.cz) // // Licensed under the terms of the GNU GPL // @@ -12,178 +13,208 @@ using System; using System.Collections; +using System.Globalization; using System.Reflection.Emit; using System.Reflection; namespace Mono.CSharp { + public class MemberName { + public string Name; + public readonly MemberName Left; + + public static readonly MemberName Null = new MemberName (""); + + public MemberName (string name) + { + this.Name = name; + } + + public MemberName (MemberName left, string name) + : this (name) + { + this.Left = left; + } + + public MemberName (MemberName left, MemberName right) + : this (left, right.Name) + { + } + + public string GetName () + { + return GetName (false); + } + + public string GetName (bool is_generic) + { + string name = is_generic ? Basename : Name; + if (Left != null) + return Left.GetName (is_generic) + "." + name; + else + return name; + } + + public string GetFullName () + { + if (Left != null) + return Left.GetFullName () + "." + Name; + else + return Name; + } + + public string GetTypeName () + { + if (Left != null) + return Left.GetTypeName () + "." + Name; + else + return Name; + } + + public Expression GetTypeExpression (Location loc) + { + if (Left != null) { + Expression lexpr = Left.GetTypeExpression (loc); + + return new MemberAccess (lexpr, Name, loc); + } else { + return new SimpleName (Name, loc); + } + } + + public MemberName Clone () + { + if (Left != null) + return new MemberName (Left.Clone (), Name); + else + return new MemberName (Name); + } + + public string Basename { + get { + return Name; + } + } + + public override string ToString () + { + return GetFullName (); + } + } + /// - /// Base representation for members. This is only used to keep track - /// of Name, Location and Modifier flags. + /// Base representation for members. This is used to keep track + /// of Name, Location and Modifier flags, and handling Attributes. /// - public abstract class MemberCore { + public abstract class MemberCore : Attributable { /// /// Public name /// - public string Name; + public string Name { + get { + // !(this is GenericMethod) && !(this is Method) + return MemberName.GetName (false); + } + } + + public readonly MemberName MemberName; /// /// Modifier flags that the user specified in the source code /// public int ModFlags; + public readonly TypeContainer Parent; + /// /// Location where this declaration happens /// public readonly Location Location; - public MemberCore (string name, Location loc) + [Flags] + public enum Flags { + Obsolete_Undetected = 1, // Obsolete attribute has not been detected yet + Obsolete = 1 << 1, // Type has obsolete attribute + ClsCompliance_Undetected = 1 << 2, // CLS Compliance has not been detected yet + ClsCompliant = 1 << 3, // Type is CLS Compliant + CloseTypeCreated = 1 << 4, // Tracks whether we have Closed the type + HasCompliantAttribute_Undetected = 1 << 5, // Presence of CLSCompliantAttribute has not been detected + HasClsCompliantAttribute = 1 << 6, // Type has CLSCompliantAttribute + ClsCompliantAttributeTrue = 1 << 7, // Type has CLSCompliant (true) + Excluded_Undetected = 1 << 8, // Conditional attribute has not been detected yet + Excluded = 1 << 9, // Method is conditional + TestMethodDuplication = 1 << 10 // Test for duplication must be performed + } + + /// + /// MemberCore flags at first detected then cached + /// + internal Flags caching_flags; + + public MemberCore (TypeContainer parent, MemberName name, Attributes attrs, + Location loc) + : base (attrs) { - Name = name; + Parent = parent; + MemberName = name; Location = loc; + caching_flags = Flags.Obsolete_Undetected | Flags.ClsCompliance_Undetected | Flags.HasCompliantAttribute_Undetected | Flags.Excluded_Undetected; } - protected void WarningNotHiding (TypeContainer parent) + /// + /// Tests presence of ObsoleteAttribute and report proper error + /// + protected void CheckUsageOfObsoleteAttribute (Type type) { - Report.Warning ( - 109, Location, - "The member " + parent.MakeName (Name) + " does not hide an " + - "inherited member. The keyword new is not required"); - + if (type == null) + return; + + ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type); + if (obsolete_attr == null) + return; + + AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, Location); } - void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method, - string name) + public abstract bool Define (); + + // + // Returns full member name for error message + // + public virtual string GetSignatureForError () { - // - // FIXME: report the old/new permissions? - // - Report.Error ( - 507, Location, parent.MakeName (Name) + - ": can't change the access modifiers when overriding inherited " + - "member `" + name + "'"); + return 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, string name) + + /// + /// Use this method when MethodBuilder is null + /// + public virtual string GetSignatureForError (TypeContainer tc) { - bool ok = true; - - if ((ModFlags & Modifiers.OVERRIDE) != 0){ - if (!(mb.IsAbstract || mb.IsVirtual)){ - Report.Error ( - 506, Location, parent.MakeName (Name) + - ": cannot override inherited member `" + - name + "' 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 `" + name + - "' 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; + return Name; + } - // - // special case for "protected internal" - // + /// + /// Base Emit method. This is also entry point for CLS-Compliant verification. + /// + public virtual void Emit () + { + // Hack with Parent == null is for EnumMember + if (Parent == null || (GetObsoleteAttribute (Parent) == null && Parent.GetObsoleteAttribute (Parent) == null)) + VerifyObsoleteAttribute (); - if ((parentp & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem){ - // - // when overriding protected internal, the method can be declared - // protected internal only within the same assembly - // + if (!RootContext.VerifyClsCompliance) + return; - if ((thisp & MethodAttributes.FamORAssem) == MethodAttributes.FamORAssem){ - if (parent.TypeBuilder.Assembly != mb.DeclaringType.Assembly){ - // - // assemblies differ - report an error - // - - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - } else if (thisp != parentp) { - // - // same assembly, but other attributes differ - report an error - // - - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - }; - } else if ((thisp & MethodAttributes.Family) != MethodAttributes.Family) { - // - // if it's not "protected internal", it must be "protected" - // - - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - } else if (parent.TypeBuilder.Assembly == mb.DeclaringType.Assembly) { - // - // protected within the same assembly - an error - // - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - } else if ((thisp & ~(MethodAttributes.Family | MethodAttributes.FamORAssem)) != - (parentp & ~(MethodAttributes.Family | MethodAttributes.FamORAssem))) { - // - // protected ok, but other attributes differ - report an error - // - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - } - } else { - if (thisp != parentp){ - Error_CannotChangeAccessModifiers (parent, mb, name); - ok = false; - } - } - } + VerifyClsCompliance (Parent); + } - if (mb.IsVirtual || mb.IsAbstract){ - if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){ - if (Name != "Finalize"){ - Report.Warning ( - 114, 2, Location, parent.MakeName (Name) + - " hides inherited member `" + name + - "'. To make the current member override that " + - "implementation, add the override keyword, " + - "otherwise use the new keyword"); - ModFlags |= Modifiers.NEW; - } - } - } else { - if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){ - if (Name != "Finalize"){ - Report.Warning ( - 108, 1, Location, "The keyword new is required on " + - parent.MakeName (Name) + " because it hides " + - "inherited member `" + name + "'"); - ModFlags |= Modifiers.NEW; - } - } + public bool InUnsafe { + get { + return ((ModFlags & Modifiers.UNSAFE) != 0) || Parent.UnsafeContext; } - - return ok; } - public abstract bool Define (TypeContainer parent); - // // Whehter is it ok to use an unsafe pointer in this type container // @@ -201,53 +232,133 @@ namespace Mono.CSharp { 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 - // + /// + /// Returns instance of ObsoleteAttribute for this MemberCore + /// + public ObsoleteAttribute GetObsoleteAttribute (DeclSpace ds) + { + // ((flags & (Flags.Obsolete_Undetected | Flags.Obsolete)) == 0) is slower, but why ? + if ((caching_flags & Flags.Obsolete_Undetected) == 0 && (caching_flags & Flags.Obsolete) == 0) { + return null; + } + + caching_flags &= ~Flags.Obsolete_Undetected; + + if (OptAttributes == null) + return null; + + // TODO: remove this allocation + EmitContext ec = new EmitContext (ds.Parent, ds, ds.Location, + null, null, ds.ModFlags, false); + + Attribute obsolete_attr = OptAttributes.Search (TypeManager.obsolete_attribute_type, ec); + if (obsolete_attr == null) + return null; + + ObsoleteAttribute obsolete = obsolete_attr.GetObsoleteAttribute (ds); + if (obsolete == null) + return null; + + caching_flags |= Flags.Obsolete; + return obsolete; + } + + /// + /// Analyze whether CLS-Compliant verification must be execute for this MemberCore. + /// + public override bool IsClsCompliaceRequired (DeclSpace container) + { + if ((caching_flags & Flags.ClsCompliance_Undetected) == 0) + return (caching_flags & Flags.ClsCompliant) != 0; + + if (GetClsCompliantAttributeValue (container) && IsExposedFromAssembly (container)) { + caching_flags &= ~Flags.ClsCompliance_Undetected; + caching_flags |= Flags.ClsCompliant; + return true; + } + + caching_flags &= ~Flags.ClsCompliance_Undetected; + return false; + } + + /// + /// Returns true when MemberCore is exposed from assembly. + /// + protected bool IsExposedFromAssembly (DeclSpace ds) + { + if ((ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return false; + + DeclSpace parentContainer = ds; + while (parentContainer != null && parentContainer.ModFlags != 0) { + if ((parentContainer.ModFlags & (Modifiers.PUBLIC | Modifiers.PROTECTED)) == 0) + return false; + parentContainer = parentContainer.Parent; + } + return true; + } /// - /// The result value from adding an declaration into - /// a struct or a class + /// Resolve CLSCompliantAttribute value or gets cached value. /// - 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 + bool GetClsCompliantAttributeValue (DeclSpace ds) + { + if (OptAttributes != null) { + EmitContext ec = new EmitContext (ds.Parent, ds, ds.Location, + null, null, ds.ModFlags, false); + Attribute cls_attribute = OptAttributes.GetClsCompliantAttribute (ec); + if (cls_attribute != null) { + caching_flags |= Flags.HasClsCompliantAttribute; + return cls_attribute.GetClsCompliantAttributeValue (ds); + } + } + return ds.GetClsCompliantAttributeValue (); } + /// + /// Returns true if MemberCore is explicitly marked with CLSCompliantAttribute + /// + protected bool HasClsCompliantAttribute { + get { + return (caching_flags & Flags.HasClsCompliantAttribute) != 0; + } + } + + + /// + /// The main virtual method for CLS-Compliant verifications. + /// The method returns true if member is CLS-Compliant and false if member is not + /// CLS-Compliant which means that CLS-Compliant tests are not necessary. A descendants override it + /// and add their extra verifications. + /// + protected virtual bool VerifyClsCompliance (DeclSpace ds) + { + if (!IsClsCompliaceRequired (ds)) { + if ((RootContext.WarningLevel >= 2) && HasClsCompliantAttribute && !IsExposedFromAssembly (ds)) { + Report.Warning (3019, Location, "CLS compliance checking will not be performed on '{0}' because it is private or internal", GetSignatureForError ()); + } + return false; + } + + if (!CodeGen.Assembly.IsClsCompliant) { + if (HasClsCompliantAttribute) { + Report.Error (3014, Location, "'{0}' cannot be marked as CLS-compliant because the assembly does not have a CLSCompliant attribute", GetSignatureForError ()); + } + return false; + } + + int index = Name.LastIndexOf ('.'); + if (Name [index > 0 ? index + 1 : 0] == '_') { + Report.Error (3008, Location, "Identifier '{0}' is not CLS-compliant", GetSignatureForError () ); + } + return true; + } + + protected abstract void VerifyObsoleteAttribute (); + + } + /// /// Base class for structs, classes, enumerations and interfaces. /// @@ -263,78 +374,72 @@ namespace Mono.CSharp { /// 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 NamespaceEntry NamespaceEntry; public Hashtable Cache = new Hashtable (); public string Basename; - /// - /// defined_names is used for toplevel objects - /// protected Hashtable defined_names; - TypeContainer parent; + static string[] attribute_targets = new string [] { "type" }; - public DeclSpace (TypeContainer parent, string name, Location l) - : base (name, l) + public DeclSpace (NamespaceEntry ns, TypeContainer parent, MemberName name, + Attributes attrs, Location l) + : base (parent, name, attrs, l) { - Basename = name.Substring (1 + name.LastIndexOf ('.')); + NamespaceEntry = ns; + Basename = name.Name; defined_names = new Hashtable (); - this.parent = parent; } /// - /// Returns a status code based purely on the name - /// of the member being added + /// Adds the member to defined_names table. It tests for duplications and enclosing name conflicts /// - protected AdditionResult IsValid (string name) + protected bool AddToContainer (MemberCore symbol, bool is_method, string fullname, string basename) { - if (name == Basename) - return AdditionResult.EnclosingClash; + if (basename == Basename) { + Report.SymbolRelatedToPreviousError (this); + Report.Error (542, "'{0}': member names cannot be the same as their enclosing type", symbol.Location, symbol.GetSignatureForError ()); + return false; + } - if (defined_names.Contains (name)) - return AdditionResult.NameExists; + MemberCore mc = (MemberCore)defined_names [fullname]; - return AdditionResult.Success; + if (is_method && (mc is MethodCore || mc is IMethodData)) { + symbol.caching_flags |= Flags.TestMethodDuplication; + mc.caching_flags |= Flags.TestMethodDuplication; + return true; + } + + if (mc != null) { + Report.SymbolRelatedToPreviousError (mc); + Report.Error (102, symbol.Location, "The type '{0}' already contains a definition for '{1}'", GetSignatureForError (), basename); + return false; + } + + defined_names.Add (fullname, symbol); + return true; } - public static int length; - public static int small; - - /// - /// 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) + public void RecordDecl () { - defined_names.Add (name, o); - -#if DEBUGME - int p = name.LastIndexOf ("."); - int l = name.Length; - length += l; - small += l -p; -#endif + if ((NamespaceEntry != null) && (Parent == RootContext.Tree.Types)) + NamespaceEntry.DefineName (MemberName.Basename, this); } /// - /// Returns the object associated with a given name in the declaration - /// space. This is the inverse operation of `DefineName' + /// Returns the MemberCore associated with a given name in the declaration + /// space. It doesn't return method based symbols !! /// - public object GetDefinition (string name) + /// + public MemberCore GetDefinition (string name) { - return defined_names [name]; + return (MemberCore)defined_names [name]; } bool in_transit = false; @@ -353,19 +458,13 @@ namespace Mono.CSharp { } } - public TypeContainer Parent { - get { - return parent; - } - } - /// /// Looks up the alias for the name /// public string LookupAlias (string name) { - if (Namespace != null) - return Namespace.LookupAlias (name); + if (NamespaceEntry != null) + return NamespaceEntry.LookupAlias (name); else return null; } @@ -377,8 +476,8 @@ namespace Mono.CSharp { // public bool IsTopLevel { get { - if (parent != null){ - if (parent.parent == null) + if (Parent != null){ + if (Parent.Parent == null) return true; } return false; @@ -387,7 +486,7 @@ namespace Mono.CSharp { public virtual void CloseType () { - if (!Created){ + if ((caching_flags & Flags.CloseTypeCreated) == 0){ try { TypeBuilder.CreateType (); } catch { @@ -402,13 +501,13 @@ namespace Mono.CSharp { // Note that this still creates the type and // it is possible to save it } - Created = true; + caching_flags |= Flags.CloseTypeCreated; } } /// /// Should be overriten by the appropriate declaration space - /// + /// public abstract TypeBuilder DefineType (); /// @@ -424,8 +523,8 @@ namespace Mono.CSharp { get { if ((ModFlags & Modifiers.UNSAFE) != 0) return true; - if (parent != null) - return parent.UnsafeContext; + if (Parent != null) + return Parent.UnsafeContext; return false; } } @@ -447,70 +546,284 @@ namespace Mono.CSharp { } // - // Looks up the type, as parsed into the expression `e' + // Looks up the type, as parsed into the expression `e'. // + //[Obsolete ("This method is going away soon")] public Type ResolveType (Expression e, bool silent, Location loc) + { + TypeExpr d = ResolveTypeExpr (e, silent, loc); + return d == null ? null : d.Type; + } + + // + // Resolves the expression `e' for a type, and will recursively define + // types. This should only be used for resolving base types. + // + public TypeExpr ResolveTypeExpr (Expression e, bool silent, Location loc) { if (type_resolve_ec == null) - type_resolve_ec = GetTypeResolveEmitContext (parent, loc); + type_resolve_ec = GetTypeResolveEmitContext (Parent, loc); type_resolve_ec.loc = loc; + type_resolve_ec.ContainerType = TypeBuilder; - int errors = Report.Errors; - Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type); + return e.ResolveAsTypeTerminal (type_resolve_ec, silent); + } + + public bool CheckAccessLevel (Type check_type) + { + if (check_type == TypeBuilder) + return true; - if (d == null || d.eclass != ExprClass.Type){ - if (!silent && errors == Report.Errors){ - Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'"); - } - return null; + TypeAttributes check_attr = check_type.Attributes & TypeAttributes.VisibilityMask; + + // + // Broken Microsoft runtime, return public for arrays, no matter what + // the accessibility is for their underlying class, and they return + // NonPublic visibility for pointers + // + if (check_type.IsArray || check_type.IsPointer) + return CheckAccessLevel (TypeManager.GetElementType (check_type)); + + if (TypeBuilder == null) + // FIXME: TypeBuilder will be null when invoked by Class.GetNormalBases(). + // However, this is invoked again later -- so safe to return true. + // May also be null when resolving top-level attributes. + return true; + + switch (check_attr){ + case TypeAttributes.Public: + return true; + + case TypeAttributes.NotPublic: + // + // This test should probably use the declaringtype. + // + return check_type.Assembly == TypeBuilder.Assembly; + + case TypeAttributes.NestedPublic: + return true; + + case TypeAttributes.NestedPrivate: + return NestedAccessible (check_type); + + case TypeAttributes.NestedFamily: + return FamilyAccessible (check_type); + + case TypeAttributes.NestedFamANDAssem: + return (check_type.Assembly == TypeBuilder.Assembly) && + FamilyAccessible (check_type); + + case TypeAttributes.NestedFamORAssem: + return (check_type.Assembly == TypeBuilder.Assembly) || + FamilyAccessible (check_type); + + case TypeAttributes.NestedAssembly: + return check_type.Assembly == TypeBuilder.Assembly; } - return d.Type; + Console.WriteLine ("HERE: " + check_attr); + return false; + } - // - // Resolves the expression `e' for a type, and will recursively define - // types. - // - public Expression ResolveTypeExpr (Expression e, bool silent, Location loc) + protected bool NestedAccessible (Type check_type) { - if (type_resolve_ec == null) - type_resolve_ec = GetTypeResolveEmitContext (parent, loc); + string check_type_name = check_type.FullName; - Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type); - - if (d == null || d.eclass != ExprClass.Type){ - if (!silent){ - Report.Error (246, loc, "Cannot find type `"+ e +"'"); - } - return null; + // At this point, we already know check_type is a nested class. + int cio = check_type_name.LastIndexOf ('+'); + + // Ensure that the string 'container' has a '+' in it to avoid false matches + string container = check_type_name.Substring (0, cio + 1); + + // Ensure that type_name ends with a '+' so that it can match 'container', if necessary + string type_name = TypeBuilder.FullName + "+"; + + // If the current class is nested inside the container of check_type, + // we can access check_type even if it is private or protected. + return type_name.StartsWith (container); + } + + protected bool FamilyAccessible (Type check_type) + { + Type declaring = check_type.DeclaringType; + if (TypeBuilder == declaring || + TypeBuilder.IsSubclassOf (declaring)) + return true; + + return NestedAccessible (check_type); + } + + // Access level of a type. + const int X = 1; + enum AccessLevel { // Each column represents `is this scope larger or equal to Blah scope' + // Public Assembly Protected + Protected = (0 << 0) | (0 << 1) | (X << 2), + Public = (X << 0) | (X << 1) | (X << 2), + Private = (0 << 0) | (0 << 1) | (0 << 2), + Internal = (0 << 0) | (X << 1) | (0 << 2), + ProtectedOrInternal = (0 << 0) | (X << 1) | (X << 2), + } + + static AccessLevel GetAccessLevelFromModifiers (int flags) + { + if ((flags & Modifiers.INTERNAL) != 0) { + + if ((flags & Modifiers.PROTECTED) != 0) + return AccessLevel.ProtectedOrInternal; + else + return AccessLevel.Internal; + + } else if ((flags & Modifiers.PROTECTED) != 0) + return AccessLevel.Protected; + + else if ((flags & Modifiers.PRIVATE) != 0) + return AccessLevel.Private; + + else + return AccessLevel.Public; + } + + // What is the effective access level of this? + // TODO: Cache this? + AccessLevel EffectiveAccessLevel { + get { + AccessLevel myAccess = GetAccessLevelFromModifiers (ModFlags); + if (!IsTopLevel && (Parent != null)) + return myAccess & Parent.EffectiveAccessLevel; + else + return myAccess; } + } + + // Return the access level for type `t' + static AccessLevel TypeEffectiveAccessLevel (Type t) + { + if (t.IsPublic) + return AccessLevel.Public; + if (t.IsNestedPrivate) + return AccessLevel.Private; + if (t.IsNotPublic) + return AccessLevel.Internal; + + // By now, it must be nested + AccessLevel parentLevel = TypeEffectiveAccessLevel (t.DeclaringType); + + if (t.IsNestedPublic) + return parentLevel; + if (t.IsNestedAssembly) + return parentLevel & AccessLevel.Internal; + if (t.IsNestedFamily) + return parentLevel & AccessLevel.Protected; + if (t.IsNestedFamORAssem) + return parentLevel & AccessLevel.ProtectedOrInternal; + if (t.IsNestedFamANDAssem) + throw new NotImplementedException ("NestedFamANDAssem not implemented, cant make this kind of type from c# anyways"); + + // nested private is taken care of + + throw new Exception ("I give up, what are you?"); + } + + // + // This answers `is the type P, as accessible as a member M which has the + // accessability @flags which is declared as a nested member of the type T, this declspace' + // + public bool AsAccessible (Type p, int flags) + { + // + // 1) if M is private, its accessability is the same as this declspace. + // we already know that P is accessible to T before this method, so we + // may return true. + // + + if ((flags & Modifiers.PRIVATE) != 0) + return true; + + while (p.IsArray || p.IsPointer || p.IsByRef) + p = TypeManager.GetElementType (p); + + AccessLevel pAccess = TypeEffectiveAccessLevel (p); + AccessLevel mAccess = this.EffectiveAccessLevel & + GetAccessLevelFromModifiers (flags); + + // for every place from which we can access M, we must + // be able to access P as well. So, we want + // For every bit in M and P, M_i -> P_1 == true + // or, ~ (M -> P) == 0 <-> ~ ( ~M | P) == 0 + + return ~ (~ mAccess | pAccess) == 0; + } + + static DoubleHash dh = new DoubleHash (1000); + + Type DefineTypeAndParents (DeclSpace tc) + { + DeclSpace container = tc.Parent; + + if (container.TypeBuilder == null && container.Name != "") + DefineTypeAndParents (container); - return d; + return tc.DefineType (); } Type LookupInterfaceOrClass (string ns, string name, out bool error) { DeclSpace parent; Type t; - + object r; + error = false; - name = MakeFQN (ns, name); + int p = name.LastIndexOf ('.'); + + if (dh.Lookup (ns, name, out r)) + return (Type) r; + else { + // + // If the type is not a nested type, we do not need `LookupType's processing. + // If the @name does not have a `.' in it, this cant be a nested type. + // + if (ns != ""){ + if (Namespace.IsNamespace (ns)) { + if (p != -1) + t = TypeManager.LookupType (ns + "." + name); + else + t = TypeManager.LookupTypeDirect (ns + "." + name); + } else + t = null; + } else if (p != -1) + t = TypeManager.LookupType (name); + else + t = TypeManager.LookupTypeDirect (name); + } - t = TypeManager.LookupType (name); - if (t != null) + if (t != null) { + dh.Insert (ns, name, t); return t; + } - parent = (DeclSpace) RootContext.Tree.Decls [name]; - if (parent == null) - return null; + // + // In case we are fed a composite name, normalize it. + // + + if (p != -1){ + ns = MakeFQN (ns, name.Substring (0, p)); + name = name.Substring (p+1); + } - t = parent.DefineType (); + parent = RootContext.Tree.LookupByNamespace (ns, name); + if (parent == null) { + dh.Insert (ns, name, null); + return null; + } + + t = DefineTypeAndParents (parent); if (t == null){ - Report.Error (146, Location, "Class definition is circular: `"+name+"'"); error = true; return null; } + + dh.Insert (ns, name, t); return t; } @@ -545,29 +858,29 @@ namespace Mono.CSharp { DeclSpace containing_ds = this; while (containing_ds != null){ - Type current_type = containing_ds.TypeBuilder; + Type container_type = containing_ds.TypeBuilder; + Type current_type = container_type; - while (current_type != null) { + while (current_type != null && current_type != TypeManager.object_type) { string pre = current_type.FullName; t = LookupInterfaceOrClass (pre, name, out error); if (error) return null; - if (t != null) + if ((t != null) && containing_ds.CheckAccessLevel (t)) 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); + for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.ImplicitParent) { + t = LookupInterfaceOrClass (ns.FullName, name, out error); if (error) return null; @@ -590,37 +903,47 @@ namespace Mono.CSharp { // namespaces // - for (Namespace ns = Namespace; ns != null; ns = ns.Parent){ + for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){ - t = LookupInterfaceOrClass (ns.Name, name, out error); + t = LookupInterfaceOrClass (ns.FullName, name, out error); if (error) return null; if (t != null) return t; + if (name.IndexOf ('.') > 0) + continue; + + string alias_value = ns.LookupAlias (name); + if (alias_value != null) { + t = LookupInterfaceOrClass ("", alias_value, 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; - Type match = null; - foreach (Namespace.UsingEntry ue in using_list){ - match = LookupInterfaceOrClass (ue.Name, name, out error); + foreach (Namespace using_ns in ns.GetUsingTable ()) { + match = LookupInterfaceOrClass (using_ns.Name, name, out error); if (error) return null; if (match != null){ if (t != null){ - Error_AmbiguousTypeReference (loc, name, t, match); - return null; + if (CheckAccessLevel (match)) { + Error_AmbiguousTypeReference (loc, name, t, match); + return null; + } + continue; } t = match; - ue.Used = true; } } if (t != null) @@ -646,6 +969,67 @@ namespace Mono.CSharp { public abstract MemberCache MemberCache { get; } + + public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb) + { + try { + TypeBuilder.SetCustomAttribute (cb); + } catch (System.ArgumentException e) { + Report.Warning (-21, a.Location, + "The CharSet named property on StructLayout\n"+ + "\tdoes not work correctly on Microsoft.NET\n"+ + "\tYou might want to remove the CharSet declaration\n"+ + "\tor compile using the Mono runtime instead of the\n"+ + "\tMicrosoft .NET runtime\n"+ + "\tThe runtime gave the error: " + e); + } + } + + /// + /// Goes through class hierarchy and get value of first CLSCompliantAttribute that found. + /// If no is attribute exists then return assembly CLSCompliantAttribute. + /// + public bool GetClsCompliantAttributeValue () + { + if ((caching_flags & Flags.HasCompliantAttribute_Undetected) == 0) + return (caching_flags & Flags.ClsCompliantAttributeTrue) != 0; + + caching_flags &= ~Flags.HasCompliantAttribute_Undetected; + + if (OptAttributes != null) { + EmitContext ec = new EmitContext (Parent, this, Location, + null, null, ModFlags, false); + Attribute cls_attribute = OptAttributes.GetClsCompliantAttribute (ec); + if (cls_attribute != null) { + caching_flags |= Flags.HasClsCompliantAttribute; + if (cls_attribute.GetClsCompliantAttributeValue (this)) { + caching_flags |= Flags.ClsCompliantAttributeTrue; + return true; + } + return false; + } + } + + if (Parent == null) { + if (CodeGen.Assembly.IsClsCompliant) { + caching_flags |= Flags.ClsCompliantAttributeTrue; + return true; + } + return false; + } + + if (Parent.GetClsCompliantAttributeValue ()) { + caching_flags |= Flags.ClsCompliantAttributeTrue; + return true; + } + return false; + } + + public override string[] ValidAttributeTargets { + get { + return attribute_targets; + } + } } /// @@ -823,7 +1207,7 @@ namespace Mono.CSharp { /// This is used when creating the member cache for a class to get all /// members from the parent class. /// - IMemberContainer Parent { + IMemberContainer ParentContainer { get; } @@ -874,8 +1258,7 @@ namespace Mono.CSharp { public readonly IMemberContainer Container; protected Hashtable member_hash; protected Hashtable method_hash; - protected Hashtable interface_hash; - + /// /// Create a new MemberCache for the given IMemberContainer `container'. /// @@ -886,14 +1269,20 @@ namespace Mono.CSharp { Timer.IncrementCounter (CounterType.MemberCache); Timer.StartTimer (TimerType.CacheInit); - interface_hash = new Hashtable (); + // If we have a parent class (we have a parent class unless we're // TypeManager.object_type), we deep-copy its MemberCache here. - if (Container.Parent != null) - member_hash = SetupCache (Container.Parent.MemberCache); - else if (Container.IsInterface) - member_hash = SetupCacheForInterface (); + if (Container.IsInterface) { + MemberCache parent; + + if (Container.ParentContainer != null) + parent = Container.ParentContainer.MemberCache; + else + parent = TypeHandle.ObjectType.MemberCache; + member_hash = SetupCacheForInterface (parent); + } else if (Container.ParentContainer != null) + member_hash = SetupCache (Container.ParentContainer.MemberCache); else member_hash = new Hashtable (); @@ -921,31 +1310,29 @@ namespace Mono.CSharp { IDictionaryEnumerator it = parent.member_hash.GetEnumerator (); while (it.MoveNext ()) { hash [it.Key] = ((ArrayList) it.Value).Clone (); - } - + } + return hash; } - void AddInterfaces (MemberCache parent) - { - foreach (Type iface in parent.interface_hash.Keys) { - if (!interface_hash.Contains (iface)) - interface_hash.Add (iface, true); - } - } /// /// Add the contents of `new_hash' to `hash'. /// - void AddHashtable (Hashtable hash, Hashtable new_hash) + void AddHashtable (Hashtable hash, MemberCache cache) { + Hashtable new_hash = cache.member_hash; IDictionaryEnumerator it = new_hash.GetEnumerator (); while (it.MoveNext ()) { ArrayList list = (ArrayList) hash [it.Key]; - if (list != null) - list.AddRange ((ArrayList) it.Value); - else - hash [it.Key] = ((ArrayList) it.Value).Clone (); + if (list == null) + hash [it.Key] = list = new ArrayList (); + + foreach (CacheEntry entry in (ArrayList) it.Value) { + if (entry.Container != cache.Container) + break; + list.Add (entry); + } } } @@ -954,22 +1341,20 @@ namespace Mono.CSharp { /// Type.GetMembers() won't return any inherited members for interface types, /// so we need to do this manually. Interfaces also inherit from System.Object. /// - Hashtable SetupCacheForInterface () + Hashtable SetupCacheForInterface (MemberCache parent) { - Hashtable hash = SetupCache (TypeHandle.ObjectType.MemberCache); - Type [] ifaces = TypeManager.GetInterfaces (Container.Type); + Hashtable hash = SetupCache (parent); + TypeExpr [] ifaces = TypeManager.GetInterfaces (Container.Type); - foreach (Type iface in ifaces) { - if (interface_hash.Contains (iface)) - continue; - interface_hash.Add (iface, true); + foreach (TypeExpr iface in ifaces) { + Type itype = iface.Type; IMemberContainer iface_container = - TypeManager.LookupMemberContainer (iface); + TypeManager.LookupMemberContainer (itype); MemberCache iface_cache = iface_container.MemberCache; - AddHashtable (hash, iface_cache.member_hash); - AddInterfaces (iface_cache); + + AddHashtable (hash, iface_cache); } return hash; @@ -1010,8 +1395,6 @@ namespace Mono.CSharp { void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container) { MemberList members = container.GetMembers (mt, bf); - BindingFlags new_bf = (container == Container) ? - bf | BindingFlags.DeclaredOnly : bf; foreach (MemberInfo member in members) { string name = member.Name; @@ -1049,13 +1432,11 @@ namespace Mono.CSharp { { MemberInfo [] members = type.GetMethods (bf); + Array.Reverse (members); + foreach (MethodBase member in members) { string name = member.Name; - // Varargs methods aren't allowed in C# code. - if ((member.CallingConvention & CallingConventions.VarArgs) != 0) - continue; - // We use a name-based hash table of ArrayList's. ArrayList list = (ArrayList) method_hash [name]; if (list == null) { @@ -1071,6 +1452,8 @@ namespace Mono.CSharp { list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf)); } + + } /// @@ -1214,9 +1597,17 @@ namespace Mono.CSharp { /// The lookup process will automatically restart itself in method-only /// search mode if it discovers that it's about to return methods. /// - public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name, - MemberFilter filter, object criteria) + ArrayList global = new ArrayList (); + bool using_global = false; + + static MemberInfo [] emptyMemberInfo = new MemberInfo [0]; + + public MemberInfo [] FindMembers (MemberTypes mt, BindingFlags bf, string name, + MemberFilter filter, object criteria) { + if (using_global) + throw new Exception (); + bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0; bool method_search = mt == MemberTypes.Method; // If we have a method cache and we aren't already doing a method-only search, @@ -1228,15 +1619,24 @@ namespace Mono.CSharp { // If this is a method-only search, we try to use the method cache if // possible; a lookup in the method cache will return a MemberInfo with // the correct ReflectedType for inherited methods. + if (method_search && (method_hash != null)) applicable = (ArrayList) method_hash [name]; else applicable = (ArrayList) member_hash [name]; if (applicable == null) - return MemberList.Empty; + return emptyMemberInfo; - ArrayList list = new ArrayList (); + // + // 32 slots gives 53 rss/54 size + // 2/4 slots gives 55 rss + // + // Strange: from 25,000 calls, only 1,800 + // are above 2. Why does this impact it? + // + global.Clear (); + using_global = true; Timer.StartTimer (TimerType.CachedLookup); @@ -1244,6 +1644,7 @@ namespace Mono.CSharp { IMemberContainer current = Container; + // `applicable' is a list of all members with the given member name `name' // in the current class and all its parent classes. The list is sorted in // reverse order due to the way how the cache is initialy created (to speed @@ -1258,7 +1659,7 @@ namespace Mono.CSharp { // iteration of this loop if there are no members with the name we're // looking for in the current class). if (entry.Container != current) { - if (declared_only || DoneSearching (list)) + if (declared_only || DoneSearching (global)) break; current = entry.Container; @@ -1276,7 +1677,7 @@ namespace Mono.CSharp { if (filter (entry.Member, criteria)) { if ((entry.EntryType & EntryType.MaskType) != EntryType.Method) do_method_search = false; - list.Add (entry.Member); + global.Add (entry.Member); } } @@ -1286,10 +1687,303 @@ namespace Mono.CSharp { // search, we restart in method-only search mode if the first match is // a method. This ensures that we return a MemberInfo with the correct // ReflectedType for inherited methods. - if (do_method_search && (list.Count > 0)) + if (do_method_search && (global.Count > 0)){ + using_global = false; + return FindMembers (MemberTypes.Method, bf, name, filter, criteria); + } - return new MemberList (list); + using_global = false; + MemberInfo [] copy = new MemberInfo [global.Count]; + global.CopyTo (copy); + return copy; } + + // find the nested type @name in @this. + public Type FindNestedType (string name) + { + ArrayList applicable = (ArrayList) member_hash [name]; + if (applicable == null) + return null; + + for (int i = applicable.Count-1; i >= 0; i--) { + CacheEntry entry = (CacheEntry) applicable [i]; + if ((entry.EntryType & EntryType.NestedType & EntryType.MaskType) != 0) + return (Type) entry.Member; + } + + return null; + } + + // + // This finds the method or property for us to override. invocationType is the type where + // the override is going to be declared, name is the name of the method/property, and + // paramTypes is the parameters, if any to the method or property + // + // Because the MemberCache holds members from this class and all the base classes, + // we can avoid tons of reflection stuff. + // + public MemberInfo FindMemberToOverride (Type invocationType, string name, Type [] paramTypes, bool is_property) + { + ArrayList applicable; + if (method_hash != null && !is_property) + applicable = (ArrayList) method_hash [name]; + else + applicable = (ArrayList) member_hash [name]; + + if (applicable == null) + return null; + // + // Walk the chain of methods, starting from the top. + // + for (int i = applicable.Count - 1; i >= 0; i--) { + CacheEntry entry = (CacheEntry) applicable [i]; + + if ((entry.EntryType & (is_property ? (EntryType.Property | EntryType.Field) : EntryType.Method)) == 0) + continue; + + PropertyInfo pi = null; + MethodInfo mi = null; + FieldInfo fi = null; + Type [] cmpAttrs = null; + + if (is_property) { + if ((entry.EntryType & EntryType.Field) != 0) { + fi = (FieldInfo)entry.Member; + + // TODO: For this case we ignore member type + //fb = TypeManager.GetField (fi); + //cmpAttrs = new Type[] { fb.MemberType }; + } else { + pi = (PropertyInfo) entry.Member; + cmpAttrs = TypeManager.GetArgumentTypes (pi); + } + } else { + mi = (MethodInfo) entry.Member; + cmpAttrs = TypeManager.GetArgumentTypes (mi); + } + + if (fi != null) { + // TODO: Almost duplicate ! + // Check visibility + switch (fi.Attributes & FieldAttributes.FieldAccessMask) { + case FieldAttributes.Private: + // + // A private method is Ok if we are a nested subtype. + // The spec actually is not very clear about this, see bug 52458. + // + if (invocationType != entry.Container.Type & + TypeManager.IsNestedChildOf (invocationType, entry.Container.Type)) + continue; + + break; + case FieldAttributes.FamANDAssem: + case FieldAttributes.Assembly: + // + // Check for assembly methods + // + if (mi.DeclaringType.Assembly != CodeGen.Assembly.Builder) + continue; + break; + } + return entry.Member; + } + + // + // Check the arguments + // + if (cmpAttrs.Length != paramTypes.Length) + continue; + + for (int j = cmpAttrs.Length - 1; j >= 0; j --) + if (paramTypes [j] != cmpAttrs [j]) + goto next; + + // + // get one of the methods because this has the visibility info. + // + if (is_property) { + mi = pi.GetGetMethod (true); + if (mi == null) + mi = pi.GetSetMethod (true); + } + + // + // Check visibility + // + switch (mi.Attributes & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Private: + // + // A private method is Ok if we are a nested subtype. + // The spec actually is not very clear about this, see bug 52458. + // + if (invocationType == entry.Container.Type || + TypeManager.IsNestedChildOf (invocationType, entry.Container.Type)) + return entry.Member; + + break; + case MethodAttributes.FamANDAssem: + case MethodAttributes.Assembly: + // + // Check for assembly methods + // + if (mi.DeclaringType.Assembly == CodeGen.Assembly.Builder) + return entry.Member; + + break; + default: + // + // A protected method is ok, because we are overriding. + // public is always ok. + // + return entry.Member; + } + next: + ; + } + + return null; + } + + /// + /// The method is looking for conflict with inherited symbols (errors CS0108, CS0109). + /// We handle two cases. The first is for types without parameters (events, field, properties). + /// The second are methods, indexers and this is why ignore_complex_types is here. + /// The latest param is temporary hack. See DoDefineMembers method for more info. + /// + public MemberInfo FindMemberWithSameName (string name, bool ignore_complex_types, MemberInfo ignore_member) + { + ArrayList applicable = null; + + if (method_hash != null) + applicable = (ArrayList) method_hash [name]; + + if (applicable != null) { + for (int i = applicable.Count - 1; i >= 0; i--) { + CacheEntry entry = (CacheEntry) applicable [i]; + if ((entry.EntryType & EntryType.Public) != 0) + return entry.Member; + } + } + + if (member_hash == null) + return null; + applicable = (ArrayList) member_hash [name]; + + if (applicable != null) { + for (int i = applicable.Count - 1; i >= 0; i--) { + CacheEntry entry = (CacheEntry) applicable [i]; + if ((entry.EntryType & EntryType.Public) != 0 & entry.Member != ignore_member) { + if (ignore_complex_types) { + if ((entry.EntryType & EntryType.Method) != 0) + continue; + + // Does exist easier way how to detect indexer ? + if ((entry.EntryType & EntryType.Property) != 0) { + Type[] arg_types = TypeManager.GetArgumentTypes ((PropertyInfo)entry.Member); + if (arg_types.Length > 0) + continue; + } + } + return entry.Member; + } + } + } + return null; + } + + Hashtable locase_table; + + /// + /// Builds low-case table for CLS Compliance test + /// + public Hashtable GetPublicMembers () + { + if (locase_table != null) + return locase_table; + + locase_table = new Hashtable (); + foreach (DictionaryEntry entry in member_hash) { + ArrayList members = (ArrayList)entry.Value; + for (int ii = 0; ii < members.Count; ++ii) { + CacheEntry member_entry = (CacheEntry) members [ii]; + + if ((member_entry.EntryType & EntryType.Public) == 0) + continue; + + // TODO: Does anyone know easier way how to detect that member is internal ? + switch (member_entry.EntryType & EntryType.MaskType) { + case EntryType.Constructor: + continue; + + case EntryType.Field: + if ((((FieldInfo)member_entry.Member).Attributes & (FieldAttributes.Assembly | FieldAttributes.Public)) == FieldAttributes.Assembly) + continue; + break; + + case EntryType.Method: + if ((((MethodInfo)member_entry.Member).Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly) + continue; + break; + + case EntryType.Property: + PropertyInfo pi = (PropertyInfo)member_entry.Member; + if (pi.GetSetMethod () == null && pi.GetGetMethod () == null) + continue; + break; + + case EntryType.Event: + EventInfo ei = (EventInfo)member_entry.Member; + MethodInfo mi = ei.GetAddMethod (); + if ((mi.Attributes & (MethodAttributes.Assembly | MethodAttributes.Public)) == MethodAttributes.Assembly) + continue; + break; + } + string lcase = ((string)entry.Key).ToLower (System.Globalization.CultureInfo.InvariantCulture); + locase_table [lcase] = member_entry.Member; + break; + } + } + return locase_table; + } + + public Hashtable Members { + get { + return member_hash; + } + } + + /// + /// Cls compliance check whether methods or constructors parameters differing only in ref or out, or in array rank + /// + public void VerifyClsParameterConflict (ArrayList al, MethodCore method, MemberInfo this_builder) + { + EntryType tested_type = (method is Constructor ? EntryType.Constructor : EntryType.Method) | EntryType.Public; + + for (int i = 0; i < al.Count; ++i) { + MemberCache.CacheEntry entry = (MemberCache.CacheEntry) al [i]; + + // skip itself + if (entry.Member == this_builder) + continue; + + if ((entry.EntryType & tested_type) != tested_type) + continue; + + MethodBase method_to_compare = (MethodBase)entry.Member; + if (AttributeTester.AreOverloadedMethodParamsClsCompliant (method.ParameterTypes, TypeManager.GetArgumentTypes (method_to_compare))) + continue; + + IMethodData md = TypeManager.GetMethod (method_to_compare); + + // TODO: now we are ignoring CLSCompliance(false) on method from other assembly which is buggy. + // However it is exactly what csc does. + if (md != null && !md.IsClsCompliaceRequired (method.Parent)) + continue; + + Report.SymbolRelatedToPreviousError (entry.Member); + Report.Error (3006, method.Location, "Overloaded method '{0}' differing only in ref or out, or in array rank, is not CLS-compliant", method.GetSignatureForError ()); + } + } } }