/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ #if FEATURE_METADATA_READER using Microsoft.Scripting.Metadata; #endif #if !WIN8 using TypeInfo = System.Type; #endif using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; #if FEATURE_REFEMIT using System.Reflection.Emit; #endif using System.Runtime.CompilerServices; using System.Security; using System.Text; using System.Runtime.InteropServices; using System.Dynamic; using System.Linq.Expressions; using Microsoft.Scripting.Generation; using Microsoft.Scripting.Runtime; using Microsoft.Scripting.Utils; #if WIN8 || WP75 namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event)] public sealed class SpecialNameAttribute : Attribute { public SpecialNameAttribute() { } } } #endif #if WIN8 namespace System { public enum TypeCode { Empty, Object, DBNull, Boolean, Char, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, String = 18 } } namespace System.Reflection { [Flags] public enum BindingFlags { /// Specifies that instance members are to be included in the search. Instance = 4, /// Specifies that static members are to be included in the search. Static = 8, /// Specifies that public members are to be included in the search. Public = 16, /// Specifies that non-public members are to be included in the search. NonPublic = 32 } } #elif !CLR45 namespace System.Reflection { public static class RuntimeReflectionExtensions { public static MethodInfo GetRuntimeBaseDefinition(this MethodInfo method) { return method.GetBaseDefinition(); } public static IEnumerable GetRuntimeMethods(this Type type) { return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } } #endif namespace Microsoft.Scripting.Utils { // CF doesn't support DefaultParameterValue attribute. Define our own, but not in System.Runtime.InteropServices namespace as that would // make C# compiler emit the parameter's default value metadata not the attribute itself. The default value metadata are not accessible on CF. #if !FEATURE_DEFAULT_PARAMETER_VALUE /// /// The Default Parameter Value Attribute. /// public sealed class DefaultParameterValueAttribute : Attribute { private readonly object _value; public object Value { get { return _value; } } /// /// The constructor /// /// The value. public DefaultParameterValueAttribute(object value) { _value = value; } } #if !ANDROID [AttributeUsage(AttributeTargets.Parameter, Inherited = false), ComVisible(true)] public sealed class OptionalAttribute : Attribute { } #endif #endif public static class ReflectionUtils { #region Accessibility public static BindingFlags AllMembers = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; public static bool IsPublic(this PropertyInfo property) { return property.GetGetMethod(nonPublic: false) != null || property.GetSetMethod(nonPublic: false) != null; } public static bool IsStatic(this PropertyInfo property) { var getter = property.GetGetMethod(nonPublic: true); var setter = property.GetSetMethod(nonPublic: true); return getter != null && getter.IsStatic || setter != null && setter.IsStatic; } public static bool IsStatic(this EventInfo evnt) { var add = evnt.GetAddMethod(nonPublic: true); var remove = evnt.GetRemoveMethod(nonPublic: true); return add != null && add.IsStatic || remove != null && remove.IsStatic; } public static bool IsPrivate(this PropertyInfo property) { var getter = property.GetGetMethod(nonPublic: true); var setter = property.GetSetMethod(nonPublic: true); return (getter == null || getter.IsPrivate) && (setter == null || setter.IsPrivate); } public static bool IsPrivate(this EventInfo evnt) { var add = evnt.GetAddMethod(nonPublic: true); var remove = evnt.GetRemoveMethod(nonPublic: true); return (add == null || add.IsPrivate) && (remove == null || remove.IsPrivate); } private static bool MatchesFlags(ConstructorInfo member, BindingFlags flags) { return ((member.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0 && ((member.IsStatic ? BindingFlags.Static : BindingFlags.Instance) & flags) != 0; } private static bool MatchesFlags(MethodInfo member, BindingFlags flags) { return ((member.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0 && ((member.IsStatic ? BindingFlags.Static : BindingFlags.Instance) & flags) != 0; } private static bool MatchesFlags(FieldInfo member, BindingFlags flags) { return ((member.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0 && ((member.IsStatic ? BindingFlags.Static : BindingFlags.Instance) & flags) != 0; } private static bool MatchesFlags(PropertyInfo member, BindingFlags flags) { return ((member.IsPublic() ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0 && ((member.IsStatic() ? BindingFlags.Static : BindingFlags.Instance) & flags) != 0; } private static bool MatchesFlags(EventInfo member, BindingFlags flags) { var add = member.GetAddMethod(); var remove = member.GetRemoveMethod(); var raise = member.GetRaiseMethod(); bool isPublic = add != null && add.IsPublic || remove != null && remove.IsPublic || raise != null && raise.IsPublic; bool isStatic = add != null && add.IsStatic || remove != null && remove.IsStatic || raise != null && raise.IsStatic; return ((isPublic ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0 && ((isStatic ? BindingFlags.Static : BindingFlags.Instance) & flags) != 0; } private static bool MatchesFlags(TypeInfo member, BindingFlags flags) { // Static/Instance are ignored return (((member.IsPublic || member.IsNestedPublic) ? BindingFlags.Public : BindingFlags.NonPublic) & flags) != 0; } private static bool MatchesFlags(MemberInfo member, BindingFlags flags) { ConstructorInfo ctor; MethodInfo method; FieldInfo field; EventInfo evnt; PropertyInfo property; if ((method = member as MethodInfo) != null) { return MatchesFlags(method, flags); } if ((field = member as FieldInfo) != null) { return MatchesFlags(field, flags); } if ((ctor = member as ConstructorInfo) != null) { return MatchesFlags(ctor, flags); } if ((evnt = member as EventInfo) != null) { return MatchesFlags(evnt, flags); } if ((property = member as PropertyInfo) != null) { return MatchesFlags(property, flags); } return MatchesFlags((TypeInfo)member, flags); } private static IEnumerable WithBindingFlags(this IEnumerable members, Func matchFlags, BindingFlags flags) where T : MemberInfo { return members.Where(member => matchFlags(member, flags)); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static IEnumerable WithBindingFlags(this IEnumerable members, BindingFlags flags) { return members.WithBindingFlags(MatchesFlags, flags); } public static MemberInfo WithBindingFlags(this MemberInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static MethodInfo WithBindingFlags(this MethodInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static ConstructorInfo WithBindingFlags(this ConstructorInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static FieldInfo WithBindingFlags(this FieldInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static PropertyInfo WithBindingFlags(this PropertyInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static EventInfo WithBindingFlags(this EventInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } public static TypeInfo WithBindingFlags(this TypeInfo member, BindingFlags flags) { return member != null && MatchesFlags(member, flags) ? member : null; } #endregion #region Signatures public static IEnumerable WithSignature(this IEnumerable members, Type[] parameterTypes) { return members.Where(c => { var ps = c.GetParameters(); if (ps.Length != parameterTypes.Length) { return false; } for (int i = 0; i < ps.Length; i++) { if (parameterTypes[i] != ps[i].ParameterType) { return false; } } return true; }); } public static IEnumerable WithSignature(this IEnumerable members, Type[] parameterTypes) { return members.Where(c => { var ps = c.GetParameters(); if (ps.Length != parameterTypes.Length) { return false; } for (int i = 0; i < ps.Length; i++) { if (parameterTypes[i] != ps[i].ParameterType) { return false; } } return true; }); } #endregion #region Member Inheritance // CLI specification, partition I, 8.10.4: Hiding, overriding, and layout // ---------------------------------------------------------------------- // While hiding applies to all members of a type, overriding deals with object layout and is applicable only to instance fields // and virtual methods. The CTS provides two forms of member overriding, new slot and expect existing slot. A member of a derived // type that is marked as a new slot will always get a new slot in the object’s layout, guaranteeing that the base field or method // is available in the object by using a qualified reference that combines the name of the base type with the name of the member // and its type or signature. A member of a derived type that is marked as expect existing slot will re-use (i.e., share or override) // a slot that corresponds to a member of the same kind (field or method), name, and type if one already exists from the base type; // if no such slot exists, a new slot is allocated and used. // // The general algorithm that is used for determining the names in a type and the layout of objects of the type is roughly as follows: // - Flatten the inherited names (using the hide by name or hide by name-and-signature rule) ignoring accessibility rules. // - For each new member that is marked “expect existing slot”, look to see if an exact match on kind (i.e., field or method), // name, and signature exists and use that slot if it is found, otherwise allocate a new slot. // - After doing this for all new members, add these new member-kind/name/signatures to the list of members of this type // - Finally, remove any inherited names that match the new members based on the hide by name or hide by name-and-signature rules. // NOTE: Following GetXxx only implement overriding, not hiding specified by hide-by-name or hide-by-name-and-signature flags. public static IEnumerable GetInheritedMethods(this Type type, string name = null, bool flattenHierarchy = false) { while (type.IsGenericParameter) { type = type.GetBaseType(); } var baseDefinitions = new HashSet(ReferenceEqualityComparer.Instance); foreach (var ancestor in type.Ancestors()) { foreach (var declaredMethod in ancestor.GetDeclaredMethods(name)) { if (declaredMethod != null && IncludeMethod(declaredMethod, type, baseDefinitions, flattenHierarchy)) { yield return declaredMethod; } } } } private static bool IncludeMethod(MethodInfo member, Type reflectedType, HashSet baseDefinitions, bool flattenHierarchy) { if (member.IsVirtual) { if (baseDefinitions.Add(RuntimeReflectionExtensions.GetRuntimeBaseDefinition(member))) { return true; } } else if (member.DeclaringType == reflectedType) { return true; } else if (!member.IsPrivate && (!member.IsStatic || flattenHierarchy)) { return true; } return false; } public static IEnumerable GetInheritedProperties(this Type type, string name = null, bool flattenHierarchy = false) { while (type.IsGenericParameter) { type = type.GetBaseType(); } var baseDefinitions = new HashSet(ReferenceEqualityComparer.Instance); foreach (var ancestor in type.Ancestors()) { if (name != null) { var declaredProperty = ancestor.GetDeclaredProperty(name); if (declaredProperty != null && IncludeProperty(declaredProperty, type, baseDefinitions, flattenHierarchy)) { yield return declaredProperty; } } else { foreach (var declaredProperty in ancestor.GetDeclaredProperties()) { if (IncludeProperty(declaredProperty, type, baseDefinitions, flattenHierarchy)) { yield return declaredProperty; } } } } } // CLI spec 22.34 Properties // ------------------------- // [Note: The CLS (see Partition I) refers to instance, virtual, and static properties. // The signature of a property (from the Type column) can be used to distinguish a static property, // since instance and virtual properties will have the “HASTHIS” bit set in the signature (§23.2.1) // while a static property will not. The distinction between an instance and a virtual property // depends on the signature of the getter and setter methods, which the CLS requires to be either // both virtual or both instance. end note] private static bool IncludeProperty(PropertyInfo member, Type reflectedType, HashSet baseDefinitions, bool flattenHierarchy) { var getter = member.GetGetMethod(nonPublic: true); var setter = member.GetSetMethod(nonPublic: true); MethodInfo virtualAccessor; if (getter != null && getter.IsVirtual) { virtualAccessor = getter; } else if (setter != null && setter.IsVirtual) { virtualAccessor = setter; } else { virtualAccessor = null; } if (virtualAccessor != null) { if (baseDefinitions.Add(RuntimeReflectionExtensions.GetRuntimeBaseDefinition(virtualAccessor))) { return true; } } else if (member.DeclaringType == reflectedType) { return true; } else if (!member.IsPrivate() && (!member.IsStatic() || flattenHierarchy)) { return true; } return false; } public static IEnumerable GetInheritedEvents(this Type type, string name = null, bool flattenHierarchy = false) { while (type.IsGenericParameter) { type = type.GetBaseType(); } var baseDefinitions = new HashSet(ReferenceEqualityComparer.Instance); foreach (var ancestor in type.Ancestors()) { if (name != null) { var declaredEvent = ancestor.GetDeclaredEvent(name); if (declaredEvent != null && IncludeEvent(declaredEvent, type, baseDefinitions, flattenHierarchy)) { yield return declaredEvent; } } else { foreach (var declaredEvent in ancestor.GetDeclaredEvents()) { if (IncludeEvent(declaredEvent, type, baseDefinitions, flattenHierarchy)) { yield return declaredEvent; } } } } } private static bool IncludeEvent(EventInfo member, Type reflectedType, HashSet baseDefinitions, bool flattenHierarchy) { var add = member.GetAddMethod(nonPublic: true); var remove = member.GetRemoveMethod(nonPublic: true); // TOOD: fire method? MethodInfo virtualAccessor; if (add != null && add.IsVirtual) { virtualAccessor = add; } else if (remove != null && remove.IsVirtual) { virtualAccessor = remove; } else { virtualAccessor = null; } if (virtualAccessor != null) { if (baseDefinitions.Add(RuntimeReflectionExtensions.GetRuntimeBaseDefinition(virtualAccessor))) { return true; } } else if (member.DeclaringType == reflectedType) { return true; } else if (!member.IsPrivate() && (!member.IsStatic() || flattenHierarchy)) { return true; } return false; } public static IEnumerable GetInheritedFields(this Type type, string name = null, bool flattenHierarchy = false) { while (type.IsGenericParameter) { type = type.GetBaseType(); } foreach (var ancestor in type.Ancestors()) { if (name != null) { var declaredField = ancestor.GetDeclaredField(name); if (declaredField != null && IncludeField(declaredField, type, flattenHierarchy)) { yield return declaredField; } } else { foreach (var declaredField in ancestor.GetDeclaredFields()) { if (IncludeField(declaredField, type, flattenHierarchy)) { yield return declaredField; } } } } } private static bool IncludeField(FieldInfo member, Type reflectedType, bool flattenHierarchy) { if (member.DeclaringType == reflectedType) { return true; } else if (!member.IsPrivate && (!member.IsStatic || flattenHierarchy)) { return true; } return false; } public static IEnumerable GetInheritedMembers(this Type type, string name = null, bool flattenHierarchy = false) { var result = type.GetInheritedMethods(name, flattenHierarchy).Cast().Concat( type.GetInheritedProperties(name, flattenHierarchy).Cast().Concat( type.GetInheritedEvents(name, flattenHierarchy).Cast().Concat( type.GetInheritedFields(name, flattenHierarchy).Cast()))); if (name == null) { return result.Concat( type.GetDeclaredConstructors().Cast().Concat( type.GetDeclaredNestedTypes().Cast())); } var nestedType = type.GetDeclaredNestedType(name); return (nestedType != null) ? result.Concat(new[] { nestedType }) : result; } #endregion #region Declared Members public static IEnumerable GetDeclaredConstructors(this Type type) { #if WIN8 return type.GetTypeInfo().DeclaredConstructors; #else return type.GetConstructors(BindingFlags.DeclaredOnly | AllMembers); #endif } #if WIN8 public static ConstructorInfo GetConstructor(this Type type, Type[] parameterTypes) { return type.GetDeclaredConstructors().Where(ci => !ci.IsStatic && ci.IsPublic).WithSignature(parameterTypes).SingleOrDefault(); } #endif public static IEnumerable GetDeclaredMethods(this Type type, string name = null) { #if WIN8 if (name == null) { return type.GetTypeInfo().DeclaredMethods; } else { return type.GetTypeInfo().GetDeclaredMethods(name); } #else if (name == null) { return type.GetMethods(BindingFlags.DeclaredOnly | AllMembers); } else { return type.GetMember(name, MemberTypes.Method, BindingFlags.DeclaredOnly | AllMembers).OfType(); } #endif } public static IEnumerable GetDeclaredProperties(this Type type) { #if WIN8 return type.GetTypeInfo().DeclaredProperties; #else return type.GetProperties(BindingFlags.DeclaredOnly | AllMembers); #endif } public static PropertyInfo GetDeclaredProperty(this Type type, string name) { Debug.Assert(name != null); #if WIN8 return type.GetTypeInfo().GetDeclaredProperty(name); #else return type.GetProperty(name, BindingFlags.DeclaredOnly | AllMembers); #endif } public static IEnumerable GetDeclaredEvents(this Type type) { #if WIN8 return type.GetTypeInfo().DeclaredEvents; #else return type.GetEvents(BindingFlags.DeclaredOnly | AllMembers); #endif } public static EventInfo GetDeclaredEvent(this Type type, string name) { Debug.Assert(name != null); #if WIN8 return type.GetTypeInfo().GetDeclaredEvent(name); #else return type.GetEvent(name, BindingFlags.DeclaredOnly | AllMembers); #endif } public static IEnumerable GetDeclaredFields(this Type type) { #if WIN8 return type.GetTypeInfo().DeclaredFields; #else return type.GetFields(BindingFlags.DeclaredOnly | AllMembers); #endif } public static FieldInfo GetDeclaredField(this Type type, string name) { Debug.Assert(name != null); #if WIN8 return type.GetTypeInfo().GetDeclaredField(name); #else return type.GetField(name, BindingFlags.DeclaredOnly | AllMembers); #endif } public static IEnumerable GetDeclaredNestedTypes(this Type type) { #if WIN8 return type.GetTypeInfo().DeclaredNestedTypes; #else return type.GetNestedTypes(BindingFlags.DeclaredOnly | AllMembers); #endif } public static TypeInfo GetDeclaredNestedType(this Type type, string name) { Debug.Assert(name != null); #if WIN8 return type.GetTypeInfo().GetDeclaredNestedType(name); #else return type.GetNestedType(name, BindingFlags.DeclaredOnly | AllMembers); #endif } public static IEnumerable GetDeclaredMembers(this Type type, string name = null) { #if WIN8 var info = type.GetTypeInfo(); if (name == null) { return info.DeclaredMembers; } else { return GetDeclaredMembersWithName(info, name); } #else if (name == null) { return type.GetMembers(BindingFlags.DeclaredOnly | AllMembers); } else { return type.GetMember(name, BindingFlags.DeclaredOnly | AllMembers); } #endif } #if WIN8 private static IEnumerable GetDeclaredMembersWithName(TypeInfo info, string name) { MemberInfo member; if ((member = info.GetDeclaredMethod(name)) != null) { yield return member; } if ((member = info.GetDeclaredField(name)) != null) { yield return member; } if ((member = info.GetDeclaredProperty(name)) != null) { yield return member; } if ((member = info.GetDeclaredEvent(name)) != null) { yield return member; } if ((member = info.GetDeclaredNestedType(name)) != null) { yield return member; } } #endif #endregion #region Win8 #if WIN8 || CLR45 public static TypeCode GetTypeCode(this Enum e) { return GetTypeCode(Enum.GetUnderlyingType(e.GetType())); } // TODO: reduce to numeric types? public static TypeCode GetTypeCode(this Type type) { if (type == typeof(int)) { return TypeCode.Int32; } if (type == typeof(sbyte)) { return TypeCode.SByte; } if (type == typeof(short)) { return TypeCode.Int16; } if (type == typeof(long)) { return TypeCode.Int64; } if (type == typeof(uint)) { return TypeCode.UInt32; } if (type == typeof(byte)) { return TypeCode.Byte; } if (type == typeof(ushort)) { return TypeCode.UInt16; } if (type == typeof(ulong)) { return TypeCode.UInt64; } if (type == typeof(bool)) { return TypeCode.Boolean; } if (type == typeof(char)) { return TypeCode.Char; } // TODO: do we need this? if (type == typeof(string)) { return TypeCode.String; } if (type == typeof(bool)) { return TypeCode.Boolean; } if (type == typeof(double)) { return TypeCode.Double; } if (type == typeof(float)) { return TypeCode.Single; } if (type == typeof(decimal)) { return TypeCode.Decimal; } if (type == typeof(DateTime)) { return TypeCode.DateTime; } return TypeCode.Object; } public static IEnumerable GetImplementedInterfaces(this Type type) { return type.GetTypeInfo().ImplementedInterfaces; } public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) { var accessor = propertyInfo.GetMethod; return nonPublic || accessor == null || accessor.IsPublic ? accessor : null; } public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo, bool nonPublic = false) { var accessor = propertyInfo.SetMethod; return nonPublic || accessor == null || accessor.IsPublic ? accessor : null; } public static MethodInfo GetAddMethod(this EventInfo eventInfo, bool nonPublic = false) { var accessor = eventInfo.AddMethod; return nonPublic || accessor == null || accessor.IsPublic ? accessor : null; } public static MethodInfo GetRemoveMethod(this EventInfo eventInfo, bool nonPublic = false) { var accessor = eventInfo.RemoveMethod; return nonPublic || accessor == null || accessor.IsPublic ? accessor : null; } public static MethodInfo GetRaiseMethod(this EventInfo eventInfo, bool nonPublic = false) { var accessor = eventInfo.RaiseMethod; return nonPublic || accessor == null || accessor.IsPublic ? accessor : null; } public static MethodInfo GetMethod(this Type type, string name) { return type.GetTypeInfo().GetDeclaredMethod(name); } // TODO: FlattenHierarchy // TODO: inherited! public static MethodInfo GetMethod(this Type type, string name, Type[] parameterTypes) { return type.GetTypeInfo().GetDeclaredMethods(name).WithSignature(parameterTypes).Single(); } public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags) { return type.GetMethods(name, bindingFlags).Single(); } private static IEnumerable GetMethods(this Type type, string name, BindingFlags bindingFlags) { return type.GetTypeInfo().GetDeclaredMethods(name).WithBindingFlags(bindingFlags); } public static MethodInfo GetMethod(this Delegate d) { return d.GetMethodInfo(); } // TODO: Callers should distinguish parameters from arguments. Stop using this method. public static Type[] GetGenericArguments(this Type type) { var info = type.GetTypeInfo(); return info.IsGenericTypeDefinition ? info.GenericTypeParameters : info.GenericTypeArguments; } public static Type[] GetGenericTypeArguments(this Type type) { return type.GetTypeInfo().GenericTypeArguments; } public static Type[] GetGenericTypeParameters(this Type type) { return type.GetTypeInfo().GenericTypeParameters; } public static bool IsAssignableFrom(this Type type, Type other) { return type.GetTypeInfo().IsAssignableFrom(other.GetTypeInfo()); } public static Type[] GetGenericParameterConstraints(this Type type) { return type.GetTypeInfo().GetGenericParameterConstraints(); } public static bool IsSubclassOf(this Type type, Type other) { return type.GetTypeInfo().IsSubclassOf(other); } public static IEnumerable GetInterfaces(this Type type) { return type.GetTypeInfo().ImplementedInterfaces; } public static Type[] GetRequiredCustomModifiers(this ParameterInfo parameter) { return EmptyTypes; } public static Type[] GetOptionalCustomModifiers(this ParameterInfo parameter) { return EmptyTypes; } public static IEnumerable GetModules(this Assembly assembly) { return assembly.Modules; } private static string GetDefaultMemberName(this Type type) { foreach (var ancestor in type.Ancestors()) { var attr = ancestor.GetTypeInfo().GetCustomAttributes().SingleOrDefault(); if (attr != null) { return attr.MemberName; } } return null; } public static IEnumerable GetDefaultMembers(this Type type) { string defaultMemberName = type.GetDefaultMemberName(); if (defaultMemberName != null) { return type.GetInheritedMembers(defaultMemberName).WithBindingFlags(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); } return Enumerable.Empty(); } #else public static Type[] GetGenericTypeArguments(this Type type) { return type.IsGenericType && !type.IsGenericTypeDefinition ? type.GetTypeInfo().GetGenericArguments() : null; } public static Type[] GetGenericTypeParameters(this Type type) { return type.IsGenericTypeDefinition ? type.GetTypeInfo().GetGenericArguments() : null; } public static IEnumerable GetModules(this Assembly assembly) { return assembly.GetModules(); } public static IEnumerable GetImplementedInterfaces(this Type type) { return type.GetInterfaces(); } public static TypeCode GetTypeCode(this Type type) { return Type.GetTypeCode(type); } public static MethodInfo GetMethodInfo(this Delegate d) { return d.Method; } public static bool IsDefined(this Assembly assembly, Type attributeType) { return assembly.IsDefined(attributeType, false); } public static T GetCustomAttribute(this Assembly assembly, bool inherit = false) where T : Attribute { return (T)Attribute.GetCustomAttribute(assembly, typeof(T), inherit); } public static T GetCustomAttribute(this MemberInfo member, bool inherit = false) where T : Attribute { return (T)Attribute.GetCustomAttribute(member, typeof(T), inherit); } public static IEnumerable GetCustomAttributes(this Assembly assembly, bool inherit = false) where T : Attribute { return Attribute.GetCustomAttributes(assembly, typeof(T), inherit).Cast(); } public static IEnumerable GetCustomAttributes(this MemberInfo member, bool inherit = false) where T : Attribute { return Attribute.GetCustomAttributes(member, typeof(T), inherit).Cast(); } #endif public static bool ContainsGenericParameters(this Type type) { return type.GetTypeInfo().ContainsGenericParameters; } public static bool IsInterface(this Type type) { return type.GetTypeInfo().IsInterface; } public static bool IsClass(this Type type) { return type.GetTypeInfo().IsClass; } public static bool IsGenericType(this Type type) { return type.GetTypeInfo().IsGenericType; } public static bool IsGenericTypeDefinition(this Type type) { return type.GetTypeInfo().IsGenericTypeDefinition; } public static bool IsSealed(this Type type) { return type.GetTypeInfo().IsSealed; } public static bool IsAbstract(this Type type) { return type.GetTypeInfo().IsAbstract; } public static bool IsPublic(this Type type) { return type.GetTypeInfo().IsPublic; } public static bool IsVisible(this Type type) { return type.GetTypeInfo().IsVisible; } public static Type GetBaseType(this Type type) { return type.GetTypeInfo().BaseType; } public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; } public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; } public static bool IsPrimitive(this Type type) { return type.GetTypeInfo().IsPrimitive; } public static GenericParameterAttributes GetGenericParameterAttributes(this Type type) { return type.GetTypeInfo().GenericParameterAttributes; } public static Type[] EmptyTypes = new Type[0]; public static object GetRawConstantValue(this FieldInfo field) { if (!field.IsLiteral) { throw new ArgumentException(field + " not a literal."); } object value = field.GetValue(null); return field.FieldType.IsEnum() ? UnwrapEnumValue(value) : value; } /// /// Converts a boxed enum value to the underlying integer value. /// public static object UnwrapEnumValue(object value) { if (value == null) { throw new ArgumentNullException("value"); } switch (value.GetType().GetTypeCode()) { case TypeCode.Byte: return System.Convert.ToByte(value); case TypeCode.Int16: return System.Convert.ToInt16(value); case TypeCode.Int32: return System.Convert.ToInt32(value); case TypeCode.Int64: return System.Convert.ToInt64(value); case TypeCode.SByte: return System.Convert.ToSByte(value); case TypeCode.UInt16: return System.Convert.ToUInt16(value); case TypeCode.UInt32: return System.Convert.ToUInt32(value); case TypeCode.UInt64: return System.Convert.ToUInt64(value); default: throw new ArgumentException("Value must be a boxed enum.", "value"); } } #endregion #if FEATURE_REFEMIT #if FEATURE_ASSEMBLYBUILDER_DEFINEDYNAMICASSEMBLY public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) { return AssemblyBuilder.DefineDynamicAssembly(name, access); } #else public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) { return AppDomain.CurrentDomain.DefineDynamicAssembly(name, access); } #endif #if !FEATURE_PDBEMIT public static ModuleBuilder DefineDynamicModule(this AssemblyBuilder assembly, string name, bool emitDebugInfo) { // ignore the flag return assembly.DefineDynamicModule(name); } #endif #endif #region Signature and Type Formatting // Generic type names have the arity (number of generic type paramters) appended at the end. // For eg. the mangled name of System.List is "List`1". This mangling is done to enable multiple // generic types to exist as long as they have different arities. public const char GenericArityDelimiter = '`'; #if !WIN8 public static StringBuilder FormatSignature(StringBuilder result, MethodBase method) { return FormatSignature(result, method, (t) => t.FullName); } public static StringBuilder FormatSignature(StringBuilder result, MethodBase method, Func nameDispenser) { ContractUtils.RequiresNotNull(result, "result"); ContractUtils.RequiresNotNull(method, "method"); ContractUtils.RequiresNotNull(nameDispenser, "nameDispenser"); MethodInfo methodInfo = method as MethodInfo; if (methodInfo != null) { FormatTypeName(result, methodInfo.ReturnType, nameDispenser); result.Append(' '); } #if FEATURE_REFEMIT MethodBuilder builder = method as MethodBuilder; if (builder != null) { result.Append(builder.Signature); return result; } ConstructorBuilder cb = method as ConstructorBuilder; if (cb != null) { result.Append(cb.Signature); return result; } #endif FormatTypeName(result, method.DeclaringType, nameDispenser); result.Append("::"); result.Append(method.Name); if (!method.IsConstructor) { FormatTypeArgs(result, method.GetGenericArguments(), nameDispenser); } result.Append("("); if (!method.ContainsGenericParameters) { ParameterInfo[] ps = method.GetParameters(); for (int i = 0; i < ps.Length; i++) { if (i > 0) result.Append(", "); FormatTypeName(result, ps[i].ParameterType, nameDispenser); if (!System.String.IsNullOrEmpty(ps[i].Name)) { result.Append(" "); result.Append(ps[i].Name); } } } else { result.Append("?"); } result.Append(")"); return result; } #endif public static StringBuilder FormatTypeName(StringBuilder result, Type type) { return FormatTypeName(result, type, (t) => t.FullName); } public static StringBuilder FormatTypeName(StringBuilder result, Type type, Func nameDispenser) { ContractUtils.RequiresNotNull(result, "result"); ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(nameDispenser, "nameDispenser"); if (type.IsGenericType()) { Type genType = type.GetGenericTypeDefinition(); string genericName = nameDispenser(genType).Replace('+', '.'); int tickIndex = genericName.IndexOf('`'); result.Append(tickIndex != -1 ? genericName.Substring(0, tickIndex) : genericName); Type[] typeArgs = type.GetGenericArguments(); if (type.IsGenericTypeDefinition()) { result.Append('<'); result.Append(',', typeArgs.Length - 1); result.Append('>'); } else { FormatTypeArgs(result, typeArgs, nameDispenser); } } else if (type.IsGenericParameter) { result.Append(type.Name); } else { // cut namespace off: result.Append(nameDispenser(type).Replace('+', '.')); } return result; } public static StringBuilder FormatTypeArgs(StringBuilder result, Type[] types) { return FormatTypeArgs(result, types, (t) => t.FullName); } public static StringBuilder FormatTypeArgs(StringBuilder result, Type[] types, Func nameDispenser) { ContractUtils.RequiresNotNull(result, "result"); ContractUtils.RequiresNotNullItems(types, "types"); ContractUtils.RequiresNotNull(nameDispenser, "nameDispenser"); if (types.Length > 0) { result.Append("<"); for (int i = 0; i < types.Length; i++) { if (i > 0) result.Append(", "); FormatTypeName(result, types[i], nameDispenser); } result.Append(">"); } return result; } internal static string ToValidTypeName(string str) { if (String.IsNullOrEmpty(str)) { return "_"; } StringBuilder sb = new StringBuilder(str); for (int i = 0; i < str.Length; i++) { if (str[i] == '\0' || str[i] == '.' || str[i] == '*' || str[i] == '+' || str[i] == '[' || str[i] == ']' || str[i] == '\\') { sb[i] = '_'; } } return sb.ToString(); } public static string GetNormalizedTypeName(Type type) { string name = type.Name; if (type.IsGenericType()) { return GetNormalizedTypeName(name); } return name; } public static string GetNormalizedTypeName(string typeName) { Debug.Assert(typeName.IndexOf('.') == -1); // This is the simple name, not the full name int backtick = typeName.IndexOf(ReflectionUtils.GenericArityDelimiter); if (backtick != -1) return typeName.Substring(0, backtick); return typeName; } #endregion #region Delegates and Dynamic Methods #if WP75 /// /// Creates an open delegate for the given (dynamic)method. /// public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) { return CreateDelegate(methodInfo, delegateType, null); } /// /// Creates a closed delegate for the given (dynamic)method. /// public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) { return Delegate.CreateDelegate(delegateType, target, methodInfo); } #elif !WIN8 /// /// Creates an open delegate for the given (dynamic)method. /// public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) { return CreateDelegate(methodInfo, delegateType, null); } /// /// Creates a closed delegate for the given (dynamic)method. /// public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) { #if FEATURE_REFEMIT DynamicMethod dm = methodInfo as DynamicMethod; if (dm != null) { return dm.CreateDelegate(delegateType, target); #endif return Delegate.CreateDelegate(delegateType, target, methodInfo); } #endif #if FEATURE_LCG public static bool IsDynamicMethod(MethodBase method) { return !PlatformAdaptationLayer.IsCompactFramework && IsDynamicMethodInternal(method); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsDynamicMethodInternal(MethodBase method) { return method is DynamicMethod; } #else public static bool IsDynamicMethod(MethodBase method) { return false; } #endif public static void GetDelegateSignature(Type delegateType, out ParameterInfo[] parameterInfos, out ParameterInfo returnInfo) { ContractUtils.RequiresNotNull(delegateType, "delegateType"); MethodInfo invokeMethod = delegateType.GetMethod("Invoke"); ContractUtils.Requires(invokeMethod != null, "delegateType", Strings.InvalidDelegate); parameterInfos = invokeMethod.GetParameters(); returnInfo = invokeMethod.ReturnParameter; } /// /// Gets a Func of CallSite, object * paramCnt, object delegate type /// that's suitable for use in a non-strongly typed call site. /// public static Type GetObjectCallSiteDelegateType(int paramCnt) { switch (paramCnt) { case 0: return typeof(Func); case 1: return typeof(Func); case 2: return typeof(Func); case 3: return typeof(Func); case 4: return typeof(Func); case 5: return typeof(Func); case 6: return typeof(Func); case 7: return typeof(Func); case 8: return typeof(Func); case 9: return typeof(Func); case 10: return typeof(Func); case 11: return typeof(Func); case 12: return typeof(Func); case 13: return typeof(Func); case 14: return typeof(Func); default: #if FEATURE_REFEMIT Type[] paramTypes = new Type[paramCnt + 2]; paramTypes[0] = typeof(CallSite); paramTypes[1] = typeof(object); for (int i = 0; i < paramCnt; i++) { paramTypes[i + 2] = typeof(object); } return Snippets.Shared.DefineDelegate("InvokeDelegate" + paramCnt, typeof(object), paramTypes); #else throw new NotSupportedException("Signature not supported on this platform."); #endif } } #if FEATURE_LCG [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework")] internal static DynamicMethod RawCreateDynamicMethod(string name, Type returnType, Type[] parameterTypes) { #if SILVERLIGHT // Module-hosted DynamicMethod is not available in SILVERLIGHT return new DynamicMethod(name, returnType, parameterTypes); #else // // WARNING: we set restrictedSkipVisibility == true (last parameter) // setting this bit will allow accessing nonpublic members // for more information see http://msdn.microsoft.com/en-us/library/bb348332.aspx // return new DynamicMethod(name, returnType, parameterTypes, true); #endif } #endif #endregion #region Methods and Parameters public static MethodBase[] GetMethodInfos(MemberInfo[] members) { return ArrayUtils.ConvertAll( members, delegate(MemberInfo inp) { return (MethodBase)inp; }); } public static Type[] GetParameterTypes(ParameterInfo[] parameterInfos) { return GetParameterTypes((IList)parameterInfos); } public static Type[] GetParameterTypes(IList parameterInfos) { Type[] result = new Type[parameterInfos.Count]; for (int i = 0; i < result.Length; i++) { result[i] = parameterInfos[i].ParameterType; } return result; } public static Type GetReturnType(this MethodBase mi) { return (mi.IsConstructor) ? mi.DeclaringType : ((MethodInfo)mi).ReturnType; } public static bool SignatureEquals(MethodInfo method, params Type[] requiredSignature) { ContractUtils.RequiresNotNull(method, "method"); Type[] actualTypes = ReflectionUtils.GetParameterTypes(method.GetParameters()); Debug.Assert(actualTypes.Length == requiredSignature.Length - 1); int i = 0; while (i < actualTypes.Length) { if (actualTypes[i] != requiredSignature[i]) return false; i++; } return method.ReturnType == requiredSignature[i]; } #if CLR2 && !SILVERLIGHT private static Type _ExtensionAttributeType; #endif [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public static bool IsExtension(this MemberInfo member) { var dlrExtension = typeof(ExtensionAttribute); if (member.IsDefined(dlrExtension, false)) { return true; } #if CLR2 && !SILVERLIGHT if (_ExtensionAttributeType == null) { try { _ExtensionAttributeType = Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") .GetType("System.Runtime.CompilerServices.ExtensionAttribute"); } catch { _ExtensionAttributeType = dlrExtension; } } if (_ExtensionAttributeType != dlrExtension) { return member.IsDefined(_ExtensionAttributeType, false); } #endif return false; } public static bool IsOutParameter(this ParameterInfo pi) { // not using IsIn/IsOut properties as they are not available in Silverlight: return pi.ParameterType.IsByRef && (pi.Attributes & (ParameterAttributes.Out | ParameterAttributes.In)) == ParameterAttributes.Out; } /// /// Returns true if the specified parameter is mandatory, i.e. is not optional and doesn't have a default value. /// public static bool IsMandatory(this ParameterInfo pi) { return (pi.Attributes & ParameterAttributes.Optional) == 0 && !pi.HasDefaultValue(); } public static bool HasDefaultValue(this ParameterInfo pi) { #if !FEATURE_DEFAULT_PARAMETER_VALUE return pi.IsDefined(typeof(DefaultParameterValueAttribute), false); #else return (pi.Attributes & ParameterAttributes.HasDefault) != 0; #endif } public static bool ProhibitsNull(this ParameterInfo parameter) { return parameter.IsDefined(typeof(NotNullAttribute), false); } public static bool ProhibitsNullItems(this ParameterInfo parameter) { return parameter.IsDefined(typeof(NotNullItemsAttribute), false); } public static bool IsParamArray(this ParameterInfo parameter) { return parameter.IsDefined(typeof(ParamArrayAttribute), false); } public static bool IsParamDictionary(this ParameterInfo parameter) { return parameter.IsDefined(typeof(ParamDictionaryAttribute), false); } public static bool IsParamsMethod(MethodBase method) { return IsParamsMethod(method.GetParameters()); } public static bool IsParamsMethod(ParameterInfo[] pis) { foreach (ParameterInfo pi in pis) { if (pi.IsParamArray() || pi.IsParamDictionary()) return true; } return false; } public static object GetDefaultValue(this ParameterInfo info) { #if !FEATURE_DEFAULT_PARAMETER_VALUE if (info.IsOptional) { return info.ParameterType == typeof(object) ? Missing.Value : ScriptingRuntimeHelpers.GetPrimitiveDefaultValue(info.ParameterType); } var defaultValueAttribute = info.GetCustomAttributes(typeof(DefaultParameterValueAttribute), false); if (defaultValueAttribute.Length > 0) { return ((DefaultParameterValueAttribute)defaultValueAttribute[0]).Value; } return null; #else return info.DefaultValue; #endif } #endregion #region Types /// /// Yields all ancestors of the given type including the type itself. /// Does not include implemented interfaces. /// public static IEnumerable Ancestors(this Type type) { do { yield return type; type = type.GetTypeInfo().BaseType; } while (type != null); } /// /// Like Type.GetInterfaces, but only returns the interfaces implemented by this type /// and not its parents. /// public static List GetDeclaredInterfaces(Type type) { IEnumerable baseInterfaces = (type.GetBaseType() != null) ? type.GetBaseType().GetInterfaces() : EmptyTypes; List interfaces = new List(); foreach (Type iface in type.GetInterfaces()) { if (!baseInterfaces.Contains(iface)) { interfaces.Add(iface); } } return interfaces; } internal static IEnumerable GetAllTypesFromAssembly(Assembly asm) { // TODO: WP7, SL5 #if SILVERLIGHT // ReflectionTypeLoadException try { return asm.GetTypes(); } catch (Exception) { return ReflectionUtils.EmptyTypes; } #elif WIN8 return asm.DefinedTypes; #else foreach (Module module in asm.GetModules()) { Type[] moduleTypes; try { moduleTypes = module.GetTypes(); } catch (ReflectionTypeLoadException e) { moduleTypes = e.Types; } foreach (var type in moduleTypes) { if (type != null) { yield return type; } } } #endif } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] internal static IEnumerable GetAllTypesFromAssembly(Assembly assembly, bool includePrivateTypes) { ContractUtils.RequiresNotNull(assembly, "assembly"); if (includePrivateTypes) { return GetAllTypesFromAssembly(assembly); } try { #if WIN8 return assembly.ExportedTypes.Select(t => t.GetTypeInfo()); #else return assembly.GetExportedTypes(); #endif } catch (NotSupportedException) { // GetExportedTypes does not work with dynamic assemblies } catch (Exception) { // Some type loads may cause exceptions. Unfortunately, there is no way to ask GetExportedTypes // for just the list of types that we successfully loaded. } return GetAllTypesFromAssembly(assembly).Where(type => type.IsPublic); } #endregion #region Type Builder #if FEATURE_REFEMIT #if WIN8 // TODO: what is ReservedMask? private const MethodAttributes MethodAttributesToEraseInOveride = MethodAttributes.Abstract | (MethodAttributes)0xD000; #else private const MethodAttributes MethodAttributesToEraseInOveride = MethodAttributes.Abstract | MethodAttributes.ReservedMask; #endif public static MethodBuilder DefineMethodOverride(TypeBuilder tb, MethodAttributes extra, MethodInfo decl) { MethodAttributes finalAttrs = (decl.Attributes & ~MethodAttributesToEraseInOveride) | extra; if (!decl.DeclaringType.GetTypeInfo().IsInterface) { finalAttrs &= ~MethodAttributes.NewSlot; } if ((extra & MethodAttributes.MemberAccessMask) != 0) { // remove existing member access, add new member access finalAttrs &= ~MethodAttributes.MemberAccessMask; finalAttrs |= extra; } MethodBuilder impl = tb.DefineMethod(decl.Name, finalAttrs, decl.CallingConvention); CopyMethodSignature(decl, impl, false); return impl; } public static void CopyMethodSignature(MethodInfo from, MethodBuilder to, bool substituteDeclaringType) { ParameterInfo[] paramInfos = from.GetParameters(); Type[] parameterTypes = new Type[paramInfos.Length]; Type[][] parameterRequiredModifiers = null, parameterOptionalModifiers = null; Type[] returnRequiredModifiers = null, returnOptionalModifiers = null; #if FEATURE_CUSTOM_MODIFIERS returnRequiredModifiers = from.ReturnParameter.GetRequiredCustomModifiers(); returnOptionalModifiers = from.ReturnParameter.GetOptionalCustomModifiers(); #endif for (int i = 0; i < paramInfos.Length; i++) { if (substituteDeclaringType && paramInfos[i].ParameterType == from.DeclaringType) { parameterTypes[i] = to.DeclaringType; } else { parameterTypes[i] = paramInfos[i].ParameterType; } #if FEATURE_CUSTOM_MODIFIERS var mods = paramInfos[i].GetRequiredCustomModifiers(); if (mods.Length > 0) { if (parameterRequiredModifiers == null) { parameterRequiredModifiers = new Type[paramInfos.Length][]; } parameterRequiredModifiers[i] = mods; } mods = paramInfos[i].GetOptionalCustomModifiers(); if (mods.Length > 0) { if (parameterOptionalModifiers == null) { parameterOptionalModifiers = new Type[paramInfos.Length][]; } parameterOptionalModifiers[i] = mods; } #endif } to.SetSignature( from.ReturnType, returnRequiredModifiers, returnOptionalModifiers, parameterTypes, parameterRequiredModifiers, parameterOptionalModifiers ); CopyGenericMethodAttributes(from, to); for (int i = 0; i < paramInfos.Length; i++) { to.DefineParameter(i + 1, paramInfos[i].Attributes, paramInfos[i].Name); } } private static void CopyGenericMethodAttributes(MethodInfo from, MethodBuilder to) { if (from.IsGenericMethodDefinition) { Type[] args = from.GetGenericArguments(); string[] names = new string[args.Length]; for (int i = 0; i < args.Length; i++) { names[i] = args[i].Name; } var builders = to.DefineGenericParameters(names); for (int i = 0; i < args.Length; i++) { // Copy template parameter attributes builders[i].SetGenericParameterAttributes(args[i].GetGenericParameterAttributes()); // Copy template parameter constraints Type[] constraints = args[i].GetGenericParameterConstraints(); List interfaces = new List(constraints.Length); foreach (Type constraint in constraints) { if (constraint.IsInterface()) { interfaces.Add(constraint); } else { builders[i].SetBaseTypeConstraint(constraint); } } if (interfaces.Count > 0) { builders[i].SetInterfaceConstraints(interfaces.ToArray()); } } } } #endif #endregion #region Extension Methods public static IEnumerable GetVisibleExtensionMethods(Assembly assembly) { #if FEATURE_METADATA_READER if (!assembly.IsDynamic && AppDomain.CurrentDomain.IsFullyTrusted) { try { return GetVisibleExtensionMethodsFast(assembly); } catch (SecurityException) { // full-demand can still fail if there is a partial trust domain on the stack } } #endif return GetVisibleExtensionMethodsSlow(assembly); } #if FEATURE_METADATA_READER [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] [MethodImpl(MethodImplOptions.NoInlining)] private static IEnumerable GetVisibleExtensionMethodsFast(Assembly assembly) { // Security: link demand return MetadataServices.GetVisibleExtensionMethodInfos(assembly); } #endif // TODO: make internal // TODO: handle type load exceptions public static IEnumerable GetVisibleExtensionMethodsSlow(Assembly assembly) { var ea = typeof(ExtensionAttribute); if (assembly.IsDefined(ea)) { foreach (TypeInfo type in ReflectionUtils.GetAllTypesFromAssembly(assembly)) { if ((type.IsPublic || type.IsNestedPublic) && type.IsAbstract && type.IsSealed && type.IsDefined(ea, false)) { foreach (MethodInfo method in type.AsType().GetDeclaredMethods()) { if (method.IsPublic && method.IsStatic && method.IsDefined(ea, false)) { yield return method; } } } } } } // Value is null if there are no extension methods in the assembly. private static Dictionary>> _extensionMethodsCache; /// /// Enumerates extension methods in given assembly. Groups the methods by declaring namespace. /// Uses a global cache if is true. /// public static IEnumerable>> GetVisibleExtensionMethodGroups(Assembly/*!*/ assembly, bool useCache) { #if !CLR2 && FEATURE_REFEMIT useCache &= !assembly.IsDynamic; #endif if (useCache) { if (_extensionMethodsCache == null) { _extensionMethodsCache = new Dictionary>>(); } lock (_extensionMethodsCache) { Dictionary> existing; if (_extensionMethodsCache.TryGetValue(assembly, out existing)) { return EnumerateExtensionMethods(existing); } } } Dictionary> result = null; foreach (MethodInfo method in ReflectionUtils.GetVisibleExtensionMethodsSlow(assembly)) { if (method.DeclaringType == null || method.DeclaringType.IsGenericTypeDefinition()) { continue; } var parameters = method.GetParameters(); if (parameters.Length == 0) { continue; } Type type = parameters[0].ParameterType; if (type.IsByRef || type.IsPointer) { continue; } string ns = method.DeclaringType.Namespace ?? String.Empty; List extensions = null; if (result == null) { result = new Dictionary>(); } if (!result.TryGetValue(ns, out extensions)) { result.Add(ns, extensions = new List()); } extensions.Add(new ExtensionMethodInfo(type, method)); } if (useCache) { lock (_extensionMethodsCache) { _extensionMethodsCache[assembly] = result; } } return EnumerateExtensionMethods(result); } // TODO: GetVisibleExtensionMethods(Hashset namespaces, Type type, string methodName) : IEnumerable {} private static IEnumerable>> EnumerateExtensionMethods(Dictionary> dict) { if (dict != null) { foreach (var entry in dict) { yield return new KeyValuePair>(entry.Key, new ReadOnlyCollection(entry.Value)); } } } #endregion #region Generic Types internal static Dictionary BindGenericParameters(Type/*!*/ openType, Type/*!*/ closedType, bool ignoreUnboundParameters) { var binding = new Dictionary(); BindGenericParameters(openType, closedType, (parameter, type) => { Type existing; if (binding.TryGetValue(parameter, out existing)) { return type == existing; } binding[parameter] = type; return true; }); return ConstraintsViolated(binding, ignoreUnboundParameters) ? null : binding; } /// /// Binds occurances of generic parameters in against corresponding types in . /// Invokes (parameter, type) for each such binding. /// Returns false if the is structurally different from or if the binder returns false. /// internal static bool BindGenericParameters(Type/*!*/ openType, Type/*!*/ closedType, Func/*!*/ binder) { if (openType.IsGenericParameter) { return binder(openType, closedType); } if (openType.IsArray) { if (!closedType.IsArray) { return false; } return BindGenericParameters(openType.GetElementType(), closedType.GetElementType(), binder); } if (!openType.IsGenericType() || !closedType.IsGenericType()) { return openType == closedType; } if (openType.GetGenericTypeDefinition() != closedType.GetGenericTypeDefinition()) { return false; } Type[] closedArgs = closedType.GetGenericArguments(); Type[] openArgs = openType.GetGenericArguments(); for (int i = 0; i < openArgs.Length; i++) { if (!BindGenericParameters(openArgs[i], closedArgs[i], binder)) { return false; } } return true; } internal static bool ConstraintsViolated(Dictionary/*!*/ binding, bool ignoreUnboundParameters) { foreach (var entry in binding) { if (ConstraintsViolated(entry.Key, entry.Value, binding, ignoreUnboundParameters)) { return true; } } return false; } internal static bool ConstraintsViolated(Type/*!*/ genericParameter, Type/*!*/ closedType, Dictionary/*!*/ binding, bool ignoreUnboundParameters) { if ((genericParameter.GetGenericParameterAttributes() & GenericParameterAttributes.ReferenceTypeConstraint) != 0 && closedType.IsValueType()) { // value type to parameter type constrained as class return true; } if ((genericParameter.GetGenericParameterAttributes() & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0 && (!closedType.IsValueType() || (closedType.IsGenericType() && closedType.GetGenericTypeDefinition() == typeof(Nullable<>)))) { // nullable or class/interface to parameter type constrained as struct return true; } if ((genericParameter.GetGenericParameterAttributes() & GenericParameterAttributes.DefaultConstructorConstraint) != 0 && (!closedType.IsValueType() && closedType.GetConstructor(ReflectionUtils.EmptyTypes) == null)) { // reference type w/o a default constructor to type constrianed as new() return true; } Type[] constraints = genericParameter.GetGenericParameterConstraints(); for (int i = 0; i < constraints.Length; i++) { Type instantiation = InstantiateConstraint(constraints[i], binding); if (instantiation == null) { if (ignoreUnboundParameters) { continue; } else { return true; } } if (!instantiation.IsAssignableFrom(closedType)) { return true; } } return false; } internal static Type InstantiateConstraint(Type/*!*/ constraint, Dictionary/*!*/ binding) { Debug.Assert(!constraint.IsArray && !constraint.IsByRef && !constraint.IsGenericTypeDefinition()); if (!constraint.ContainsGenericParameters()) { return constraint; } Type closedType; if (constraint.IsGenericParameter) { return binding.TryGetValue(constraint, out closedType) ? closedType : null; } Type[] args = constraint.GetGenericArguments(); for (int i = 0; i < args.Length; i++) { if ((args[i] = InstantiateConstraint(args[i], binding)) == null) { return null; } } return constraint.GetGenericTypeDefinition().MakeGenericType(args); } #endregion } public struct ExtensionMethodInfo : IEquatable { private readonly Type/*!*/ _extendedType; // cached type of the first parameter private readonly MethodInfo/*!*/ _method; internal ExtensionMethodInfo(Type/*!*/ extendedType, MethodInfo/*!*/ method) { Assert.NotNull(extendedType, method); _extendedType = extendedType; _method = method; } public Type/*!*/ ExtendedType { get { return _extendedType; } } public MethodInfo/*!*/ Method { get { return _method; } } public override bool Equals(object obj) { return obj is ExtensionMethodInfo && Equals((ExtensionMethodInfo)obj); } public bool Equals(ExtensionMethodInfo other) { return _method.Equals(other._method); } public static bool operator ==(ExtensionMethodInfo self, ExtensionMethodInfo other) { return self.Equals(other); } public static bool operator !=(ExtensionMethodInfo self, ExtensionMethodInfo other) { return !self.Equals(other); } public override int GetHashCode() { return _method.GetHashCode(); } /// /// Determines if a given type matches the type that the method extends. /// The match might be non-trivial if the extended type is an open generic type with constraints. /// public bool IsExtensionOf(Type/*!*/ type) { ContractUtils.RequiresNotNull(type, "type"); #if FEATURE_TYPE_EQUIVALENCE if (type.IsEquivalentTo(ExtendedType)) { return true; } #else if (type == _extendedType) { return true; } #endif if (!_extendedType.GetTypeInfo().ContainsGenericParameters) { return false; } // // Ignores constraints that can't be instantiated given the information we have (type of the first parameter). // // For example, // void Foo(this S x, T y) where S : T; // // We make such methods available on all types. // If they are not called with arguments that satisfy the constraint the overload resolver might fail. // return ReflectionUtils.BindGenericParameters(_extendedType, type, true) != null; } } }