2 // decl.cs: Declaration base class for structs, classes, enums and interfaces.
4 // Author: Miguel de Icaza (miguel@gnu.org)
6 // Licensed under the terms of the GNU GPL
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
10 // TODO: Move the method verification stuff from the class.cs and interface.cs here
15 using System.Collections;
16 using System.Reflection.Emit;
17 using System.Reflection;
19 namespace Mono.CSharp {
21 public class TypeName {
22 public readonly string Name;
23 public readonly TypeArguments TypeArguments;
25 public readonly TypeName Left;
27 public static readonly TypeName Null = new TypeName ("");
29 public TypeName (string name)
34 public TypeName (string name, TypeArguments args)
37 this.TypeArguments = args;
40 public TypeName (TypeName left, string name, TypeArguments args)
46 public string GetName ()
49 return Left.GetName () + "." + Name;
54 public int CountTypeArguments {
56 if (TypeArguments == null)
59 return TypeArguments.Count;
63 public string GetFullName ()
66 if (TypeArguments != null)
67 full_name = Name + "<" + TypeArguments + ">";
71 return Left.GetFullName () + "." + full_name;
76 public string GetTypeName (bool full)
79 if (full && (TypeArguments != null))
80 suffix = "!" + TypeArguments.Count;
82 return Left.GetTypeName (full) + "." + Name + suffix;
87 public Expression GetTypeExpression (Location loc)
90 Expression lexpr = Left.GetTypeExpression (loc);
92 if (TypeArguments != null)
93 return new GenericMemberAccess (lexpr, Name, TypeArguments, loc);
95 return new MemberAccess (lexpr, Name, loc);
97 if (TypeArguments != null)
98 return new ConstructedType (Name, TypeArguments, loc);
100 return new SimpleName (Name, loc);
104 public MemberName GetMemberName ()
106 if (TypeArguments != null) {
107 string[] type_params = TypeArguments.GetDeclarations ();
108 return new MemberName (Left, Name, type_params);
110 return new MemberName (Left, Name);
113 public override string ToString ()
116 if (TypeArguments != null)
117 full_name = Name + "<" + TypeArguments + ">";
122 return Left + "." + full_name;
128 public class MemberName {
129 public readonly TypeName TypeName;
130 public readonly string Name;
131 public readonly string[] TypeParameters;
133 public MemberName (string name)
138 public MemberName (TypeName type, string name)
140 this.TypeName = type;
144 public MemberName (TypeName type, MemberName name)
146 this.TypeName = type;
147 this.Name = name.Name;
148 this.TypeParameters = name.TypeParameters;
151 public MemberName (TypeName type, string name, ArrayList type_params)
154 if (type_params != null) {
155 TypeParameters = new string [type_params.Count];
156 type_params.CopyTo (TypeParameters, 0);
160 public MemberName (TypeName type, string name, string[] type_params)
163 this.TypeParameters = type_params;
166 public TypeName MakeTypeName (Location loc)
168 if (TypeParameters != null) {
169 TypeArguments args = new TypeArguments (loc);
170 foreach (string param in TypeParameters)
171 args.Add (new SimpleName (param, loc));
172 return new TypeName (TypeName, Name, args);
175 return new TypeName (TypeName, Name, null);
178 public static readonly MemberName Null = new MemberName ("");
180 public string Basename {
182 if (TypeParameters != null)
183 return Name + "!" + TypeParameters.Length;
189 public string GetName (bool is_generic)
191 string name = is_generic ? Basename : Name;
192 if (TypeName != null)
193 return TypeName.GetTypeName (is_generic) + "." + name;
198 public int CountTypeParameters {
200 if (TypeParameters != null)
203 return TypeParameters.Length;
207 protected string PrintTypeParams ()
209 if (TypeParameters != null) {
210 StringBuilder sb = new StringBuilder ();
212 for (int i = 0; i < TypeParameters.Length; i++) {
215 sb.Append (TypeParameters [i]);
218 return sb.ToString ();
224 public string FullName {
226 string full_name = Name + PrintTypeParams ();
228 if (TypeName != null)
229 return TypeName + "." + full_name;
235 public override string ToString ()
237 return String.Format ("MemberName [{0}:{1}:{2}]",
238 TypeName, Name, PrintTypeParams ());
243 /// Base representation for members. This is only used to keep track
244 /// of Name, Location and Modifier flags.
246 public abstract class MemberCore {
252 public readonly MemberName MemberName;
255 /// Modifier flags that the user specified in the source code
260 /// Location where this declaration happens
262 public readonly Location Location;
265 /// Attributes for this type
267 Attributes attributes;
269 public MemberCore (MemberName name, Attributes attrs, Location loc)
271 Name = name.GetName (!(this is GenericMethod) && !(this is Method));
277 public abstract bool Define (TypeContainer parent);
280 // Returns full member name for error message
282 public virtual string GetSignatureForError () {
286 public Attributes OptAttributes
297 // Whehter is it ok to use an unsafe pointer in this type container
299 public bool UnsafeOK (DeclSpace parent)
302 // First check if this MemberCore modifier flags has unsafe set
304 if ((ModFlags & Modifiers.UNSAFE) != 0)
307 if (parent.UnsafeContext)
310 Expression.UnsafeError (Location);
316 /// Base class for structs, classes, enumerations and interfaces.
319 /// They all create new declaration spaces. This
320 /// provides the common foundation for managing those name
323 public abstract class DeclSpace : MemberCore, IAlias {
325 /// This points to the actual definition that is being
326 /// created with System.Reflection.Emit
328 public TypeBuilder TypeBuilder;
331 /// If we are a generic type, this is the type we are
332 /// currently defining. We need to lookup members on this
333 /// instead of the TypeBuilder.
335 public TypeExpr CurrentType;
338 /// This variable tracks whether we have Closed the type
340 public bool Created = false;
343 // This is the namespace in which this typecontainer
344 // was declared. We use this to resolve names.
346 public NamespaceEntry NamespaceEntry;
348 public Hashtable Cache = new Hashtable ();
350 public string Basename;
353 /// defined_names is used for toplevel objects
355 protected Hashtable defined_names;
357 readonly bool is_generic;
358 readonly int count_type_params;
361 // Whether we are Generic
363 public bool IsGeneric {
367 else if (parent != null)
368 return parent.IsGeneric;
374 TypeContainer parent;
376 public DeclSpace (NamespaceEntry ns, TypeContainer parent, MemberName name,
377 Attributes attrs, Location l)
378 : base (name, attrs, l)
381 Basename = name.Name;
382 defined_names = new Hashtable ();
383 if (name.TypeParameters != null) {
385 count_type_params = name.TypeParameters.Length;
388 count_type_params += parent.count_type_params;
389 this.parent = parent;
392 public void RecordDecl ()
394 if ((NamespaceEntry != null) && (parent == RootContext.Tree.Types))
395 NamespaceEntry.DefineName (MemberName.Basename, this);
399 /// The result value from adding an declaration into
400 /// a struct or a class
402 public enum AdditionResult {
404 /// The declaration has been successfully
405 /// added to the declation space.
410 /// The symbol has already been defined.
415 /// Returned if the declation being added to the
416 /// name space clashes with its container name.
418 /// The only exceptions for this are constructors
419 /// and static constructors
424 /// Returned if a constructor was created (because syntactically
425 /// it looked like a constructor) but was not (because the name
426 /// of the method is not the same as the container class
431 /// This is only used by static constructors to emit the
432 /// error 111, but this error for other things really
433 /// happens at another level for other functions.
438 /// Some other error.
444 /// Returns a status code based purely on the name
445 /// of the member being added
447 protected AdditionResult IsValid (string basename, string name)
449 if (basename == Basename)
450 return AdditionResult.EnclosingClash;
452 if (defined_names.Contains (name))
453 return AdditionResult.NameExists;
455 return AdditionResult.Success;
458 public static int length;
459 public static int small;
462 /// Introduce @name into this declaration space and
463 /// associates it with the object @o. Note that for
464 /// methods this will just point to the first method. o
466 public void DefineName (string name, object o)
468 defined_names.Add (name, o);
471 int p = name.LastIndexOf ('.');
479 /// Returns the object associated with a given name in the declaration
480 /// space. This is the inverse operation of `DefineName'
482 public object GetDefinition (string name)
484 return defined_names [name];
487 bool in_transit = false;
490 /// This function is used to catch recursive definitions
493 public bool InTransit {
503 public TypeContainer Parent {
510 /// Looks up the alias for the name
512 public IAlias LookupAlias (string name)
514 if (NamespaceEntry != null)
515 return NamespaceEntry.LookupAlias (name);
521 // root_types contains all the types. All TopLevel types
522 // hence have a parent that points to `root_types', that is
523 // why there is a non-obvious test down here.
525 public bool IsTopLevel {
528 if (parent.parent == null)
535 public virtual void CloseType ()
539 TypeBuilder.CreateType ();
542 // The try/catch is needed because
543 // nested enumerations fail to load when they
546 // Even if this is the right order (enumerations
547 // declared after types).
549 // Note that this still creates the type and
550 // it is possible to save it
557 /// Should be overriten by the appropriate declaration space
559 public abstract TypeBuilder DefineType ();
562 /// Define all members, but don't apply any attributes or do anything which may
563 /// access not-yet-defined classes. This method also creates the MemberCache.
565 public abstract bool DefineMembers (TypeContainer parent);
568 // Whether this is an `unsafe context'
570 public bool UnsafeContext {
572 if ((ModFlags & Modifiers.UNSAFE) != 0)
575 return parent.UnsafeContext;
580 public static string MakeFQN (string nsn, string name)
584 return String.Concat (nsn, ".", name);
587 EmitContext type_resolve_ec;
588 EmitContext GetTypeResolveEmitContext (TypeContainer parent, Location loc)
590 type_resolve_ec = new EmitContext (parent, this, loc, null, null, ModFlags, false);
591 type_resolve_ec.ResolvingTypeTree = true;
593 return type_resolve_ec;
597 // Looks up the type, as parsed into the expression `e'
599 public Type ResolveType (Expression e, bool silent, Location loc)
601 TypeExpr d = ResolveTypeExpr (e, silent, loc);
605 return ResolveType (d, loc);
608 public Type ResolveType (TypeExpr d, Location loc)
610 if (!d.CheckAccessLevel (this)) {
611 Report. Error (122, loc, "`" + d.Name + "' " +
612 "is inaccessible because of its protection level");
616 Type t = d.ResolveType (type_resolve_ec);
620 TypeContainer tc = TypeManager.LookupTypeContainer (t);
621 if ((tc != null) && tc.IsGeneric) {
623 int tnum = TypeManager.GetNumberOfTypeArguments (t);
624 Report.Error (305, loc,
625 "Using the generic type `{0}' " +
626 "requires {1} type arguments",
627 TypeManager.GetFullName (t), tnum);
631 ConstructedType ctype = new ConstructedType (
632 t, TypeParameters, loc);
634 t = ctype.ResolveType (type_resolve_ec);
641 // Resolves the expression `e' for a type, and will recursively define
644 public TypeExpr ResolveTypeExpr (Expression e, bool silent, Location loc)
646 if (type_resolve_ec == null)
647 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
648 type_resolve_ec.loc = loc;
649 if (this is GenericMethod)
650 type_resolve_ec.ContainerType = Parent.TypeBuilder;
652 type_resolve_ec.ContainerType = TypeBuilder;
654 int errors = Report.Errors;
656 TypeExpr d = e.ResolveAsTypeTerminal (type_resolve_ec);
658 if ((d != null) && (d.eclass == ExprClass.Type))
661 if (silent || (Report.Errors != errors))
664 if (e is SimpleName){
665 SimpleName s = new SimpleName (((SimpleName) e).Name, loc);
666 d = s.ResolveAsTypeTerminal (type_resolve_ec);
668 if ((d == null) || (d.Type == null)) {
669 Report.Error (246, loc, "Cannot find type `{0}'", e);
673 int num_args = TypeManager.GetNumberOfTypeArguments (d.Type);
676 Report.Error (308, loc,
677 "The non-generic type `{0}' cannot " +
678 "be used with type arguments.",
679 TypeManager.CSharpName (d.Type));
683 Report.Error (305, loc,
684 "Using the generic type `{0}' " +
685 "requires {1} type arguments",
686 TypeManager.GetFullName (d.Type), num_args);
690 Report.Error (246, loc, "Cannot find type `{0}'", e);
694 public bool CheckAccessLevel (Type check_type)
697 if (this is GenericMethod)
698 tb = Parent.TypeBuilder;
702 if (check_type.IsGenericInstance)
703 check_type = check_type.GetGenericTypeDefinition ();
705 if (check_type == tb)
708 if (check_type.IsGenericParameter)
709 return true; // FIXME
711 TypeAttributes check_attr = check_type.Attributes & TypeAttributes.VisibilityMask;
714 // Broken Microsoft runtime, return public for arrays, no matter what
715 // the accessibility is for their underlying class, and they return
716 // NonPublic visibility for pointers
718 if (check_type.IsArray || check_type.IsPointer)
719 return CheckAccessLevel (TypeManager.GetElementType (check_type));
722 case TypeAttributes.Public:
725 case TypeAttributes.NotPublic:
727 // This test should probably use the declaringtype.
729 if (check_type.Assembly == tb.Assembly){
734 case TypeAttributes.NestedPublic:
737 case TypeAttributes.NestedPrivate:
738 string check_type_name = check_type.FullName;
739 string type_name = CurrentType != null ?
740 CurrentType.Name : tb.FullName;
742 int cio = check_type_name.LastIndexOf ('+');
743 string container = check_type_name.Substring (0, cio);
746 // Check if the check_type is a nested class of the current type
748 if (check_type_name.StartsWith (type_name + "+")){
752 if (type_name.StartsWith (container)){
758 case TypeAttributes.NestedFamily:
760 // Only accessible to methods in current type or any subtypes
762 return FamilyAccessible (tb, check_type);
764 case TypeAttributes.NestedFamANDAssem:
765 return (check_type.Assembly == tb.Assembly) &&
766 FamilyAccessible (tb, check_type);
768 case TypeAttributes.NestedFamORAssem:
769 return (check_type.Assembly == tb.Assembly) ||
770 FamilyAccessible (tb, check_type);
772 case TypeAttributes.NestedAssembly:
773 return check_type.Assembly == tb.Assembly;
776 Console.WriteLine ("HERE: " + check_attr);
781 protected bool FamilyAccessible (TypeBuilder tb, Type check_type)
783 Type declaring = check_type.DeclaringType;
784 if (tb.IsSubclassOf (declaring))
787 string check_type_name = check_type.FullName;
789 int cio = check_type_name.LastIndexOf ('+');
790 string container = check_type_name.Substring (0, cio);
793 // Check if the check_type is a nested class of the current type
795 if (check_type_name.StartsWith (container + "+"))
801 // Access level of a type.
803 enum AccessLevel { // Each column represents `is this scope larger or equal to Blah scope'
804 // Public Assembly Protected
805 Protected = (0 << 0) | (0 << 1) | (X << 2),
806 Public = (X << 0) | (X << 1) | (X << 2),
807 Private = (0 << 0) | (0 << 1) | (0 << 2),
808 Internal = (0 << 0) | (X << 1) | (0 << 2),
809 ProtectedOrInternal = (0 << 0) | (X << 1) | (X << 2),
812 static AccessLevel GetAccessLevelFromModifiers (int flags)
814 if ((flags & Modifiers.INTERNAL) != 0) {
816 if ((flags & Modifiers.PROTECTED) != 0)
817 return AccessLevel.ProtectedOrInternal;
819 return AccessLevel.Internal;
821 } else if ((flags & Modifiers.PROTECTED) != 0)
822 return AccessLevel.Protected;
823 else if ((flags & Modifiers.PRIVATE) != 0)
824 return AccessLevel.Private;
826 return AccessLevel.Public;
829 // What is the effective access level of this?
831 AccessLevel EffectiveAccessLevel {
833 AccessLevel myAccess = GetAccessLevelFromModifiers (ModFlags);
834 if (!IsTopLevel && (Parent != null))
835 return myAccess & Parent.EffectiveAccessLevel;
840 // Return the access level for type `t'
841 static AccessLevel TypeEffectiveAccessLevel (Type t)
844 return AccessLevel.Public;
845 if (t.IsNestedPrivate)
846 return AccessLevel.Private;
848 return AccessLevel.Internal;
850 // By now, it must be nested
851 AccessLevel parentLevel = TypeEffectiveAccessLevel (t.DeclaringType);
853 if (t.IsNestedPublic)
855 if (t.IsNestedAssembly)
856 return parentLevel & AccessLevel.Internal;
857 if (t.IsNestedFamily)
858 return parentLevel & AccessLevel.Protected;
859 if (t.IsNestedFamORAssem)
860 return parentLevel & AccessLevel.ProtectedOrInternal;
861 if (t.IsNestedFamANDAssem)
862 throw new NotImplementedException ("NestedFamANDAssem not implemented, cant make this kind of type from c# anyways");
864 // nested private is taken care of
866 throw new Exception ("I give up, what are you?");
870 // This answers `is the type P, as accessible as a member M which has the
871 // accessability @flags which is declared as a nested member of the type T, this declspace'
873 public bool AsAccessible (Type p, int flags)
875 if (p.IsGenericParameter)
876 return true; // FIXME
879 // 1) if M is private, its accessability is the same as this declspace.
880 // we already know that P is accessible to T before this method, so we
884 if ((flags & Modifiers.PRIVATE) != 0)
887 while (p.IsArray || p.IsPointer || p.IsByRef)
888 p = TypeManager.GetElementType (p);
890 AccessLevel pAccess = TypeEffectiveAccessLevel (p);
891 AccessLevel mAccess = this.EffectiveAccessLevel &
892 GetAccessLevelFromModifiers (flags);
894 // for every place from which we can access M, we must
895 // be able to access P as well. So, we want
896 // For every bit in M and P, M_i -> P_1 == true
897 // or, ~ (M -> P) == 0 <-> ~ ( ~M | P) == 0
899 return ~ (~ mAccess | pAccess) == 0;
902 static DoubleHash dh = new DoubleHash (1000);
904 Type DefineTypeAndParents (DeclSpace tc)
906 DeclSpace container = tc.Parent;
908 if (container.TypeBuilder == null && container.Name != "")
909 DefineTypeAndParents (container);
911 return tc.DefineType ();
914 Type LookupInterfaceOrClass (string ns, string name, out bool error)
922 if (dh.Lookup (ns, name, out r))
926 if (Namespace.IsNamespace (ns)){
927 string fullname = (ns != "") ? ns + "." + name : name;
928 t = TypeManager.LookupType (fullname);
932 t = TypeManager.LookupType (name);
936 dh.Insert (ns, name, t);
941 // In case we are fed a composite name, normalize it.
943 int p = name.LastIndexOf ('.');
945 ns = MakeFQN (ns, name.Substring (0, p));
946 name = name.Substring (p+1);
949 parent = RootContext.Tree.LookupByNamespace (ns, name);
950 if (parent == null) {
951 dh.Insert (ns, name, null);
955 t = DefineTypeAndParents (parent);
961 dh.Insert (ns, name, t);
965 public static void Error_AmbiguousTypeReference (Location loc, string name, string t1, string t2)
967 Report.Error (104, loc,
968 "`{0}' is an ambiguous reference ({1} or {2})",
972 public Type FindNestedType (Location loc, string name,
973 out DeclSpace containing_ds)
978 containing_ds = this;
979 while (containing_ds != null){
980 Type container_type = containing_ds.TypeBuilder;
981 Type current_type = container_type;
983 while (current_type != null && current_type != TypeManager.object_type) {
984 string pre = current_type.FullName;
986 t = LookupInterfaceOrClass (pre, name, out error);
990 if ((t != null) && containing_ds.CheckAccessLevel (t))
993 current_type = current_type.BaseType;
995 containing_ds = containing_ds.Parent;
1002 /// GetType is used to resolve type names at the DeclSpace level.
1003 /// Use this to lookup class/struct bases, interface bases or
1004 /// delegate type references
1008 /// Contrast this to LookupType which is used inside method bodies to
1009 /// lookup types that have already been defined. GetType is used
1010 /// during the tree resolution process and potentially define
1011 /// recursively the type
1013 public Type FindType (Location loc, string name)
1019 // For the case the type we are looking for is nested within this one
1020 // or is in any base class
1022 DeclSpace containing_ds = this;
1024 while (containing_ds != null){
1025 Type container_type = containing_ds.TypeBuilder;
1026 Type current_type = container_type;
1028 while (current_type != null && current_type != TypeManager.object_type) {
1029 string pre = current_type.FullName;
1031 t = LookupInterfaceOrClass (pre, name, out error);
1035 if ((t != null) && containing_ds.CheckAccessLevel (t))
1038 current_type = current_type.BaseType;
1040 containing_ds = containing_ds.Parent;
1044 // Attempt to lookup the class on our namespace and all it's implicit parents
1046 for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.ImplicitParent) {
1047 t = LookupInterfaceOrClass (ns.FullName, name, out error);
1056 // Attempt to do a direct unqualified lookup
1058 t = LookupInterfaceOrClass ("", name, out error);
1066 // Attempt to lookup the class on any of the `using'
1070 for (NamespaceEntry ns = NamespaceEntry; ns != null; ns = ns.Parent){
1072 t = LookupInterfaceOrClass (ns.FullName, name, out error);
1080 // Now check the using clause list
1083 foreach (Namespace using_ns in ns.GetUsingTable ()) {
1084 match = LookupInterfaceOrClass (using_ns.Name, name, out error);
1088 if (match != null) {
1090 if (CheckAccessLevel (match)) {
1091 Error_AmbiguousTypeReference (loc, name, t.FullName, match.FullName);
1104 //Report.Error (246, Location, "Can not find type `"+name+"'");
1109 /// This function is broken and not what you're looking for. It should only
1110 /// be used while the type is still being created since it doesn't use the cache
1111 /// and relies on the filter doing the member name check.
1113 public abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
1114 MemberFilter filter, object criteria);
1117 /// If we have a MemberCache, return it. This property may return null if the
1118 /// class doesn't have a member cache or while it's still being created.
1120 public abstract MemberCache MemberCache {
1125 // Extensions for generics
1127 TypeParameter[] type_params;
1128 TypeParameter[] type_param_list;
1130 protected string GetInstantiationName ()
1132 StringBuilder sb = new StringBuilder (Name);
1134 for (int i = 0; i < type_param_list.Length; i++) {
1137 sb.Append (type_param_list [i].Name);
1140 return sb.ToString ();
1143 bool check_type_parameter (ArrayList list, int start, string name)
1145 for (int i = 0; i < start; i++) {
1146 TypeParameter param = (TypeParameter) list [i];
1148 if (param.Name != name)
1151 if (RootContext.WarningLevel >= 3)
1154 "Type parameter `{0}' has same name " +
1155 "as type parameter from outer type `{1}'",
1156 name, parent.GetInstantiationName ());
1164 TypeParameter[] initialize_type_params ()
1166 if (type_param_list != null)
1167 return type_param_list;
1169 DeclSpace the_parent = parent;
1170 if (this is GenericMethod)
1171 the_parent = the_parent.Parent;
1174 TypeParameter[] parent_params = null;
1175 if ((the_parent != null) && the_parent.IsGeneric) {
1176 parent_params = the_parent.initialize_type_params ();
1177 start = parent_params != null ? parent_params.Length : 0;
1180 ArrayList list = new ArrayList ();
1181 if (parent_params != null)
1182 list.AddRange (parent_params);
1184 int count = type_params != null ? type_params.Length : 0;
1185 for (int i = 0; i < count; i++) {
1186 TypeParameter param = type_params [i];
1187 check_type_parameter (list, start, param.Name);
1191 type_param_list = new TypeParameter [list.Count];
1192 list.CopyTo (type_param_list, 0);
1193 return type_param_list;
1196 public AdditionResult SetParameterInfo (ArrayList constraints_list)
1199 if (constraints_list != null) {
1201 80, Location, "Contraints are not allowed " +
1202 "on non-generic declarations");
1203 return AdditionResult.Error;
1206 return AdditionResult.Success;
1209 type_params = new TypeParameter [MemberName.TypeParameters.Length];
1212 // Register all the names
1214 for (int i = 0; i < MemberName.TypeParameters.Length; i++) {
1215 string name = MemberName.TypeParameters [i];
1217 AdditionResult res = IsValid (name, name);
1219 if (res != AdditionResult.Success)
1222 Constraints constraints = null;
1223 if (constraints_list != null) {
1224 foreach (Constraints constraint in constraints_list) {
1225 if (constraint.TypeParameter == name) {
1226 constraints = constraint;
1232 type_params [i] = new TypeParameter (name, constraints, Location);
1234 DefineName (name, type_params [i]);
1237 return AdditionResult.Success;
1240 public TypeParameter[] TypeParameters {
1243 throw new InvalidOperationException ();
1244 if (type_param_list == null)
1245 initialize_type_params ();
1247 return type_param_list;
1251 protected TypeParameter[] CurrentTypeParameters {
1254 throw new InvalidOperationException ();
1255 if (type_params != null)
1258 return new TypeParameter [0];
1262 public int CountTypeParameters {
1264 return count_type_params;
1268 public TypeParameterExpr LookupGeneric (string name, Location loc)
1273 foreach (TypeParameter type_param in CurrentTypeParameters) {
1274 if (type_param.Name != name)
1277 return new TypeParameterExpr (type_param, loc);
1281 return parent.LookupGeneric (name, loc);
1286 bool IAlias.IsType {
1287 get { return true; }
1290 string IAlias.Name {
1291 get { return Name; }
1294 TypeExpr IAlias.Type
1297 if (TypeBuilder == null)
1298 throw new InvalidOperationException ();
1300 if (CurrentType != null)
1303 return new TypeExpression (TypeBuilder, Location);
1309 /// This is a readonly list of MemberInfo's.
1311 public class MemberList : IList {
1312 public readonly IList List;
1316 /// Create a new MemberList from the given IList.
1318 public MemberList (IList list)
1323 this.List = new ArrayList ();
1328 /// Concatenate the ILists `first' and `second' to a new MemberList.
1330 public MemberList (IList first, IList second)
1332 ArrayList list = new ArrayList ();
1333 list.AddRange (first);
1334 list.AddRange (second);
1339 public static readonly MemberList Empty = new MemberList (new ArrayList ());
1342 /// Cast the MemberList into a MemberInfo[] array.
1345 /// This is an expensive operation, only use it if it's really necessary.
1347 public static explicit operator MemberInfo [] (MemberList list)
1349 Timer.StartTimer (TimerType.MiscTimer);
1350 MemberInfo [] result = new MemberInfo [list.Count];
1351 list.CopyTo (result, 0);
1352 Timer.StopTimer (TimerType.MiscTimer);
1364 public bool IsSynchronized {
1366 return List.IsSynchronized;
1370 public object SyncRoot {
1372 return List.SyncRoot;
1376 public void CopyTo (Array array, int index)
1378 List.CopyTo (array, index);
1383 public IEnumerator GetEnumerator ()
1385 return List.GetEnumerator ();
1390 public bool IsFixedSize {
1396 public bool IsReadOnly {
1402 object IList.this [int index] {
1404 return List [index];
1408 throw new NotSupportedException ();
1412 // FIXME: try to find out whether we can avoid the cast in this indexer.
1413 public MemberInfo this [int index] {
1415 return (MemberInfo) List [index];
1419 public int Add (object value)
1421 throw new NotSupportedException ();
1424 public void Clear ()
1426 throw new NotSupportedException ();
1429 public bool Contains (object value)
1431 return List.Contains (value);
1434 public int IndexOf (object value)
1436 return List.IndexOf (value);
1439 public void Insert (int index, object value)
1441 throw new NotSupportedException ();
1444 public void Remove (object value)
1446 throw new NotSupportedException ();
1449 public void RemoveAt (int index)
1451 throw new NotSupportedException ();
1456 /// This interface is used to get all members of a class when creating the
1457 /// member cache. It must be implemented by all DeclSpace derivatives which
1458 /// want to support the member cache and by TypeHandle to get caching of
1459 /// non-dynamic types.
1461 public interface IMemberContainer {
1463 /// The name of the IMemberContainer. This is only used for
1464 /// debugging purposes.
1471 /// The type of this IMemberContainer.
1478 /// Returns the IMemberContainer of the parent class or null if this
1479 /// is an interface or TypeManger.object_type.
1480 /// This is used when creating the member cache for a class to get all
1481 /// members from the parent class.
1483 IMemberContainer Parent {
1488 /// Whether this is an interface.
1495 /// Returns all members of this class with the corresponding MemberTypes
1496 /// and BindingFlags.
1499 /// When implementing this method, make sure not to return any inherited
1500 /// members and check the MemberTypes and BindingFlags properly.
1501 /// Unfortunately, System.Reflection is lame and doesn't provide a way to
1502 /// get the BindingFlags (static/non-static,public/non-public) in the
1503 /// MemberInfo class, but the cache needs this information. That's why
1504 /// this method is called multiple times with different BindingFlags.
1506 MemberList GetMembers (MemberTypes mt, BindingFlags bf);
1509 /// Return the container's member cache.
1511 MemberCache MemberCache {
1517 /// The MemberCache is used by dynamic and non-dynamic types to speed up
1518 /// member lookups. It has a member name based hash table; it maps each member
1519 /// name to a list of CacheEntry objects. Each CacheEntry contains a MemberInfo
1520 /// and the BindingFlags that were initially used to get it. The cache contains
1521 /// all members of the current class and all inherited members. If this cache is
1522 /// for an interface types, it also contains all inherited members.
1524 /// There are two ways to get a MemberCache:
1525 /// * if this is a dynamic type, lookup the corresponding DeclSpace and then
1526 /// use the DeclSpace.MemberCache property.
1527 /// * if this not a dynamic type, call TypeHandle.GetTypeHandle() to get a
1528 /// TypeHandle instance for the type and then use TypeHandle.MemberCache.
1530 public class MemberCache {
1531 public readonly IMemberContainer Container;
1532 protected Hashtable member_hash;
1533 protected Hashtable method_hash;
1535 Hashtable interface_hash;
1538 /// Create a new MemberCache for the given IMemberContainer `container'.
1540 public MemberCache (IMemberContainer container)
1542 this.Container = container;
1544 Timer.IncrementCounter (CounterType.MemberCache);
1545 Timer.StartTimer (TimerType.CacheInit);
1549 // If we have a parent class (we have a parent class unless we're
1550 // TypeManager.object_type), we deep-copy its MemberCache here.
1551 if (Container.IsInterface) {
1553 interface_hash = new Hashtable ();
1555 if (Container.Parent != null)
1556 parent = Container.Parent.MemberCache;
1558 parent = TypeHandle.ObjectType.MemberCache;
1559 member_hash = SetupCacheForInterface (parent);
1560 } else if (Container.Parent != null)
1561 member_hash = SetupCache (Container.Parent.MemberCache);
1563 member_hash = new Hashtable ();
1565 // If this is neither a dynamic type nor an interface, create a special
1566 // method cache with all declared and inherited methods.
1567 Type type = container.Type;
1568 if (!(type is TypeBuilder) && !type.IsInterface && !type.IsGenericParameter) {
1569 method_hash = new Hashtable ();
1573 // Add all members from the current class.
1574 AddMembers (Container);
1576 Timer.StopTimer (TimerType.CacheInit);
1580 /// Bootstrap this member cache by doing a deep-copy of our parent.
1582 Hashtable SetupCache (MemberCache parent)
1584 Hashtable hash = new Hashtable ();
1586 IDictionaryEnumerator it = parent.member_hash.GetEnumerator ();
1587 while (it.MoveNext ()) {
1588 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1596 /// Add the contents of `new_hash' to `hash'.
1598 void AddHashtable (Hashtable hash, Hashtable new_hash)
1600 IDictionaryEnumerator it = new_hash.GetEnumerator ();
1601 while (it.MoveNext ()) {
1602 ArrayList list = (ArrayList) hash [it.Key];
1604 list.AddRange ((ArrayList) it.Value);
1606 hash [it.Key] = ((ArrayList) it.Value).Clone ();
1611 /// Bootstrap the member cache for an interface type.
1612 /// Type.GetMembers() won't return any inherited members for interface types,
1613 /// so we need to do this manually. Interfaces also inherit from System.Object.
1615 Hashtable SetupCacheForInterface (MemberCache parent)
1617 Hashtable hash = SetupCache (parent);
1618 TypeExpr [] ifaces = TypeManager.GetInterfaces (Container.Type);
1620 foreach (TypeExpr iface in ifaces) {
1621 Type itype = iface.Type;
1623 if (interface_hash.Contains (itype))
1626 interface_hash [itype] = null;
1628 IMemberContainer iface_container =
1629 TypeManager.LookupMemberContainer (itype);
1631 MemberCache iface_cache = iface_container.MemberCache;
1633 AddHashtable (hash, iface_cache.member_hash);
1635 if (iface_cache.interface_hash == null)
1638 foreach (Type parent_contains in iface_cache.interface_hash.Keys)
1639 interface_hash [parent_contains] = null;
1646 /// Add all members from class `container' to the cache.
1648 void AddMembers (IMemberContainer container)
1650 // We need to call AddMembers() with a single member type at a time
1651 // to get the member type part of CacheEntry.EntryType right.
1652 AddMembers (MemberTypes.Constructor, container);
1653 AddMembers (MemberTypes.Field, container);
1654 AddMembers (MemberTypes.Method, container);
1655 AddMembers (MemberTypes.Property, container);
1656 AddMembers (MemberTypes.Event, container);
1657 // Nested types are returned by both Static and Instance searches.
1658 AddMembers (MemberTypes.NestedType,
1659 BindingFlags.Static | BindingFlags.Public, container);
1660 AddMembers (MemberTypes.NestedType,
1661 BindingFlags.Static | BindingFlags.NonPublic, container);
1664 void AddMembers (MemberTypes mt, IMemberContainer container)
1666 AddMembers (mt, BindingFlags.Static | BindingFlags.Public, container);
1667 AddMembers (mt, BindingFlags.Static | BindingFlags.NonPublic, container);
1668 AddMembers (mt, BindingFlags.Instance | BindingFlags.Public, container);
1669 AddMembers (mt, BindingFlags.Instance | BindingFlags.NonPublic, container);
1673 /// Add all members from class `container' with the requested MemberTypes and
1674 /// BindingFlags to the cache. This method is called multiple times with different
1675 /// MemberTypes and BindingFlags.
1677 void AddMembers (MemberTypes mt, BindingFlags bf, IMemberContainer container)
1679 MemberList members = container.GetMembers (mt, bf);
1681 foreach (MemberInfo member in members) {
1682 string name = member.Name;
1684 int pos = name.IndexOf ('<');
1686 name = name.Substring (0, pos);
1688 // We use a name-based hash table of ArrayList's.
1689 ArrayList list = (ArrayList) member_hash [name];
1691 list = new ArrayList ();
1692 member_hash.Add (name, list);
1695 // When this method is called for the current class, the list will
1696 // already contain all inherited members from our parent classes.
1697 // We cannot add new members in front of the list since this'd be an
1698 // expensive operation, that's why the list is sorted in reverse order
1699 // (ie. members from the current class are coming last).
1700 list.Add (new CacheEntry (container, member, mt, bf));
1705 /// Add all declared and inherited methods from class `type' to the method cache.
1707 void AddMethods (Type type)
1709 AddMethods (BindingFlags.Static | BindingFlags.Public |
1710 BindingFlags.FlattenHierarchy, type);
1711 AddMethods (BindingFlags.Static | BindingFlags.NonPublic |
1712 BindingFlags.FlattenHierarchy, type);
1713 AddMethods (BindingFlags.Instance | BindingFlags.Public, type);
1714 AddMethods (BindingFlags.Instance | BindingFlags.NonPublic, type);
1717 void AddMethods (BindingFlags bf, Type type)
1719 MemberInfo [] members = type.GetMethods (bf);
1721 Array.Reverse (members);
1723 foreach (MethodBase member in members) {
1724 string name = member.Name;
1726 // Varargs methods aren't allowed in C# code.
1727 if ((member.CallingConvention & CallingConventions.VarArgs) != 0)
1730 // We use a name-based hash table of ArrayList's.
1731 ArrayList list = (ArrayList) method_hash [name];
1733 list = new ArrayList ();
1734 method_hash.Add (name, list);
1737 // Unfortunately, the elements returned by Type.GetMethods() aren't
1738 // sorted so we need to do this check for every member.
1739 BindingFlags new_bf = bf;
1740 if (member.DeclaringType == type)
1741 new_bf |= BindingFlags.DeclaredOnly;
1743 list.Add (new CacheEntry (Container, member, MemberTypes.Method, new_bf));
1748 /// Compute and return a appropriate `EntryType' magic number for the given
1749 /// MemberTypes and BindingFlags.
1751 protected static EntryType GetEntryType (MemberTypes mt, BindingFlags bf)
1753 EntryType type = EntryType.None;
1755 if ((mt & MemberTypes.Constructor) != 0)
1756 type |= EntryType.Constructor;
1757 if ((mt & MemberTypes.Event) != 0)
1758 type |= EntryType.Event;
1759 if ((mt & MemberTypes.Field) != 0)
1760 type |= EntryType.Field;
1761 if ((mt & MemberTypes.Method) != 0)
1762 type |= EntryType.Method;
1763 if ((mt & MemberTypes.Property) != 0)
1764 type |= EntryType.Property;
1765 // Nested types are returned by static and instance searches.
1766 if ((mt & MemberTypes.NestedType) != 0)
1767 type |= EntryType.NestedType | EntryType.Static | EntryType.Instance;
1769 if ((bf & BindingFlags.Instance) != 0)
1770 type |= EntryType.Instance;
1771 if ((bf & BindingFlags.Static) != 0)
1772 type |= EntryType.Static;
1773 if ((bf & BindingFlags.Public) != 0)
1774 type |= EntryType.Public;
1775 if ((bf & BindingFlags.NonPublic) != 0)
1776 type |= EntryType.NonPublic;
1777 if ((bf & BindingFlags.DeclaredOnly) != 0)
1778 type |= EntryType.Declared;
1784 /// The `MemberTypes' enumeration type is a [Flags] type which means that it may
1785 /// denote multiple member types. Returns true if the given flags value denotes a
1786 /// single member types.
1788 public static bool IsSingleMemberType (MemberTypes mt)
1791 case MemberTypes.Constructor:
1792 case MemberTypes.Event:
1793 case MemberTypes.Field:
1794 case MemberTypes.Method:
1795 case MemberTypes.Property:
1796 case MemberTypes.NestedType:
1805 /// We encode the MemberTypes and BindingFlags of each members in a "magic"
1806 /// number to speed up the searching process.
1809 protected enum EntryType {
1814 MaskStatic = Instance|Static,
1818 MaskProtection = Public|NonPublic,
1822 Constructor = 0x020,
1829 MaskType = Constructor|Event|Field|Method|Property|NestedType
1832 protected struct CacheEntry {
1833 public readonly IMemberContainer Container;
1834 public readonly EntryType EntryType;
1835 public readonly MemberInfo Member;
1837 public CacheEntry (IMemberContainer container, MemberInfo member,
1838 MemberTypes mt, BindingFlags bf)
1840 this.Container = container;
1841 this.Member = member;
1842 this.EntryType = GetEntryType (mt, bf);
1847 /// This is called each time we're walking up one level in the class hierarchy
1848 /// and checks whether we can abort the search since we've already found what
1849 /// we were looking for.
1851 protected bool DoneSearching (ArrayList list)
1854 // We've found exactly one member in the current class and it's not
1855 // a method or constructor.
1857 if (list.Count == 1 && !(list [0] is MethodBase))
1861 // Multiple properties: we query those just to find out the indexer
1864 if ((list.Count > 0) && (list [0] is PropertyInfo))
1871 /// Looks up members with name `name'. If you provide an optional
1872 /// filter function, it'll only be called with members matching the
1873 /// requested member name.
1875 /// This method will try to use the cache to do the lookup if possible.
1877 /// Unlike other FindMembers implementations, this method will always
1878 /// check all inherited members - even when called on an interface type.
1880 /// If you know that you're only looking for methods, you should use
1881 /// MemberTypes.Method alone since this speeds up the lookup a bit.
1882 /// When doing a method-only search, it'll try to use a special method
1883 /// cache (unless it's a dynamic type or an interface) and the returned
1884 /// MemberInfo's will have the correct ReflectedType for inherited methods.
1885 /// The lookup process will automatically restart itself in method-only
1886 /// search mode if it discovers that it's about to return methods.
1888 ArrayList global = new ArrayList ();
1889 bool using_global = false;
1891 public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name,
1892 MemberFilter filter, object criteria)
1895 throw new Exception ();
1897 bool declared_only = (bf & BindingFlags.DeclaredOnly) != 0;
1898 bool method_search = mt == MemberTypes.Method;
1899 // If we have a method cache and we aren't already doing a method-only search,
1900 // then we restart a method search if the first match is a method.
1901 bool do_method_search = !method_search && (method_hash != null);
1903 ArrayList applicable;
1905 // If this is a method-only search, we try to use the method cache if
1906 // possible; a lookup in the method cache will return a MemberInfo with
1907 // the correct ReflectedType for inherited methods.
1909 if (method_search && (method_hash != null))
1910 applicable = (ArrayList) method_hash [name];
1912 applicable = (ArrayList) member_hash [name];
1914 if (applicable == null)
1915 return MemberList.Empty;
1918 // 32 slots gives 53 rss/54 size
1919 // 2/4 slots gives 55 rss
1921 // Strange: from 25,000 calls, only 1,800
1922 // are above 2. Why does this impact it?
1925 using_global = true;
1927 Timer.StartTimer (TimerType.CachedLookup);
1929 EntryType type = GetEntryType (mt, bf);
1931 IMemberContainer current = Container;
1933 // `applicable' is a list of all members with the given member name `name'
1934 // in the current class and all its parent classes. The list is sorted in
1935 // reverse order due to the way how the cache is initialy created (to speed
1936 // things up, we're doing a deep-copy of our parent).
1938 for (int i = applicable.Count-1; i >= 0; i--) {
1939 CacheEntry entry = (CacheEntry) applicable [i];
1941 // This happens each time we're walking one level up in the class
1942 // hierarchy. If we're doing a DeclaredOnly search, we must abort
1943 // the first time this happens (this may already happen in the first
1944 // iteration of this loop if there are no members with the name we're
1945 // looking for in the current class).
1946 if (entry.Container != current) {
1947 if (declared_only || DoneSearching (global))
1950 current = entry.Container;
1953 // Is the member of the correct type ?
1954 if ((entry.EntryType & type & EntryType.MaskType) == 0)
1957 // Is the member static/non-static ?
1958 if ((entry.EntryType & type & EntryType.MaskStatic) == 0)
1961 // Apply the filter to it.
1962 if (filter (entry.Member, criteria)) {
1963 if ((entry.EntryType & EntryType.MaskType) != EntryType.Method)
1964 do_method_search = false;
1965 global.Add (entry.Member);
1969 Timer.StopTimer (TimerType.CachedLookup);
1971 // If we have a method cache and we aren't already doing a method-only
1972 // search, we restart in method-only search mode if the first match is
1973 // a method. This ensures that we return a MemberInfo with the correct
1974 // ReflectedType for inherited methods.
1975 if (do_method_search && (global.Count > 0)){
1976 using_global = false;
1978 return FindMembers (MemberTypes.Method, bf, name, filter, criteria);
1981 using_global = false;
1982 MemberInfo [] copy = new MemberInfo [global.Count];
1983 global.CopyTo (copy);
1984 return new MemberList (copy);
1988 // This finds the method or property for us to override. invocationType is the type where
1989 // the override is going to be declared, name is the name of the method/property, and
1990 // paramTypes is the parameters, if any to the method or property
1992 // Because the MemberCache holds members from this class and all the base classes,
1993 // we can avoid tons of reflection stuff.
1995 public MemberInfo FindMemberToOverride (Type invocationType, string name, Type [] paramTypes, bool is_property)
1997 ArrayList applicable;
1998 if (method_hash != null && !is_property)
1999 applicable = (ArrayList) method_hash [name];
2001 applicable = (ArrayList) member_hash [name];
2003 if (applicable == null)
2006 // Walk the chain of methods, starting from the top.
2008 for (int i = applicable.Count - 1; i >= 0; i--) {
2009 CacheEntry entry = (CacheEntry) applicable [i];
2011 if ((entry.EntryType & (is_property ? EntryType.Property : EntryType.Method)) == 0)
2014 PropertyInfo pi = null;
2015 MethodInfo mi = null;
2019 pi = (PropertyInfo) entry.Member;
2020 cmpAttrs = TypeManager.GetArgumentTypes (pi);
2022 mi = (MethodInfo) entry.Member;
2023 cmpAttrs = TypeManager.GetArgumentTypes (mi);
2027 // Check the arguments
2029 if (cmpAttrs.Length != paramTypes.Length)
2032 for (int j = cmpAttrs.Length - 1; j >= 0; j --) {
2033 if (!paramTypes [j].Equals (cmpAttrs [j]))
2038 // get one of the methods because this has the visibility info.
2041 mi = pi.GetGetMethod (true);
2043 mi = pi.GetSetMethod (true);
2049 switch (mi.Attributes & MethodAttributes.MemberAccessMask) {
2050 case MethodAttributes.Private:
2052 // A private method is Ok if we are a nested subtype.
2053 // The spec actually is not very clear about this, see bug 52458.
2055 if (invocationType == entry.Container.Type ||
2056 TypeManager.IsNestedChildOf (invocationType, entry.Container.Type))
2057 return entry.Member;
2060 case MethodAttributes.FamANDAssem:
2061 case MethodAttributes.Assembly:
2063 // Check for assembly methods
2065 if (mi.DeclaringType.Assembly == CodeGen.Assembly.Builder)
2066 return entry.Member;
2071 // A protected method is ok, because we are overriding.
2072 // public is always ok.
2074 return entry.Member;