X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Ftypemanager.cs;h=f6fda8ca320dbd3b11d7622987e266f067667623;hb=845496d2057c292342d4120498bba6caecfc95e5;hp=ace12a5a4d4057fa3b6e7a48e096c64f855d473e;hpb=af101b7583a809e7bf748a4e7b887c378216249f;p=mono.git diff --git a/mcs/mcs/typemanager.cs b/mcs/mcs/typemanager.cs index ace12a5a4d4..f6fda8ca320 100755 --- a/mcs/mcs/typemanager.cs +++ b/mcs/mcs/typemanager.cs @@ -9,9 +9,16 @@ // (C) 2001 Ximian, Inc (http://www.ximian.com) // // -#define CACHE + +// +// We will eventually remove the SIMPLE_SPEEDUP, and should never change +// the behavior of the compilation. This can be removed if we rework +// the code to get a list of namespaces available. +// +#define SIMPLE_SPEEDUP using System; +using System.IO; using System.Globalization; using System.Collections; using System.Reflection; @@ -67,13 +74,34 @@ public class TypeManager { static public Type methodimpl_attr_type; static public Type marshal_as_attr_type; static public Type param_array_type; + static public Type guid_attr_type; static public Type void_ptr_type; static public Type indexer_name_type; + static public Type exception_type; static public object obsolete_attribute_type; static public object conditional_attribute_type; + // + // An empty array of types + // static public Type [] NoTypes; + + // + // Expressions representing the internal types. Used during declaration + // definition. + // + static public Expression system_object_expr, system_string_expr; + static public Expression system_boolean_expr, system_decimal_expr; + static public Expression system_single_expr, system_double_expr; + static public Expression system_sbyte_expr, system_byte_expr; + static public Expression system_int16_expr, system_uint16_expr; + static public Expression system_int32_expr, system_uint32_expr; + static public Expression system_int64_expr, system_uint64_expr; + static public Expression system_char_expr, system_void_expr; + static public Expression system_asynccallback_expr; + static public Expression system_iasyncresult_expr; + // // This is only used when compiling corlib // @@ -100,6 +128,8 @@ public class TypeManager { // These methods are called by code generated by the compiler // static public MethodInfo string_concat_string_string; + static public MethodInfo string_concat_string_string_string; + static public MethodInfo string_concat_string_string_string_string; static public MethodInfo string_concat_object_object; static public MethodInfo string_isinterneted_string; static public MethodInfo system_type_get_type_from_handle; @@ -159,10 +189,7 @@ public class TypeManager { // static ArrayList user_types; - // - // Keeps a mapping between TypeBuilders and their TypeContainers - // - static PtrHashtable builder_to_container; + static PtrHashtable builder_to_declspace; // // Tracks the interfaces implemented by typebuilders. We only @@ -176,31 +203,29 @@ public class TypeManager { // static Hashtable method_arguments; + // + // Maps PropertyBuilder to a Type array that contains + // the arguments to the indexer + // + static Hashtable indexer_arguments; + // // Maybe `method_arguments' should be replaced and only // method_internal_params should be kept? // static Hashtable method_internal_params; - static PtrHashtable builder_to_interface; - - // - // Keeps track of delegate types - // - - static Hashtable builder_to_delegate; - // - // Keeps track of enum types + // Keeps track of attribute types // - static Hashtable builder_to_enum; + static Hashtable builder_to_attr; // - // Keeps track of attribute types + // Keeps track of methods // - static Hashtable builder_to_attr; + static Hashtable builder_to_method; struct Signature { public string name; @@ -250,6 +275,32 @@ public class TypeManager { // A delegate that points to the filter above. static MemberFilter signature_filter; + // + // These are expressions that represent some of the internal data types, used + // elsewhere + // + static void InitExpressionTypes () + { + system_object_expr = new TypeLookupExpression ("System.Object"); + system_string_expr = new TypeLookupExpression ("System.String"); + system_boolean_expr = new TypeLookupExpression ("System.Boolean"); + system_decimal_expr = new TypeLookupExpression ("System.Decimal"); + system_single_expr = new TypeLookupExpression ("System.Single"); + system_double_expr = new TypeLookupExpression ("System.Double"); + system_sbyte_expr = new TypeLookupExpression ("System.SByte"); + system_byte_expr = new TypeLookupExpression ("System.Byte"); + system_int16_expr = new TypeLookupExpression ("System.Int16"); + system_uint16_expr = new TypeLookupExpression ("System.UInt16"); + system_int32_expr = new TypeLookupExpression ("System.Int32"); + system_uint32_expr = new TypeLookupExpression ("System.UInt32"); + system_int64_expr = new TypeLookupExpression ("System.Int64"); + system_uint64_expr = new TypeLookupExpression ("System.UInt64"); + system_char_expr = new TypeLookupExpression ("System.Char"); + system_void_expr = new TypeLookupExpression ("System.Void"); + system_asynccallback_expr = new TypeLookupExpression ("System.AsyncCallback"); + system_iasyncresult_expr = new TypeLookupExpression ("System.IAsyncResult"); + } + static TypeManager () { assemblies = new Assembly [0]; @@ -259,18 +310,18 @@ public class TypeManager { types = new Hashtable (); typecontainers = new Hashtable (); - builder_to_interface = new PtrHashtable (); - builder_to_delegate = new PtrHashtable (); - builder_to_enum = new PtrHashtable (); + builder_to_declspace = new PtrHashtable (); builder_to_attr = new PtrHashtable (); + builder_to_method = new PtrHashtable (); method_arguments = new PtrHashtable (); method_internal_params = new PtrHashtable (); - builder_to_container = new PtrHashtable (); + indexer_arguments = new PtrHashtable (); builder_to_ifaces = new PtrHashtable (); NoTypes = new Type [0]; signature_filter = new MemberFilter (SignatureFilter); + InitExpressionTypes (); } public static void AddUserType (string name, TypeBuilder t, Type [] ifaces) @@ -279,7 +330,7 @@ public class TypeManager { types.Add (name, t); } catch { Type prev = (Type) types [name]; - TypeContainer tc = (TypeContainer) builder_to_container [prev]; + TypeContainer tc = builder_to_declspace [prev] as TypeContainer; if (tc != null){ // @@ -289,7 +340,7 @@ public class TypeManager { return; } - tc = (TypeContainer) builder_to_container [t]; + tc = builder_to_declspace [t] as TypeContainer; Report.Warning ( 1595, "The type `" + name + "' is defined in an existing assembly;"+ @@ -316,7 +367,7 @@ public class TypeManager { public static void AddUserType (string name, TypeBuilder t, TypeContainer tc, Type [] ifaces) { - builder_to_container.Add (t, tc); + builder_to_declspace.Add (t, tc); typecontainers.Add (name, tc); AddUserType (name, t, ifaces); } @@ -324,48 +375,64 @@ public class TypeManager { public static void AddDelegateType (string name, TypeBuilder t, Delegate del) { types.Add (name, t); - builder_to_delegate.Add (t, del); + builder_to_declspace.Add (t, del); } public static void AddEnumType (string name, TypeBuilder t, Enum en) { types.Add (name, t); - builder_to_enum.Add (t, en); + builder_to_declspace.Add (t, en); } public static void AddUserInterface (string name, TypeBuilder t, Interface i, Type [] ifaces) { AddUserType (name, t, ifaces); - builder_to_interface.Add (t, i); + builder_to_declspace.Add (t, i); + } + + public static void AddMethod (MethodBuilder builder, MethodData method) + { + builder_to_method.Add (builder, method); } public static void RegisterAttrType (Type t, TypeContainer tc) { builder_to_attr.Add (t, tc); } - + /// /// Returns the TypeContainer whose Type is `t' or null if there is no /// TypeContainer for `t' (ie, the Type comes from a library) /// public static TypeContainer LookupTypeContainer (Type t) { - return (TypeContainer) builder_to_container [t]; + return builder_to_declspace [t] as TypeContainer; + } + + public static IMemberContainer LookupMemberContainer (Type t) + { + if (t is TypeBuilder) { + IMemberContainer container = builder_to_declspace [t] as IMemberContainer; + if (container != null) + return container; + } + + return TypeHandle.GetTypeHandle (t); } public static Interface LookupInterface (Type t) { - return (Interface) builder_to_interface [t]; + return builder_to_declspace [t] as Interface; } public static Delegate LookupDelegate (Type t) { - return (Delegate) builder_to_delegate [t]; + return builder_to_declspace [t] as Delegate; } public static Enum LookupEnum (Type t) { - return (Enum) builder_to_enum [t]; + return builder_to_declspace [t] as Enum; } public static TypeContainer LookupAttr (Type t) @@ -401,8 +468,56 @@ public class TypeManager { modules = n; } + // + // Low-level lookup, cache-less + // + static Type LookupTypeReflection (string name) + { + Type t; + + foreach (Assembly a in assemblies){ + t = a.GetType (name); + if (t != null) + return t; + } + + foreach (ModuleBuilder mb in modules) { + t = mb.GetType (name); + if (t != null){ + return t; + } + } + return null; + } + + static Hashtable negative_hits = new Hashtable (); + + // + // This function is used when you want to avoid the lookups, and want to go + // directly to the source. This will use the cache. + // + // Notice that bypassing the cache is bad, because on Microsoft.NET runtime + // GetType ("DynamicType[]") != GetType ("DynamicType[]"), and there is no + // way to test things other than doing a fullname compare + // + public static Type LookupTypeDirect (string name) + { + Type t = (Type) types [name]; + if (t != null) + return t; + + t = LookupTypeReflection (name); + if (t == null) + return null; + + types [name] = t; + return t; + } + /// - /// Returns the Type associated with @name + /// Returns the Type associated with @name, takes care of the fact that + /// reflection expects nested types to be separated from the main type + /// with a "+" instead of a "." /// public static Type LookupType (string name) { @@ -416,26 +531,210 @@ public class TypeManager { if (t != null) return t; - foreach (Assembly a in assemblies){ - t = a.GetType (name); - if (t != null){ - types [name] = t; +#if SIMPLE_SPEEDUP + if (negative_hits.Contains (name)) + return null; +#endif + + // + // Optimization: ComposedCast will work with an existing type, and might already have the + // full name of the type, so the full system lookup can probably be avoided. + // + + string [] elements = name.Split ('.'); + int count = elements.Length; + for (int n = 1; n <= count; n++){ + string top_level_type = String.Join (".", elements, 0, n); + + t = (Type) types [top_level_type]; + if (t == null){ + t = LookupTypeReflection (top_level_type); + if (t == null) + continue; + } + + if (count == n){ + types [name] = t; return t; + } + + // + // We know that System.Object does not have children, and since its the parent of + // all the objects, it always gets probbed for inner classes. + // + if (top_level_type == "System.Object") + return null; + + string newt = top_level_type + "+" + String.Join ("+", elements, n, count - n); + t = LookupTypeDirect (newt); + if (t != null) + types [newt] = t; + return t; + } + +#if SIMPLE_SPEEDUP + negative_hits [name] = true; +#endif + return null; + } + + static Hashtable assemblies_namespaces = new Hashtable (); + + // + // Returns a list of all namespaces in the assemblies and types loaded. + // + static Hashtable ExtractAssemblyNamespaces () + { + foreach (Assembly a in assemblies){ + foreach (Type t in a.GetTypes ()){ + string ns = t.Namespace; + + if (assemblies_namespaces.Contains (ns)) + continue; + assemblies_namespaces [ns] = ns; } } - foreach (ModuleBuilder mb in modules) { - t = mb.GetType (name); - if (t != null) { - types [name] = t; - return t; + return assemblies_namespaces; + } + + static Hashtable AddModuleNamespaces (Hashtable h) + { + foreach (ModuleBuilder mb in modules){ + foreach (Type t in mb.GetTypes ()){ + string ns = t.Namespace; + + if (h.Contains (ns)) + continue; + h [ns] = ns; } } + return h; + } + + + /// + /// Returns the list of namespaces that are active for this executable + /// + public static Hashtable GetAssemblyNamespaces (string executable_name) + { + string cache_name = executable_name + ".nsc"; + Hashtable cached_namespaces = LoadCache (cache_name); + + if (cached_namespaces != null) + assemblies_namespaces = cached_namespaces; + else { + Console.WriteLine ("rebuilding namespace cache"); + assemblies_namespaces = ExtractAssemblyNamespaces (); + SaveCache (cache_name); + } + + return assemblies_namespaces; + } + + public static Hashtable GetNamespaces () + { + if (assemblies_namespaces == null) + assemblies_namespaces = ExtractAssemblyNamespaces (); + + Hashtable nh = (Hashtable) assemblies_namespaces.Clone (); + + return AddModuleNamespaces (nh); + } + + // + // Loads the namespace cache for the given executable name + // + static Hashtable LoadCache (string cache_file) + { + if (!File.Exists (cache_file)){ + Console.WriteLine ("Cache not found"); + return null; + } + + Hashtable cached_module_list, cached_namespaces; + try { + using (FileStream fs = File.OpenRead (cache_file)){ + StreamReader reader = new StreamReader (fs); + + int assembly_count = Int32.Parse (reader.ReadLine ()); + + if (assembly_count != assemblies.Length){ + Console.WriteLine ("Assembly missmatch ({0}, {1})", assembly_count, assemblies.Length); + return null; + } + + int namespace_count = Int32.Parse (reader.ReadLine ()); + + cached_module_list = new Hashtable (assembly_count); + for (int i = 0; i < assembly_count; i++) + cached_module_list [reader.ReadLine ()] = true; + + cached_namespaces = new Hashtable (namespace_count); + for (int i = 0; i < namespace_count; i++){ + string s = reader.ReadLine (); + cached_namespaces [s] = s; + } + } + + // + // Now, check that the cache is still valid + // + + foreach (Assembly a in assemblies) + if (cached_module_list [a.CodeBase] == null){ + Console.WriteLine ("assembly not found in cache: " + a.CodeBase); + return null; + } + + return cached_namespaces; + } catch { + } return null; } + static void SaveCache (string cache_file) + { + try { + using (FileStream fs = File.OpenWrite (cache_file)){ + StreamWriter writer = new StreamWriter (fs); + + writer.WriteLine (assemblies.Length); + writer.WriteLine (assemblies_namespaces.Count); + + foreach (Assembly a in assemblies) + writer.WriteLine (a.CodeBase); + + foreach (DictionaryEntry de in assemblies_namespaces){ + writer.WriteLine ((string) de.Key); + } + + writer.Flush (); + fs.Flush (); + } + } catch (Exception e) { + Console.WriteLine ("Failed: " + e); + } + } + + public static void GetAllTypes () + { + Hashtable namespaces = new Hashtable (); + + foreach (Assembly a in assemblies){ + foreach (Type t in a.GetTypes ()){ + } + } + + foreach (ModuleBuilder mb in modules){ + foreach (Type t in mb.GetTypes ()){ + } + } + } + + /// /// Returns the C# name of a type if possible, or the full type name otherwise /// @@ -516,21 +815,26 @@ public class TypeManager { /// static MethodInfo GetMethod (Type t, string name, Type [] args) { - MemberInfo [] mi; + MemberList list; Signature sig; sig.name = name; sig.args = args; - mi = FindMembers ( - t, MemberTypes.Method, - instance_and_static | BindingFlags.Public, signature_filter, sig); - if (mi == null || mi.Length == 0 || !(mi [0] is MethodInfo)){ + list = FindMembers (t, MemberTypes.Method, instance_and_static | BindingFlags.Public, + signature_filter, sig); + if (list.Count == 0) { + Report.Error (-19, "Can not find the core function `" + name + "'"); + return null; + } + + MethodInfo mi = list [0] as MethodInfo; + if (mi == null) { Report.Error (-19, "Can not find the core function `" + name + "'"); return null; } - return (MethodInfo) mi [0]; + return mi; } /// @@ -538,20 +842,27 @@ public class TypeManager { /// static ConstructorInfo GetConstructor (Type t, Type [] args) { - MemberInfo [] mi; + MemberList list; Signature sig; sig.name = ".ctor"; sig.args = args; - mi = FindMembers (t, MemberTypes.Constructor, - instance_and_static | BindingFlags.Public | BindingFlags.DeclaredOnly, signature_filter, sig); - if (mi == null || mi.Length == 0 || !(mi [0] is ConstructorInfo)){ + list = FindMembers (t, MemberTypes.Constructor, + instance_and_static | BindingFlags.Public | BindingFlags.DeclaredOnly, + signature_filter, sig); + if (list.Count == 0){ + Report.Error (-19, "Can not find the core constructor for type `" + t.Name + "'"); + return null; + } + + ConstructorInfo ci = list [0] as ConstructorInfo; + if (ci == null){ Report.Error (-19, "Can not find the core constructor for type `" + t.Name + "'"); return null; } - return (ConstructorInfo) mi [0]; + return ci; } public static void InitEnumUnderlyingTypes () @@ -614,12 +925,22 @@ public class TypeManager { marshal_as_attr_type = CoreLookupType ("System.Runtime.InteropServices.MarshalAsAttribute"); param_array_type = CoreLookupType ("System.ParamArrayAttribute"); + // + // Temporary while people upgrade their corlibs + // + // + // Change from LookupType to CoreLookupType before release + // + guid_attr_type = LookupType ("System.Runtime.InteropServices.GuidAttribute"); + unverifiable_code_type= CoreLookupType ("System.Security.UnverifiableCodeAttribute"); void_ptr_type = CoreLookupType ("System.Void*"); indexer_name_type = CoreLookupType ("System.Runtime.CompilerServices.IndexerNameAttribute"); + exception_type = CoreLookupType ("System.Exception"); + // // Attribute types // @@ -655,16 +976,22 @@ public class TypeManager { system_void_array_copyto_array_int = GetMethod ( system_array_type, "CopyTo", system_array_int_arg); - Type [] system_type_type_arg = { system_type_type, system_type_type }; + Type [] system_type_type_arg = { system_type_type, system_type_type, system_type_type }; + + try { system_void_set_corlib_type_builders = GetMethod ( system_assemblybuilder_type, "SetCorlibTypeBuilders", system_type_type_arg); - object[] args = new object [2]; + object[] args = new object [3]; args [0] = object_type; args [1] = value_type; + args [2] = enum_type; system_void_set_corlib_type_builders.Invoke (CodeGen.AssemblyBuilder, args); + } catch { + Console.WriteLine ("Corlib compilation is not supported in Microsoft.NET due to bugs in it"); + } } } @@ -679,6 +1006,12 @@ public class TypeManager { Type [] string_string = { string_type, string_type }; string_concat_string_string = GetMethod ( string_type, "Concat", string_string); + Type [] string_string_string = { string_type, string_type, string_type }; + string_concat_string_string_string = GetMethod ( + string_type, "Concat", string_string_string); + Type [] string_string_string_string = { string_type, string_type, string_type, string_type }; + string_concat_string_string_string_string = GetMethod ( + string_type, "Concat", string_string_string_string); Type [] object_object = { object_type, object_type }; string_concat_object_object = GetMethod ( @@ -776,87 +1109,132 @@ public class TypeManager { const BindingFlags instance_and_static = BindingFlags.Static | BindingFlags.Instance; - // - // FIXME: This can be optimized easily. speedup by having a single builder mapping - // - public static MemberInfo [] FindMembers (Type t, MemberTypes mt, BindingFlags bf, - MemberFilter filter, object criteria) + static Hashtable type_hash = new Hashtable (); + + /// + /// This is the "old", non-cache based FindMembers() function. We cannot use + /// the cache here because there is no member name argument. + /// + public static MemberList FindMembers (Type t, MemberTypes mt, BindingFlags bf, + MemberFilter filter, object criteria) { + DeclSpace decl = (DeclSpace) builder_to_declspace [t]; + + // + // `builder_to_declspace' contains all dynamic types. + // + if (decl != null) { + MemberList list; + Timer.StartTimer (TimerType.FindMembers); + list = decl.FindMembers (mt, bf, filter, criteria); + Timer.StopTimer (TimerType.FindMembers); + return list; + } + // // We have to take care of arrays specially, because GetType on // a TypeBuilder array will return a Type, not a TypeBuilder, // and we can not call FindMembers on this type. // if (t.IsSubclassOf (TypeManager.array_type)) - return TypeManager.array_type.FindMembers (mt, bf, filter, criteria); - - if (!(t is TypeBuilder)){ - // - // Since FindMembers will not lookup both static and instance - // members, we emulate this behaviour here. - // - if ((bf & instance_and_static) == instance_and_static){ - MemberInfo [] i_members = t.FindMembers ( - mt, bf & ~BindingFlags.Static, filter, criteria); - - int i_len = i_members.Length; - if (i_len == 1){ - MemberInfo one = i_members [0]; - - // - // If any of these are present, we are done! - // - if ((one is Type) || (one is EventInfo) || (one is FieldInfo)) - return i_members; - } + return new MemberList (TypeManager.array_type.FindMembers (mt, bf, filter, criteria)); + + // + // Since FindMembers will not lookup both static and instance + // members, we emulate this behaviour here. + // + if ((bf & instance_and_static) == instance_and_static){ + MemberInfo [] i_members = t.FindMembers ( + mt, bf & ~BindingFlags.Static, filter, criteria); + + int i_len = i_members.Length; + if (i_len == 1){ + MemberInfo one = i_members [0]; + + // + // If any of these are present, we are done! + // + if ((one is Type) || (one is EventInfo) || (one is FieldInfo)) + return new MemberList (i_members); + } - MemberInfo [] s_members = t.FindMembers ( - mt, bf & ~BindingFlags.Instance, filter, criteria); - - int s_len = s_members.Length; - if (i_len > 0 || s_len > 0){ - MemberInfo [] both = new MemberInfo [i_len + s_len]; - - i_members.CopyTo (both, 0); - s_members.CopyTo (both, i_len); - - return both; - } else { - if (i_len > 0) - return i_members; - else - return s_members; - } + MemberInfo [] s_members = t.FindMembers ( + mt, bf & ~BindingFlags.Instance, filter, criteria); + + int s_len = s_members.Length; + if (i_len > 0 || s_len > 0) + return new MemberList (i_members, s_members); + else { + if (i_len > 0) + return new MemberList (i_members); + else + return new MemberList (s_members); } - return t.FindMembers (mt, bf, filter, criteria); } + return new MemberList (t.FindMembers (mt, bf, filter, criteria)); + } + + + /// + /// This method is only called from within MemberLookup. It tries to use the member + /// cache if possible and falls back to the normal FindMembers if not. The `used_cache' + /// flag tells the caller whether we used the cache or not. If we used the cache, then + /// our return value will already contain all inherited members and the caller don't need + /// to check base classes and interfaces anymore. + /// + private static MemberList MemberLookup_FindMembers (Type t, MemberTypes mt, BindingFlags bf, + string name, out bool used_cache) + { // - // FIXME: We should not have builder_to_blah everywhere, - // we should just have a builder_to_findmemberizable - // and have them implement a new ICanFindMembers interface + // We have to take care of arrays specially, because GetType on + // a TypeBuilder array will return a Type, not a TypeBuilder, + // and we can not call FindMembers on this type. // - Enum e = (Enum) builder_to_enum [t]; + if (t.IsSubclassOf (TypeManager.array_type)) { + used_cache = true; + return TypeHandle.ArrayType.MemberCache.FindMembers ( + mt, bf, name, FilterWithClosure_delegate, null); + } - if (e != null) - return e.FindMembers (mt, bf, filter, criteria); - - Delegate del = (Delegate) builder_to_delegate [t]; + // + // If this is a dynamic type, it's always in the `builder_to_declspace' hash table + // and we can ask the DeclSpace for the MemberCache. + // + if (t is TypeBuilder) { + DeclSpace decl = (DeclSpace) builder_to_declspace [t]; + MemberCache cache = decl.MemberCache; - if (del != null) - return del.FindMembers (mt, bf, filter, criteria); + // + // If this DeclSpace has a MemberCache, use it. + // - Interface iface = (Interface) builder_to_interface [t]; + if (cache != null) { + used_cache = true; + return cache.FindMembers ( + mt, bf, name, FilterWithClosure_delegate, null); + } - if (iface != null) - return iface.FindMembers (mt, bf, filter, criteria); - - TypeContainer tc = (TypeContainer) builder_to_container [t]; + // If there is no MemberCache, we need to use the "normal" FindMembers. - if (tc != null) - return tc.FindMembers (mt, bf, filter, criteria); + MemberList list; + Timer.StartTimer (TimerType.FindMembers); + list = decl.FindMembers (mt, bf | BindingFlags.DeclaredOnly, + FilterWithClosure_delegate, name); + Timer.StopTimer (TimerType.FindMembers); + used_cache = false; + return list; + } - return null; + // + // This call will always succeed. There is exactly one TypeHandle instance per + // type, TypeHandle.GetTypeHandle() will either return it or create a new one + // if it didn't already exist. + // + TypeHandle handle = TypeHandle.GetTypeHandle (t); + + used_cache = true; + return handle.MemberCache.FindMembers (mt, bf, name, FilterWithClosure_delegate, null); } public static bool IsBuiltinType (Type t) @@ -864,6 +1242,21 @@ public class TypeManager { if (t == object_type || t == string_type || t == int32_type || t == uint32_type || t == int64_type || t == uint64_type || t == float_type || t == double_type || t == char_type || t == short_type || t == decimal_type || t == bool_type || + t == sbyte_type || t == byte_type || t == ushort_type || t == void_type) + return true; + else + return false; + } + + // + // This is like IsBuiltinType, but lacks decimal_type, we should also clean up + // the pieces in the code where we use IsBuiltinType and special case decimal_type. + // + public static bool IsCLRType (Type t) + { + if (t == object_type || t == int32_type || t == uint32_type || + t == int64_type || t == uint64_type || t == float_type || t == double_type || + t == char_type || t == short_type || t == bool_type || t == sbyte_type || t == byte_type || t == ushort_type) return true; else @@ -885,10 +1278,58 @@ public class TypeManager { else return false; } + + // + // Whether a type is unmanaged. This is used by the unsafe code (25.2) + // + public static bool IsUnmanagedType (Type t) + { + if (IsBuiltinType (t) && t != TypeManager.string_type) + return true; + + if (IsEnumType (t)) + return true; + + if (t.IsPointer) + return true; + + if (IsValueType (t)){ + if (t is TypeBuilder){ + TypeContainer tc = LookupTypeContainer (t); + + foreach (Field f in tc.Fields){ + if (f.FieldBuilder.IsStatic) + continue; + if (!IsUnmanagedType (f.FieldBuilder.FieldType)) + return false; + } + } else { + FieldInfo [] fields = t.GetFields (); + + foreach (FieldInfo f in fields){ + if (f.IsStatic) + continue; + if (!IsUnmanagedType (f.FieldType)) + return false; + } + } + return true; + } + + return false; + } + + public static bool IsValueType (Type t) + { + if (t.IsSubclassOf (TypeManager.value_type)) + return true; + else + return false; + } public static bool IsInterfaceType (Type t) { - Interface iface = (Interface) builder_to_interface [t]; + Interface iface = builder_to_declspace [t] as Interface; if (iface != null) return true; @@ -896,6 +1337,33 @@ public class TypeManager { return false; } + // + // Checks whether `type' is a subclass or nested child of `parent'. + // + public static bool IsSubclassOrNestedChildOf (Type type, Type parent) + { + do { + if ((type == parent) || type.IsSubclassOf (parent)) + return true; + + // Handle nested types. + type = type.DeclaringType; + } while (type != null); + + return false; + } + + // + // Checks whether `type' is a nested child of `parent'. + // + public static bool IsNestedChildOf (Type type, Type parent) + { + if ((type == parent) || type.IsSubclassOf (parent)) + return false; + else + return IsSubclassOrNestedChildOf (type, parent); + } + /// /// Returns the User Defined Types /// @@ -911,6 +1379,38 @@ public class TypeManager { } } + static Hashtable attr_to_allowmult; + + public static void RegisterAttributeAllowMultiple (Type attr_type, bool allow) + { + if (attr_to_allowmult == null) + attr_to_allowmult = new PtrHashtable (); + + if (attr_to_allowmult.Contains (attr_type)) + return; + + attr_to_allowmult.Add (attr_type, allow); + + } + + public static bool AreMultipleAllowed (Type attr_type) + { + if (!(attr_type is TypeBuilder)) { + System.Attribute [] attrs = System.Attribute.GetCustomAttributes (attr_type); + + foreach (System.Attribute tmp in attrs) + if (tmp is AttributeUsageAttribute) + return ((AttributeUsageAttribute) tmp).AllowMultiple; + + return false; + } + + if (attr_to_allowmult == null) + return false; + + return (bool) attr_to_allowmult [attr_type]; + } + static Hashtable builder_to_constant; public static void RegisterConstant (FieldBuilder fb, Const c) @@ -986,6 +1486,38 @@ public class TypeManager { return types; } } + + /// + /// Returns the argument types for an indexer based on its PropertyInfo + /// + /// For dynamic indexers, we use the compiler provided types, for + /// indexers from existing assemblies we load them from GetParameters, + /// and insert them into the cache + /// + static public Type [] GetArgumentTypes (PropertyInfo indexer) + { + if (indexer_arguments.Contains (indexer)) + return (Type []) indexer_arguments [indexer]; + else if (indexer is PropertyBuilder) + // If we're a PropertyBuilder and not in the + // `indexer_arguments' hash, then we're a property and + // not an indexer. + return NoTypes; + else { + ParameterInfo [] pi = indexer.GetIndexParameters (); + // Property, not an indexer. + if (pi == null) + return NoTypes; + int c = pi.Length; + Type [] types = new Type [c]; + + for (int i = 0; i < c; i++) + types [i] = pi [i].ParameterType; + + indexer_arguments.Add (indexer, types); + return types; + } + } // // This is a workaround the fact that GetValue is not @@ -1054,7 +1586,7 @@ public class TypeManager { return (MethodInfo) pair.Second; } else - return ei.GetAddMethod (); + return ei.GetRemoveMethod (); } static Hashtable priv_fields_events; @@ -1076,7 +1608,7 @@ public class TypeManager { { return (MemberInfo) priv_fields_events [ei]; } - + static Hashtable properties; static public bool RegisterProperty (PropertyBuilder pb, MethodBase get, MethodBase set) @@ -1091,56 +1623,15 @@ public class TypeManager { return true; } - - // - // FIXME: we need to return the accessors depending on whether - // they are visible or not. - // - static public MethodInfo [] GetAccessors (PropertyInfo pi) - { - MethodInfo [] ret; - - if (pi is PropertyBuilder){ - Pair pair = (Pair) properties [pi]; - - ret = new MethodInfo [2]; - ret [0] = (MethodInfo) pair.First; - ret [1] = (MethodInfo) pair.Second; - - return ret; - } else { - MethodInfo [] mi = new MethodInfo [2]; - - // - // Why this and not pi.GetAccessors? - // Because sometimes index 0 is the getter - // sometimes it is 1 - // - mi [0] = pi.GetGetMethod (true); - mi [1] = pi.GetSetMethod (true); - - return mi; - } - } - static public MethodInfo GetPropertyGetter (PropertyInfo pi) + static public bool RegisterIndexer (PropertyBuilder pb, MethodBase get, MethodBase set, Type[] args) { - if (pi is PropertyBuilder){ - Pair de = (Pair) properties [pi]; - - return (MethodInfo) de.Second; - } else - return pi.GetSetMethod (); - } + if (!RegisterProperty (pb, get,set)) + return false; - static public MethodInfo GetPropertySetter (PropertyInfo pi) - { - if (pi is PropertyBuilder){ - Pair de = (Pair) properties [pi]; + indexer_arguments.Add (pb, args); - return (MethodInfo) de.First; - } else - return pi.GetGetMethod (); + return true; } /// @@ -1245,11 +1736,13 @@ public class TypeManager { // This is a custom version of Convert.ChangeType() which works // with the TypeBuilder defined types when compiling corlib. - public static object ChangeType (object value, Type conversionType) + public static object ChangeType (object value, Type conversionType, out bool error) { - if (!(value is IConvertible)) - throw new ArgumentException (); - + if (!(value is IConvertible)){ + error = true; + return null; + } + IConvertible convertValue = (IConvertible) value; CultureInfo ci = CultureInfo.CurrentCulture; NumberFormatInfo provider = ci.NumberFormat; @@ -1260,6 +1753,7 @@ public class TypeManager { // the system type itself. You cannot use Type.GetTypeCode() // on such a type - it'd always return TypeCode.Object. // + error = false; if (conversionType.Equals (typeof (Boolean))) return (object)(convertValue.ToBoolean (provider)); else if (conversionType.Equals (typeof (Byte))) @@ -1293,7 +1787,8 @@ public class TypeManager { else if (conversionType.Equals (typeof (Object))) return (object)(value); else - throw new InvalidCastException (); + error = true; + return null; } // @@ -1387,6 +1882,12 @@ public class TypeManager { case TypeCode.String: return TypeManager.string_type; default: + if (t == typeof (void)) + return TypeManager.void_type; + if (t == typeof (object)) + return TypeManager.object_type; + if (t == typeof (System.Type)) + return TypeManager.type_type; return t; } } @@ -1429,19 +1930,22 @@ public class TypeManager { /// public static string IndexerPropertyName (Type t) { - if (t is TypeBuilder) { - TypeContainer tc = (TypeContainer) builder_to_container [t]; + if (t.IsInterface) { + Interface i = LookupInterface (t); - // - // FIXME: Temporary hack, until we deploy the IndexerName - // property code (and attributes) in the interface code. - // - if (tc == null){ - return "Item"; + if ((i == null) || (i.IndexerName == null)) + return "Item"; + + return i.IndexerName; + } else { + TypeContainer tc = LookupTypeContainer (t); + + if ((tc == null) || (tc.IndexerName == null)) + return "Item"; + + return tc.IndexerName; } - - return tc.IndexerName; } System.Attribute attr = System.Attribute.GetCustomAttribute ( @@ -1487,12 +1991,8 @@ public class TypeManager { } if (i != old_count) continue; - - if (!(method is MethodInfo && new_method is MethodInfo)) - return true; - - if (((MethodInfo) method).ReturnType == ((MethodInfo) new_method).ReturnType) - return true; + + return true; } return false; } @@ -1503,7 +2003,7 @@ public class TypeManager { // // The name is assumed to be the same. // - public static ArrayList CopyNewMethods (ArrayList target_list, MemberInfo [] new_members) + public static ArrayList CopyNewMethods (ArrayList target_list, MemberList new_members) { if (target_list == null){ target_list = new ArrayList (); @@ -1530,22 +2030,31 @@ public class TypeManager { [Flags] public enum MethodFlags { IsObsolete = 1, - ShouldIgnore = 2 + IsObsoleteError = 2, + ShouldIgnore = 3 } - static public MethodFlags GetMethodFlags (MethodBase mb) + // + // Returns the TypeManager.MethodFlags for this method. + // This emits an error 619 / warning 618 if the method is obsolete. + // In the former case, TypeManager.MethodFlags.IsObsoleteError is returned. + // + static public MethodFlags GetMethodFlags (MethodBase mb, Location loc) { MethodFlags flags = 0; if (mb.DeclaringType is TypeBuilder){ - // - // FIXME: Support lookups of Obsolete and ConditionalAttribute - // on MethodBuilders. - // - return 0; + MethodData method = (MethodData) builder_to_method [mb]; + if (method == null) { + // FIXME: implement Obsolete attribute on Property, + // Indexer and Event. + return 0; + } + + return method.GetMethodFlags (loc); } - object [] attrs = mb.GetCustomAttributes (false); + object [] attrs = mb.GetCustomAttributes (true); foreach (object ta in attrs){ if (!(ta is System.Attribute)){ Console.WriteLine ("Unknown type in GetMethodFlags: " + ta); @@ -1553,7 +2062,20 @@ public class TypeManager { } System.Attribute a = (System.Attribute) ta; if (a.TypeId == TypeManager.obsolete_attribute_type){ + ObsoleteAttribute oa = (ObsoleteAttribute) a; + + string method_desc = TypeManager.CSharpSignature (mb); + + if (oa.IsError) { + Report.Error (619, loc, "Method `" + method_desc + + "' is obsolete: `" + oa.Message + "'"); + return MethodFlags.IsObsoleteError; + } else + Report.Warning (618, loc, "Method `" + method_desc + + "' is obsolete: `" + oa.Message + "'"); + flags |= MethodFlags.IsObsolete; + continue; } @@ -1589,6 +2111,7 @@ public class TypeManager { // static Type closure_invocation_type; static Type closure_queried_type; + static Type closure_qualifier_type; // // The assembly that defines the type is that is calling us @@ -1606,9 +2129,13 @@ public class TypeManager { // fields. // - if (m.Name != closure_name) + if ((filter_criteria != null) && (m.Name != (string) filter_criteria)) return false; + if (((closure_qualifier_type == null) || (closure_qualifier_type == closure_invocation_type)) && + (m.DeclaringType == closure_invocation_type)) + return true; + // // Ugly: we need to find out the type of `m', and depending // on this, tell whether we accept or not @@ -1618,7 +2145,7 @@ public class TypeManager { MethodAttributes ma = mb.Attributes & MethodAttributes.MemberAccessMask; if (ma == MethodAttributes.Private) - return closure_private_ok; + return closure_private_ok || (closure_invocation_type == m.DeclaringType); // // FamAndAssem requires that we not only derivate, but we are on the @@ -1629,7 +2156,35 @@ public class TypeManager { return false; } - // FamORAssem, Family and Public: + // Assembly and FamORAssem succeed if we're in the same assembly. + if ((ma == MethodAttributes.Assembly) || (ma == MethodAttributes.FamORAssem)){ + if (closure_invocation_assembly == mb.DeclaringType.Assembly) + return true; + } + + // We already know that we aren't in the same assembly. + if (ma == MethodAttributes.Assembly) + return false; + + // Family and FamANDAssem require that we derive. + if ((ma == MethodAttributes.Family) || (ma == MethodAttributes.FamANDAssem)){ + if (closure_invocation_type == null) + return false; + + if (!IsSubclassOrNestedChildOf (closure_invocation_type, mb.DeclaringType)) + return false; + + // Although a derived class can access protected members of its base class + // it cannot do so through an instance of the base class (CS1540). + if (!mb.IsStatic && (closure_invocation_type != closure_qualifier_type) && + (closure_qualifier_type != null) && + closure_invocation_type.IsSubclassOf (closure_qualifier_type)) + return false; + + return true; + } + + // Public. return true; } @@ -1638,7 +2193,7 @@ public class TypeManager { FieldAttributes fa = fi.Attributes & FieldAttributes.FieldAccessMask; if (fa == FieldAttributes.Private) - return closure_private_ok; + return closure_private_ok || (closure_invocation_type == m.DeclaringType); // // FamAndAssem requires that we not only derivate, but we are on the @@ -1648,41 +2203,106 @@ public class TypeManager { if (closure_invocation_assembly != fi.DeclaringType.Assembly) return false; } - // FamORAssem, Family and Public: + + // Assembly and FamORAssem succeed if we're in the same assembly. + if ((fa == FieldAttributes.Assembly) || (fa == FieldAttributes.FamORAssem)){ + if (closure_invocation_assembly == fi.DeclaringType.Assembly) + return true; + } + + // We already know that we aren't in the same assembly. + if (fa == FieldAttributes.Assembly) + return false; + + // Family and FamANDAssem require that we derive. + if ((fa == FieldAttributes.Family) || (fa == FieldAttributes.FamANDAssem)){ + if (closure_invocation_type == null) + return false; + + if (!IsSubclassOrNestedChildOf (closure_invocation_type, fi.DeclaringType)) + return false; + + // Although a derived class can access protected members of its base class + // it cannot do so through an instance of the base class (CS1540). + if (!fi.IsStatic && (closure_invocation_type != closure_qualifier_type) && + (closure_qualifier_type != null) && + closure_invocation_type.IsSubclassOf (closure_qualifier_type)) + return false; + + return true; + } + + // Public. return true; } // - // EventInfos and PropertyInfos, return true + // EventInfos and PropertyInfos, return true because they lack permission + // informaiton, so we need to check later on the methods. // return true; } static MemberFilter FilterWithClosure_delegate = new MemberFilter (FilterWithClosure); - + // // Looks up a member called `name' in the `queried_type'. This lookup - // is done by code that is contained in the definition for `invocation_type'. + // is done by code that is contained in the definition for `invocation_type' + // through a qualifier of type `qualifier_type' (or null if there is no qualifier). + // + // `invocation_type' is used to check whether we're allowed to access the requested + // member wrt its protection level. + // + // When called from MemberAccess, `qualifier_type' is the type which is used to access + // the requested member (`class B { A a = new A (); a.foo = 5; }'; here invocation_type + // is B and qualifier_type is A). This is used to do the CS1540 check. + // + // When resolving a SimpleName, `qualifier_type' is null. + // + // The `qualifier_type' is used for the CS1540 check; it's normally either null or + // the same than `queried_type' - except when we're being called from BaseAccess; + // in this case, `invocation_type' is the current type and `queried_type' the base + // type, so this'd normally trigger a CS1540. // // The binding flags are `bf' and the kind of members being looked up are `mt' // + // The return value always includes private members which code in `invocation_type' + // is allowed to access (using the specified `qualifier_type' if given); only use + // BindingFlags.NonPublic to bypass the permission check. + // // Returns an array of a single element for everything but Methods/Constructors // that might return multiple matches. // - public static MemberInfo [] MemberLookup (Type invocation_type, Type queried_type, - MemberTypes mt, BindingFlags original_bf, string name) + public static MemberInfo [] MemberLookup (Type invocation_type, Type qualifier_type, + Type queried_type, MemberTypes mt, + BindingFlags original_bf, string name) + { + Timer.StartTimer (TimerType.MemberLookup); + + MemberInfo[] retval = RealMemberLookup (invocation_type, qualifier_type, + queried_type, mt, original_bf, name); + + Timer.StopTimer (TimerType.MemberLookup); + + return retval; + } + + static MemberInfo [] RealMemberLookup (Type invocation_type, Type qualifier_type, + Type queried_type, MemberTypes mt, + BindingFlags original_bf, string name) { BindingFlags bf = original_bf; ArrayList method_list = null; Type current_type = queried_type; bool searching = (original_bf & BindingFlags.DeclaredOnly) == 0; - bool private_ok; + bool skip_iface_check = true, used_cache = false; bool always_ok_flag = false; closure_name = name; closure_invocation_type = invocation_type; closure_invocation_assembly = invocation_type != null ? invocation_type.Assembly : null; + closure_qualifier_type = qualifier_type; // // If we are a nested class, we always have access to our container @@ -1693,7 +2313,7 @@ public class TypeManager { if (invocation_name.IndexOf ('+') != -1){ string container = queried_type.FullName + "+"; int container_length = container.Length; - + if (invocation_name.Length > container_length){ string shared = invocation_name.Substring (0, container_length); @@ -1704,7 +2324,7 @@ public class TypeManager { } do { - MemberInfo [] mi; + MemberList list; // // `NonPublic' is lame, because it includes both protected and @@ -1715,26 +2335,37 @@ public class TypeManager { // public, private and protected (internal does not come into the // equation) // - if (invocation_type != null){ - if (invocation_type == current_type){ - private_ok = true; - } else - private_ok = always_ok_flag; - - if (private_ok || invocation_type.IsSubclassOf (current_type)) - bf = original_bf | BindingFlags.NonPublic; - } else { - private_ok = false; - bf = original_bf & ~BindingFlags.NonPublic; - } + if ((invocation_type != null) && + ((invocation_type == current_type) || + IsNestedChildOf (invocation_type, current_type)) || + always_ok_flag) + bf = original_bf | BindingFlags.NonPublic; + else + bf = original_bf; - closure_private_ok = private_ok; + closure_private_ok = (bf & BindingFlags.NonPublic) != 0; closure_queried_type = current_type; - - mi = TypeManager.FindMembers ( - current_type, mt, bf | BindingFlags.DeclaredOnly, - FilterWithClosure_delegate, name); - + + Timer.StopTimer (TimerType.MemberLookup); + + list = MemberLookup_FindMembers (current_type, mt, bf, name, out used_cache); + + Timer.StartTimer (TimerType.MemberLookup); + + // + // When queried for an interface type, the cache will automatically check all + // inherited members, so we don't need to do this here. However, this only + // works if we already used the cache in the first iteration of this loop. + // + // If we used the cache in any further iteration, we can still terminate the + // loop since the cache always looks in all parent classes. + // + + if (used_cache) + searching = false; + else + skip_iface_check = false; + if (current_type == TypeManager.object_type) searching = false; else { @@ -1748,12 +2379,7 @@ public class TypeManager { current_type = TypeManager.object_type; } - if (mi == null) - continue; - - int count = mi.Length; - - if (count == 0) + if (list.Count == 0) continue; // @@ -1761,29 +2387,36 @@ public class TypeManager { // searches, which means that our above FindMembers will // return two copies of the same. // - if (count == 1 && !(mi [0] is MethodBase)){ - return mi; + if (list.Count == 1 && !(list [0] is MethodBase)){ + return (MemberInfo []) list; } // // Multiple properties: we query those just to find out the indexer // name // - if (mi [0] is PropertyInfo) - return mi; - + if (list [0] is PropertyInfo) + return (MemberInfo []) list; + // // We found methods, turn the search into "method scan" // mode. // - method_list = CopyNewMethods (method_list, mi); + method_list = CopyNewMethods (method_list, list); mt &= (MemberTypes.Method | MemberTypes.Constructor); } while (searching); if (method_list != null && method_list.Count > 0) return (MemberInfo []) method_list.ToArray (typeof (MemberInfo)); - + + // + // This happens if we already used the cache in the first iteration, in this case + // the cache already looked in all interfaces. + // + if (skip_iface_check) + return null; + // // Interfaces do not list members they inherit, so we have to // scan those. @@ -1801,7 +2434,7 @@ public class TypeManager { foreach (Type itype in ifaces){ MemberInfo [] x; - x = MemberLookup (null, itype, mt, bf, name); + x = MemberLookup (null, null, itype, mt, bf, name); if (x != null) return x; } @@ -1812,4 +2445,134 @@ public class TypeManager { } +/// +/// There is exactly one instance of this class per type. +/// +public sealed class TypeHandle : IMemberContainer { + public readonly TypeHandle BaseType; + + readonly int id = ++next_id; + static int next_id = 0; + + /// + /// Lookup a TypeHandle instance for the given type. If the type doesn't have + /// a TypeHandle yet, a new instance of it is created. This static method + /// ensures that we'll only have one TypeHandle instance per type. + /// + public static TypeHandle GetTypeHandle (Type t) + { + TypeHandle handle = (TypeHandle) type_hash [t]; + if (handle != null) + return handle; + + handle = new TypeHandle (t); + type_hash.Add (t, handle); + return handle; + } + + /// + /// Returns the TypeHandle for TypeManager.object_type. + /// + public static IMemberContainer ObjectType { + get { + if (object_type != null) + return object_type; + + object_type = GetTypeHandle (TypeManager.object_type); + + return object_type; + } + } + + /// + /// Returns the TypeHandle for TypeManager.array_type. + /// + public static IMemberContainer ArrayType { + get { + if (array_type != null) + return array_type; + + array_type = GetTypeHandle (TypeManager.array_type); + + return array_type; + } + } + + private static PtrHashtable type_hash = new PtrHashtable (); + + private static TypeHandle object_type = null; + private static TypeHandle array_type = null; + + private Type type; + private bool is_interface; + private MemberCache member_cache; + + private TypeHandle (Type type) + { + this.type = type; + if (type.BaseType != null) + BaseType = GetTypeHandle (type.BaseType); + else if ((type != TypeManager.object_type) && (type != typeof (object))) + is_interface = true; + this.member_cache = new MemberCache (this); + } + + // IMemberContainer methods + + public string Name { + get { + return type.FullName; + } + } + + public Type Type { + get { + return type; + } + } + + public IMemberContainer Parent { + get { + return BaseType; + } + } + + public bool IsInterface { + get { + return is_interface; + } + } + + public MemberList GetMembers (MemberTypes mt, BindingFlags bf) + { + if (mt == MemberTypes.Event) + return new MemberList (type.GetEvents (bf | BindingFlags.DeclaredOnly)); + else + return new MemberList (type.FindMembers (mt, bf | BindingFlags.DeclaredOnly, + null, null)); + } + + // IMemberFinder methods + + public MemberList FindMembers (MemberTypes mt, BindingFlags bf, string name, + MemberFilter filter, object criteria) + { + return member_cache.FindMembers (mt, bf, name, filter, criteria); + } + + public MemberCache MemberCache { + get { + return member_cache; + } + } + + public override string ToString () + { + if (BaseType != null) + return "TypeHandle (" + id + "," + Name + " : " + BaseType + ")"; + else + return "TypeHandle (" + id + "," + Name + ")"; + } +} + }