示例#1
0
        /// <summary>
        /// See if the given sets of parameters match each other
        /// </summary>
        /// <param name="parameters1">The first set of parameters</param>
        /// <param name="parameters2">The second set of parameters</param>
        /// <param name="noMatchOnGenericVersions">True to fail the match if either parameter is
        /// generic, false to allow matching to generic parameters even if the other isn't.</param>
        /// <returns>True if they match, false if not</returns>
        /// <param name="allowMismatchedArrayTypes">True to allow a match with mismatched array types or false to
        /// not allow a match.  If true, we're getting pretty desperate for a match.</param>
        /// <remarks>When <paramref name="noMatchOnGenericVersions"/> is true, it prevents matching a
        /// non-generic overload of the method to a generic version of the method.  This allows the
        /// non-generic version to be matched correctly (i.e. Contains(T) and Contains(Guid)).  If not
        /// done, the generic version is matched to both methods and the reflection info contains a
        /// duplicate generic method and loses the non-generic overload.</remarks>
        private static bool ParametersMatch(ParameterList parameters1, ParameterList parameters2,
                                            bool noMatchOnGenericVersions, bool allowMismatchedArrayTypes)
        {
            if (parameters1.Count != parameters2.Count)
            {
                return(false);
            }

            for (int i = 0; i < parameters1.Count; i++)
            {
                TypeNode type1 = parameters1[i].Type;
                TypeNode type2 = parameters2[i].Type;

                // !EFW - Fail the match if we are looking for a non-generic match
                if (noMatchOnGenericVersions && (type1.IsTemplateParameter || type2.IsTemplateParameter))
                {
                    return(false);
                }

                // We can't determine the equivalence of template parameters; this is probably not good
                if (type1.IsTemplateParameter || type2.IsTemplateParameter)
                {
                    // !EFW - As a fallback, compare the type parameter positions.  If they don't match, this
                    // probably isn't the one we want.
                    int p1 = GetTemplateParameterPosition(parameters1[i].DeclaringMethod.DeclaringType, type1.Name.Name),
                        p2 = GetTemplateParameterPosition(parameters2[i].DeclaringMethod.DeclaringType, type2.Name.Name);

                    if (p1 != -1 && p2 != -1 && p1 != p2)
                    {
                        // !EFW - Another test case supplied by Jared Moore.  If the types are something like
                        // MyBaseClass<T, T> and MyBaseClass<T, U> we can still provide a match by comparing
                        // all possible positions.  As long as they intersect, it's probably a good match.
                        var positions1 = GetTemplateParameterPositions(parameters1[i].DeclaringMethod.DeclaringType,
                                                                       type1.Name.Name);
                        var positions2 = GetTemplateParameterPositions(parameters2[i].DeclaringMethod.DeclaringType,
                                                                       type2.Name.Name);

                        // If we found any but none of them are the same, then no match.
                        if (positions1.Any() && positions2.Any() && !positions1.Intersect(positions2).Any())
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // The node type must be the same; this is probably a fast check
                    if (type1.NodeType != type2.NodeType)
                    {
                        return(false);
                    }

                    // If they are "normal" types, we will compare them.  Comparing arrays, pointers, etc. is
                    // dangerous, because the types they contain may be template parameters
                    if (type1.NodeType == NodeType.Class || type1.NodeType == NodeType.Struct ||
                        type1.NodeType == NodeType.Interface || type1.NodeType == NodeType.EnumNode ||
                        type1.NodeType == NodeType.DelegateNode)
                    {
                        type1 = type1.GetTemplateType();
                        type2 = type2.GetTemplateType();

                        if (!type2.IsStructurallyEquivalentTo(type1))
                        {
                            return(false);
                        }
                    }

                    // !EFW - Comparing array types may be dangerous but, as it turns out, is necessary.  If
                    // two overloads take an array as a parameter, it always returns the first overload as the
                    // match in derived types.  As such, we do need to compare the array element types.  For
                    // generic types, we can get the underlying template type from the declaring method's type
                    // and match that.
                    // https://github.com/EWSoftware/SHFB/issues/57
                    if (type1.NodeType == NodeType.ArrayType)
                    {
                        type1 = ((ArrayType)type1).ElementType;
                        type2 = ((ArrayType)type2).ElementType;

                        if (type2.IsTemplateParameter)
                        {
                            // Get the position from the second set of parameters
                            int pos = GetTemplateParameterPosition(parameters2[i].DeclaringMethod.DeclaringType,
                                                                   type2.Name.Name);

                            // Get the actual type from the first set of parameters
                            var declType = parameters1[i].DeclaringMethod.DeclaringType;

                            if (pos != -1 && declType.TemplateArguments != null && pos < declType.TemplateArguments.Count)
                            {
                                type2 = declType.TemplateArguments[pos];
                            }
                        }

                        if (type1.NodeType != type2.NodeType || !type2.IsStructurallyEquivalentTo(type1))
                        {
                            // !EFW - Yet another edge case to check.  In this case for example,
                            // KeyValue<int, int> didn't match KeyValue<TKey, TValue> and it failed to find any
                            // matches.  The fix is to see if both types are generic and compare the template
                            // parameter names.  This is getting rather complicated isn't it?
                            // https://github.com/EWSoftware/SHFB/issues/154
                            if (!type1.IsGeneric || !type2.IsGeneric || type1.Template == null || type2.Template == null ||
                                type1.Template.TemplateParameters.Count != type2.Template.TemplateParameters.Count ||
                                type1.Template.TemplateParameters.Select(t => t.Name.Name).Except(
                                    type2.Template.TemplateParameters.Select(t => t.Name.Name)).Count() != 0)
                            {
                                // If this is the last ditch attempt and were allowing mismatched array types,
                                // we're pretty much screwed so carry on.  This can happen in some really
                                // complex cases were we end up with an intrinsic type and a template parameter:
                                // https://github.com/EWSoftware/SHFB/issues/302
                                if (!allowMismatchedArrayTypes || type1.StructuralElementTypes == null ||
                                    type2.StructuralElementTypes == null || type1.StructuralElementTypes.Count == 0 ||
                                    type2.StructuralElementTypes.Count == 0 ||
                                    type1.StructuralElementTypes[0].IsTemplateParameter == type2.StructuralElementTypes[0].IsTemplateParameter)
                                {
                                    return(false);
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
示例#2
0
        //=====================================================================

        /// <summary>
        /// This finds all extension methods, adds information about them to the types, and tracks
        /// them for adding to the reflection data later in the other callbacks.
        /// </summary>
        /// <param name="writer">The reflection data XML writer</param>
        /// <param name="info">For this callback, the information object is a namespace list</param>
        private void RecordExtensionMethods(XmlWriter writer, object info)
        {
            NamespaceList spaces = (NamespaceList)info;

            foreach (Namespace space in spaces)
            {
                // !EFW - Don't bother checking unexposed namespaces
                if (!mrw.ApiFilter.IsExposedNamespace(space))
                {
                    continue;
                }

                TypeNodeList types = space.Types;

                foreach (TypeNode type in types)
                {
                    // !EFW - Don't bother checking unexposed types
                    if (!mrw.ApiFilter.IsExposedType(type))
                    {
                        continue;
                    }

                    // Go through the members looking for fields signaling extension methods.  Members may be
                    // added so convert to a list first to avoid enumeration issues.
                    foreach (Member member in type.Members.ToList())
                    {
                        Method method = member as Method;

                        if (method == null || !mrw.ApiFilter.IsExposedMember(method) ||
                            !method.Attributes.Any(a => a.Type.FullName == "System.Runtime.CompilerServices.ExtensionAttribute"))
                        {
                            continue;
                        }

                        ParameterList parameters = method.Parameters;

                        // !EFW - This fix was reported without an example.  Sometimes, there are no parameters.
                        // In such cases, ignore it to prevent a crash.
                        if (parameters == null || parameters.Count == 0)
                        {
                            continue;
                        }

                        TypeNode extendedType = parameters[0].Type;

                        // Recognize generic extension methods where the extended type is a specialization of a
                        // generic type and the extended type's specialized template argument is a type parameter
                        // declared by the generic extension method.  In this case, we need to save a TypeNode
                        // for the non-specialized type in the index because a TypeNode for the specialized type
                        // won't match correctly in AddExtensionMethods().  NOTE: we are not interested in
                        // extended types that are specialized by a specific type rather than by the extension
                        // method's template parameter.
                        if (method.IsGeneric && method.TemplateParameters.Count > 0)
                        {
                            if (extendedType.IsGeneric && extendedType.TemplateArguments != null &&
                                extendedType.TemplateArguments.Count == 1)
                            {
                                // Is the extended type's template argument a template parameter rather than a
                                // specialized type?
                                TypeNode arg = extendedType.TemplateArguments[0];

                                if (arg.IsTemplateParameter)
                                {
                                    // Is the template parameter declared on the extension method
                                    ITypeParameter gtp = (ITypeParameter)arg;

                                    if (gtp.DeclaringMember == method && gtp.ParameterListIndex == 0)
                                    {
                                        // Get a TypeNode for the non-specialized type
                                        extendedType = extendedType.GetTemplateType();
                                    }
                                }
                            }
                        }

                        List <Method> methods = null;

                        if (!index.TryGetValue(extendedType, out methods))
                        {
                            methods = new List <Method>();
                            index.Add(extendedType, methods);
                        }

                        methods.Add(method);
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        /// See if the given sets of parameters match each other
        /// </summary>
        /// <param name="parameters1">The first set of parameters</param>
        /// <param name="parameters2">The second set of parameters</param>
        /// <param name="noMatchOnGenericVersions">True to fail the match if either parameter is
        /// generic, false to allow matching to generic parameters even if the other isn't.</param>
        /// <returns>True if they match, false if not</returns>
        /// <remarks>When <paramref name="noMatchOnGenericVersions"/> is true, it prevents matching a
        /// non-generic overload of the method to a generic version of the method.  This allows the
        /// non-generic version to be matched correctly (i.e. Contains(T) and Contains(Guid)).  If not
        /// done, the generic version is matched to both methods and the reflection info contains a
        /// duplicate generic method and loses the non-generic overload.</remarks>
        private static bool ParametersMatch(ParameterList parameters1, ParameterList parameters2,
                                            bool noMatchOnGenericVersions)
        {
            if (parameters1.Count != parameters2.Count)
            {
                return(false);
            }

            for (int i = 0; i < parameters1.Count; i++)
            {
                TypeNode type1 = parameters1[i].Type;
                TypeNode type2 = parameters2[i].Type;

                // !EFW - Fail the match if we are looking for a non-generic match
                if (noMatchOnGenericVersions && (type1.IsTemplateParameter || type2.IsTemplateParameter))
                {
                    return(false);
                }

                // We can't determine the equivalence of template parameters; this is probably not good
                if (type1.IsTemplateParameter || type2.IsTemplateParameter)
                {
                    // !EFW - As a fallback, compare the type parameter positions.  If they don't match, this
                    // probably isn't the one we want.
                    int p1 = GetTemplateParameterPosition(parameters1[i].DeclaringMethod.DeclaringType, type1.Name.Name),
                        p2 = GetTemplateParameterPosition(parameters2[i].DeclaringMethod.DeclaringType, type2.Name.Name);

                    if (p1 != -1 && p2 != -1 && p1 != p2)
                    {
                        // !EFW - Another test case supplied by Jared Moore.  If the types are something like
                        // MyBaseClass<T, T> and MyBaseClass<T, U> we can still provide a match by comparing
                        // all possible positions.  As long as they intersect, it's probably a good match.
                        var positions1 = GetTemplateParameterPositions(parameters1[i].DeclaringMethod.DeclaringType,
                                                                       type1.Name.Name);
                        var positions2 = GetTemplateParameterPositions(parameters2[i].DeclaringMethod.DeclaringType,
                                                                       type2.Name.Name);

                        // If we found any but none of them are the same, then no match.
                        if (positions1.Any() && positions2.Any() && !positions1.Intersect(positions2).Any())
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // The node type must be the same; this is probably a fast check
                    if (type1.NodeType != type2.NodeType)
                    {
                        return(false);
                    }

                    // If they are "normal" types, we will compare them.  Comparing arrays, pointers, etc. is
                    // dangerous, because the types they contain may be template parameters
                    if (type1.NodeType == NodeType.Class || type1.NodeType == NodeType.Struct ||
                        type1.NodeType == NodeType.Interface || type1.NodeType == NodeType.EnumNode ||
                        type1.NodeType == NodeType.DelegateNode)
                    {
                        type1 = type1.GetTemplateType();
                        type2 = type2.GetTemplateType();

                        if (!type2.IsStructurallyEquivalentTo(type1))
                        {
                            return(false);
                        }
                    }

                    // !EFW - Comparing array types may be dangerous but, as it turns out, is necessary.  If
                    // two overloads take an array as a parameter, it always returns the first overload as the
                    // match in derived types.  As such, we do need to compare the array element types.  For
                    // generic types, we can get the underlying template type from the declaring method's type
                    // and match that.
                    // https://github.com/EWSoftware/SHFB/issues/57
                    if (type1.NodeType == NodeType.ArrayType)
                    {
                        type1 = ((ArrayType)type1).ElementType;
                        type2 = ((ArrayType)type2).ElementType;

                        if (type2.IsTemplateParameter)
                        {
                            // Get the position from the second set of parameters
                            int pos = GetTemplateParameterPosition(parameters2[i].DeclaringMethod.DeclaringType,
                                                                   type2.Name.Name);

                            // Get the actual type from the first set of parameters
                            var declType = parameters1[i].DeclaringMethod.DeclaringType;

                            if (pos != -1 && declType.TemplateArguments != null && pos < declType.TemplateArguments.Count)
                            {
                                type2 = declType.TemplateArguments[pos];
                            }
                        }

                        if (type1.NodeType != type2.NodeType || !type2.IsStructurallyEquivalentTo(type1))
                        {
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }
示例#4
0
        /// <summary>
        /// This is used to add the extension method elements to the type's member list
        /// </summary>
        /// <param name="writer">The reflection data XML writer</param>
        /// <param name="info">For this callback, this is a member dictionary</param>
        private void AddExtensionMethods(XmlWriter writer, object info)
        {
            MemberDictionary members = info as MemberDictionary;

            if (members == null)
            {
                return;
            }

            TypeNode type = members.Type;

            foreach (Interface contract in type.Interfaces)
            {
                List <Method> extensionMethods = null;

                if (index.TryGetValue(contract, out extensionMethods))
                {
                    foreach (Method extensionMethod in extensionMethods)
                    {
                        if (!IsExtensionMethodHidden(extensionMethod, members))
                        {
                            AddExtensionMethod(writer, type, extensionMethod, null);
                        }
                    }
                }

                if (contract.IsGeneric && contract.TemplateArguments != null && contract.TemplateArguments.Count > 0)
                {
                    Interface templateContract = (Interface)contract.GetTemplateType();
                    TypeNode  specialization   = contract.TemplateArguments[0];

                    if (index.TryGetValue(templateContract, out extensionMethods))
                    {
                        foreach (Method extensionMethod in extensionMethods)
                        {
                            if (IsValidTemplateArgument(specialization, extensionMethod.TemplateParameters[0]))
                            {
                                if (!IsExtensionMethodHidden(extensionMethod, members))
                                {
                                    AddExtensionMethod(writer, type, extensionMethod, specialization);
                                }
                            }
                        }
                    }
                }
            }

            TypeNode comparisonType = type;

            while (comparisonType != null)
            {
                List <Method> extensionMethods = null;

                if (index.TryGetValue(comparisonType, out extensionMethods))
                {
                    foreach (Method extensionMethod in extensionMethods)
                    {
                        if (!IsExtensionMethodHidden(extensionMethod, members))
                        {
                            AddExtensionMethod(writer, type, extensionMethod, null);
                        }
                    }
                }

                if (comparisonType.IsGeneric && comparisonType.TemplateArguments != null &&
                    comparisonType.TemplateArguments.Count > 0)
                {
                    TypeNode templateType   = comparisonType.GetTemplateType();
                    TypeNode specialization = comparisonType.TemplateArguments[0];

                    if (index.TryGetValue(templateType, out extensionMethods))
                    {
                        foreach (Method extensionMethod in extensionMethods)
                        {
                            if (IsValidTemplateArgument(specialization, extensionMethod.TemplateParameters[0]))
                            {
                                if (!IsExtensionMethodHidden(extensionMethod, members))
                                {
                                    AddExtensionMethod(writer, type, extensionMethod, specialization);
                                }
                            }
                        }
                    }
                }

                comparisonType = comparisonType.BaseType;
            }
        }
示例#5
0
        /// <summary>
        /// See if the given sets of parameters match each other
        /// </summary>
        /// <param name="parameters1">The first set of parameters</param>
        /// <param name="parameters2">The second set of parameters</param>
        /// <param name="noMatchOnGenericVersions">True to fail the match if either parameter is
        /// generic, false to allow matching to generic parameters even if the other isn't.</param>
        /// <returns>True if they match, false if not</returns>
        /// <remarks>When <paramref name="noMatchOnGenericVersions"/> is true, it prevents matching a
        /// non-generic overload of the method to a generic version of the method.  This allows the
        /// non-generic version to be matched correctly (i.e. Contains(T) and Contains(Guid)).  If not
        /// done, the generic version is matched to both methods and the reflection info contains a
        /// duplicate generic method and loses the non-generic overload.</remarks>
        private static bool ParametersMatch(ParameterList parameters1, ParameterList parameters2,
                                            bool noMatchOnGenericVersions)
        {
            if (parameters1.Count != parameters2.Count)
            {
                return(false);
            }

            for (int i = 0; i < parameters1.Count; i++)
            {
                TypeNode type1 = parameters1[i].Type;
                TypeNode type2 = parameters2[i].Type;

                // !EFW - Fail the match if we are looking for a non-generic match
                if (noMatchOnGenericVersions && (type1.IsTemplateParameter || type2.IsTemplateParameter))
                {
                    return(false);
                }

                // We can't determine the equivalence of template parameters; this is probably not good
                if (type1.IsTemplateParameter || type2.IsTemplateParameter)
                {
                    // !EFW - As a fallback, compare the type parameter positions.  If they don't match, this
                    // probably isn't the one we want.
                    int p1 = GetTemplateParameterPosition(parameters1[i].DeclaringMethod.DeclaringType, type1.Name.Name),
                        p2 = GetTemplateParameterPosition(parameters2[i].DeclaringMethod.DeclaringType, type2.Name.Name);

                    if (p1 != -1 && p2 != -1 && p1 != p2)
                    {
                        // !EFW - Another test case supplied by Jared Moore.  If the types are something like
                        // MyBaseClass<T, T> and MyBaseClass<T, U> we can still provide a match by comparing
                        // all possible positions.  As long as they intersect, it's probably a good match.
                        var positions1 = GetTemplateParameterPositions(parameters1[i].DeclaringMethod.DeclaringType,
                                                                       type1.Name.Name);
                        var positions2 = GetTemplateParameterPositions(parameters2[i].DeclaringMethod.DeclaringType,
                                                                       type2.Name.Name);

                        // If we found any but none of them are the same, then no match.
                        if (positions1.Any() && positions2.Any() && !positions1.Intersect(positions2).Any())
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // The node type must be the same; this is probably a fast check
                    if (type1.NodeType != type2.NodeType)
                    {
                        return(false);
                    }

                    // If they are "normal" types, we will compare them.  Comparing arrays, pointers, etc. is
                    // dangerous, because the types they contain may be template parameters
                    if (type1.NodeType == NodeType.Class || type1.NodeType == NodeType.Struct ||
                        type1.NodeType == NodeType.Interface || type1.NodeType == NodeType.EnumNode ||
                        type1.NodeType == NodeType.DelegateNode)
                    {
                        type1 = type1.GetTemplateType();
                        type2 = type2.GetTemplateType();

                        if (!type2.IsStructurallyEquivalentTo(type1))
                        {
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }