/* ****************************************************************************
 *
 * 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 !FEATURE_TYPE_INFO
using TypeInfo = System.Type;
#endif

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
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 || NETSTANDARD
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 {
        /// <summary>Specifies that instance members are to be included in the search.</summary>
        Instance = 4,
        /// <summary>Specifies that static members are to be included in the search.</summary>
        Static = 8,
        /// <summary>Specifies that public members are to be included in the search.</summary>
        Public = 16,
        /// <summary>Specifies that non-public members are to be included in the search.</summary>
        NonPublic = 32
    }
}
#elif !CLR45
namespace System.Reflection {
    public static class RuntimeReflectionExtensions {
        public static MethodInfo GetRuntimeBaseDefinition(this MethodInfo method) {
            return method.GetBaseDefinition();
        }

        public static IEnumerable<MethodInfo> GetRuntimeMethods(this Type type) {
            return type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
        }

#if !FEATURE_GET_METHOD_INFO
        public static MethodInfo GetMethodInfo(this Delegate d) {
            return d.Method;
        }
#endif

#if !FEATURE_TYPE_INFO
        public static InterfaceMapping GetRuntimeInterfaceMap(this Type typeInfo, Type interfaceType) {
            return typeInfo.GetInterfaceMap(interfaceType);
        }
#endif
    }
}
#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
    /// <summary>
    /// The Default Parameter Value Attribute.
    /// </summary>
    public sealed class DefaultParameterValueAttribute : Attribute
    {
        private readonly object _value;

        public object Value
        {
            get { return _value; }
        }

        /// <summary>
        /// The constructor
        /// </summary>
        /// <param name="value">The value.</param>
        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<T> WithBindingFlags<T>(this IEnumerable<T> members, Func<T, BindingFlags, bool> matchFlags, BindingFlags flags)
            where T : MemberInfo {
            return members.Where(member => matchFlags(member, flags));
        }

        public static IEnumerable<MemberInfo> WithBindingFlags(this IEnumerable<MemberInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<MethodInfo> WithBindingFlags(this IEnumerable<MethodInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<ConstructorInfo> WithBindingFlags(this IEnumerable<ConstructorInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<FieldInfo> WithBindingFlags(this IEnumerable<FieldInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<PropertyInfo> WithBindingFlags(this IEnumerable<PropertyInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<EventInfo> WithBindingFlags(this IEnumerable<EventInfo> members, BindingFlags flags) {
            return members.WithBindingFlags(MatchesFlags, flags);
        }

        public static IEnumerable<TypeInfo> WithBindingFlags(this IEnumerable<TypeInfo> 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<MethodInfo> WithSignature(this IEnumerable<MethodInfo> 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<ConstructorInfo> WithSignature(this IEnumerable<ConstructorInfo> 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<MethodInfo> GetInheritedMethods(this Type type, string name = null, bool flattenHierarchy = false) {
            while (type.IsGenericParameter) {
                type = type.GetBaseType();
            }

            var baseDefinitions = new HashSet<MethodInfo>(ReferenceEqualityComparer<MethodInfo>.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<MethodInfo> 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<PropertyInfo> GetInheritedProperties(this Type type, string name = null, bool flattenHierarchy = false) {
            while (type.IsGenericParameter) {
                type = type.GetBaseType();
            }

            var baseDefinitions = new HashSet<MethodInfo>(ReferenceEqualityComparer<MethodInfo>.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<MethodInfo> 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<EventInfo> GetInheritedEvents(this Type type, string name = null, bool flattenHierarchy = false) {
            while (type.IsGenericParameter) {
                type = type.GetBaseType();
            }

            var baseDefinitions = new HashSet<MethodInfo>(ReferenceEqualityComparer<MethodInfo>.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<MethodInfo> 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<FieldInfo> 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<MemberInfo> GetInheritedMembers(this Type type, string name = null, bool flattenHierarchy = false) {
            var result =
                type.GetInheritedMethods(name, flattenHierarchy).Cast<MethodInfo, MemberInfo>().Concat(
                type.GetInheritedProperties(name, flattenHierarchy).Cast<PropertyInfo, MemberInfo>().Concat(
                type.GetInheritedEvents(name, flattenHierarchy).Cast<EventInfo, MemberInfo>().Concat(
                type.GetInheritedFields(name, flattenHierarchy).Cast<FieldInfo, MemberInfo>())));

            if (name == null) {
                return result.Concat<MemberInfo>(
                    type.GetDeclaredConstructors().Cast<ConstructorInfo, MemberInfo>().Concat(
                    type.GetDeclaredNestedTypes().Cast<TypeInfo, MemberInfo>()));
            }

            var nestedType = type.GetDeclaredNestedType(name);
            return (nestedType != null) ? result.Concat(new[] { nestedType }) : result;
        }

        #endregion

        #region Declared Members

        public static IEnumerable<ConstructorInfo> GetDeclaredConstructors(this Type type) {
#if FEATURE_TYPE_INFO
            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<MethodInfo> GetDeclaredMethods(this Type type, string name = null) {
#if FEATURE_TYPE_INFO
            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<MethodInfo>();
            }
#endif
        }

        public static IEnumerable<PropertyInfo> GetDeclaredProperties(this Type type) {
#if FEATURE_TYPE_INFO
            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 FEATURE_TYPE_INFO
            return type.GetTypeInfo().GetDeclaredProperty(name);
#else
            return type.GetProperty(name, BindingFlags.DeclaredOnly | AllMembers);
#endif
        }

        public static IEnumerable<EventInfo> GetDeclaredEvents(this Type type) {
#if FEATURE_TYPE_INFO
            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 FEATURE_TYPE_INFO
            return type.GetTypeInfo().GetDeclaredEvent(name);
#else
            return type.GetEvent(name, BindingFlags.DeclaredOnly | AllMembers);
#endif
        }

        public static IEnumerable<FieldInfo> GetDeclaredFields(this Type type) {
#if FEATURE_TYPE_INFO
            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 FEATURE_TYPE_INFO
            return type.GetTypeInfo().GetDeclaredField(name);
#else
            return type.GetField(name, BindingFlags.DeclaredOnly | AllMembers);
#endif
        }

        public static IEnumerable<TypeInfo> GetDeclaredNestedTypes(this Type type) {
#if FEATURE_TYPE_INFO
            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 FEATURE_TYPE_INFO
            return type.GetTypeInfo().GetDeclaredNestedType(name);
#else
            return type.GetNestedType(name, BindingFlags.DeclaredOnly | AllMembers);
#endif
        }

        public static IEnumerable<MemberInfo> 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<MemberInfo> 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<Type> GetImplementedInterfaces(this Type type) {
            return type.GetTypeInfo().ImplementedInterfaces;
        }

#if !NETSTANDARD
        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();
        }
#endif

        private static IEnumerable<MethodInfo> GetMethods(this Type type, string name, BindingFlags bindingFlags) {
            return type.GetTypeInfo().GetDeclaredMethods(name).WithBindingFlags(bindingFlags);
        }


        public static Type CreateType(this TypeBuilder builder) {
            return builder.CreateTypeInfo().AsType();
        }

#if !NETSTANDARD
        public static object GetRawConstantValue(this FieldInfo field) {
            return ((dynamic)field).GetRawConstantValue();
        }

        public static int GetMetadataToken(this MemberInfo member) {
            return ((dynamic)member).MetadataToken;
        }
#endif

        public static Module GetModule(this MemberInfo member) {
            return member.Module;
        }

#if !NETSTANDARD
        public static Type[] GetGenericArguments(this Type type) {
            return type.GetTypeInfo().GenericTypeArguments;
        }

        public static bool IsAssignableFrom(this Type type, Type other) {
            return type.GetTypeInfo().IsAssignableFrom(other.GetTypeInfo());
        }
#endif

        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);
        }

#if !NETSTANDARD
        public static IEnumerable<Type> GetInterfaces(this Type type) {
            return type.GetTypeInfo().ImplementedInterfaces;
        }
#endif

        public static Type[] GetRequiredCustomModifiers(this ParameterInfo parameter) {
            return EmptyTypes;
        }

        public static Type[] GetOptionalCustomModifiers(this ParameterInfo parameter) {
            return EmptyTypes;
        }

        public static IEnumerable<Module> 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<DefaultMemberAttribute>().SingleOrDefault();
                if (attr != null) {
                    return attr.MemberName;
                }
            }

            return null;
        }

#if !NETSTANDARD
        public static IEnumerable<MemberInfo> 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<MemberInfo>();
        }
#endif
#else

        public static IEnumerable<Module> GetModules(this Assembly assembly) {
            return assembly.GetModules();
        }

        public static IEnumerable<Type> GetImplementedInterfaces(this Type type) {
            return type.GetInterfaces();
        }

        public static TypeCode GetTypeCode(this Type type) {
            return Type.GetTypeCode(type);
        }

        public static int GetMetadataToken(this MemberInfo member) {
            return member.MetadataToken;
        }

        public static Module GetModule(this MemberInfo member) {
            return member.Module;
        }

        public static bool IsDefined(this Assembly assembly, Type attributeType) {
            return assembly.IsDefined(attributeType, false);
        }

        public static T GetCustomAttribute<T>(this Assembly assembly, bool inherit = false) where T : Attribute {
            return (T)Attribute.GetCustomAttribute(assembly, typeof(T), inherit);
        }

        public static T GetCustomAttribute<T>(this MemberInfo member, bool inherit = false) where T : Attribute {
            return (T)Attribute.GetCustomAttribute(member, typeof(T), inherit);
        }

        public static IEnumerable<T> GetCustomAttributes<T>(this Assembly assembly, bool inherit = false) where T : Attribute {
            return Attribute.GetCustomAttributes(assembly, typeof(T), inherit).Cast<T>();
        }

        public static IEnumerable<T> GetCustomAttributes<T>(this MemberInfo member, bool inherit = false) where T : Attribute {
            return Attribute.GetCustomAttributes(member, typeof(T), inherit).Cast<T>();
        }
#endif

        public static MethodInfo GetMethod(this Delegate d) {
            return d.GetMethodInfo();
        }

        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];

        #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<T> 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<Type, string> 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<Type, string> 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<Type, string> 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
        /// <summary>
        /// Creates an open delegate for the given (dynamic)method.
        /// </summary>
        public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) {
            return CreateDelegate(methodInfo, delegateType, null);
        }

        /// <summary>
        /// Creates a closed delegate for the given (dynamic)method.
        /// </summary>
        public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) {
            return Delegate.CreateDelegate(delegateType, target, methodInfo);
        }
#elif !WIN8 && !NETSTANDARD
        /// <summary>
        /// Creates an open delegate for the given (dynamic)method.
        /// </summary>
        public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType) {
            return CreateDelegate(methodInfo, delegateType, null);
        }

        /// <summary>
        /// Creates a closed delegate for the given (dynamic)method.
        /// </summary>
        public static Delegate CreateDelegate(this MethodInfo methodInfo, Type delegateType, object target) {
            DynamicMethod dm = methodInfo as DynamicMethod;
            if (dm != null) {
                return dm.CreateDelegate(delegateType, target);
            } else {
                return Delegate.CreateDelegate(delegateType, target, methodInfo);
            }
        }
#endif

        public static bool IsDynamicMethod(MethodBase method) {
            return !PlatformAdaptationLayer.IsCompactFramework && IsDynamicMethodInternal(method);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static bool IsDynamicMethodInternal(MethodBase method) {
            return method is DynamicMethod;
        }

        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;
        }

        /// <summary>
        /// Gets a Func of CallSite, object * paramCnt, object delegate type
        /// that's suitable for use in a non-strongly typed call site.
        /// </summary>
        public static Type GetObjectCallSiteDelegateType(int paramCnt) {
            switch (paramCnt) {
                case 0: return typeof(Func<CallSite, object, object>);
                case 1: return typeof(Func<CallSite, object, object, object>);
                case 2: return typeof(Func<CallSite, object, object, object, object>);
                case 3: return typeof(Func<CallSite, object, object, object, object, object>);
                case 4: return typeof(Func<CallSite, object, object, object, object, object, object>);
                case 5: return typeof(Func<CallSite, object, object, object, object, object, object, object>);
                case 6: return typeof(Func<CallSite, object, object, object, object, object, object, object, object>);
                case 7: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object>);
                case 8: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object>);
                case 9: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object>);
                case 10: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object, object>);
                case 11: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object, object, object>);
                case 12: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
                case 13: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
                case 14: return typeof(Func<CallSite, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>);
                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
            }
        }

        [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
        }

        #endregion

        #region Methods and Parameters

        public static MethodInfo[] GetMethodInfos(Delegate[] delegates) {
            MethodInfo[] result = new MethodInfo[delegates.Length];
            for (int i = 0; i < delegates.Length; i++) result[i] = delegates[i].GetMethod();
            return result;
        }

        public static MethodBase[] GetMethodInfos(MemberInfo[] members) {
            return ArrayUtils.ConvertAll<MemberInfo, MethodBase>(
                members,
                delegate(MemberInfo inp) { return (MethodBase)inp; });
        }

        public static Type[] GetParameterTypes(ParameterInfo[] parameterInfos) {
            return GetParameterTypes((IList<ParameterInfo>)parameterInfos);
        }

        public static Type[] GetParameterTypes(IList<ParameterInfo> 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;
        }

        /// <summary>
        /// Returns <c>true</c> if the specified parameter is mandatory, i.e. is not optional and doesn't have a default value.
        /// </summary>
        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

        /// <summary>
        /// Yields all ancestors of the given type including the type itself.
        /// Does not include implemented interfaces.
        /// </summary>
        public static IEnumerable<Type> Ancestors(this Type type) {
            do {
                yield return type;
                type = type.GetTypeInfo().BaseType;
            } while (type != null);
        }

        /// <summary>
        /// Like Type.GetInterfaces, but only returns the interfaces implemented by this type
        /// and not its parents.
        /// </summary>
        public static List<Type> GetDeclaredInterfaces(Type type) {
            IEnumerable<Type> baseInterfaces = (type.GetBaseType() != null) ? type.GetBaseType().GetInterfaces() : EmptyTypes;
            List<Type> interfaces = new List<Type>();
            foreach (Type iface in type.GetInterfaces()) {
                if (!baseInterfaces.Contains(iface)) {
                    interfaces.Add(iface);
                }
            }
            return interfaces;
        }

        internal static IEnumerable<TypeInfo> 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.GetTypeInfo();
                    }
                }
            }
#endif
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        internal static IEnumerable<TypeInfo> 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().Select(t => t.GetTypeInfo());
#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

        private const MethodAttributes MethodAttributesReservedMask = (MethodAttributes)0xD000; // MethodAttributes.ReservedMask
        private const MethodAttributes MethodAttributesToEraseInOveride = MethodAttributes.Abstract | MethodAttributesReservedMask;

        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<Type> interfaces = new List<Type>(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<MethodInfo> 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<MethodInfo> GetVisibleExtensionMethodsFast(Assembly assembly) {
            // Security: link demand
            return MetadataServices.GetVisibleExtensionMethodInfos(assembly);
        }
#endif

        // TODO: make internal
        // TODO: handle type load exceptions
        public static IEnumerable<MethodInfo> 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<Assembly, Dictionary<string, List<ExtensionMethodInfo>>> _extensionMethodsCache;

        /// <summary>
        /// Enumerates extension methods in given assembly. Groups the methods by declaring namespace.
        /// Uses a global cache if <paramref name="useCache"/> is true.
        /// </summary>
        public static IEnumerable<KeyValuePair<string, IEnumerable<ExtensionMethodInfo>>> GetVisibleExtensionMethodGroups(Assembly/*!*/ assembly, bool useCache) {
#if !CLR2 && FEATURE_REFEMIT
            useCache &= !assembly.IsDynamic;
#endif
            if (useCache) {
                if (_extensionMethodsCache == null) {
                    _extensionMethodsCache = new Dictionary<Assembly, Dictionary<string, List<ExtensionMethodInfo>>>();
                }

                lock (_extensionMethodsCache) {
                    Dictionary<string, List<ExtensionMethodInfo>> existing;
                    if (_extensionMethodsCache.TryGetValue(assembly, out existing)) {
                        return EnumerateExtensionMethods(existing);
                    }
                }
            }

            Dictionary<string, List<ExtensionMethodInfo>> 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<ExtensionMethodInfo> extensions = null;

                if (result == null) {
                    result = new Dictionary<string, List<ExtensionMethodInfo>>();
                }

                if (!result.TryGetValue(ns, out extensions)) {
                    result.Add(ns, extensions = new List<ExtensionMethodInfo>());
                }

                extensions.Add(new ExtensionMethodInfo(type, method));
            }

            if (useCache) {
                lock (_extensionMethodsCache) {
                    _extensionMethodsCache[assembly] = result;
                }
            }

            return EnumerateExtensionMethods(result);
        }

        // TODO: GetVisibleExtensionMethods(Hashset<string> namespaces, Type type, string methodName) : IEnumerable<MethodInfo> {}

        private static IEnumerable<KeyValuePair<string, IEnumerable<ExtensionMethodInfo>>> EnumerateExtensionMethods(Dictionary<string, List<ExtensionMethodInfo>> dict) {
            if (dict != null) {
                foreach (var entry in dict) {
                    yield return new KeyValuePair<string, IEnumerable<ExtensionMethodInfo>>(entry.Key, new ReadOnlyCollection<ExtensionMethodInfo>(entry.Value));
                }
            }
        }

        #endregion

        #region Generic Types

        internal static Dictionary<Type, Type> BindGenericParameters(Type/*!*/ openType, Type/*!*/ closedType, bool ignoreUnboundParameters) {
            var binding = new Dictionary<Type, Type>();
            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;
        }

        /// <summary>
        /// Binds occurances of generic parameters in <paramref name="openType"/> against corresponding types in <paramref name="closedType"/>.
        /// Invokes <paramref name="binder"/>(parameter, type) for each such binding.
        /// Returns false if the <paramref name="openType"/> is structurally different from <paramref name="closedType"/> or if the binder returns false.
        /// </summary>
        internal static bool BindGenericParameters(Type/*!*/ openType, Type/*!*/ closedType, Func<Type, Type, bool>/*!*/ 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<Type, Type>/*!*/ 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<Type, Type>/*!*/ 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<T> 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<Type, Type>/*!*/ 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<ExtensionMethodInfo> {
        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();
        }
        
        /// <summary>
        /// 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.
        /// </summary>
        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<S, T>(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;
        }
    }
}
