Merge pull request #757 from mlintner/master
authorMiguel de Icaza <miguel@gnome.org>
Wed, 23 Sep 2015 20:05:09 +0000 (16:05 -0400)
committerMiguel de Icaza <miguel@gnome.org>
Wed, 23 Sep 2015 20:05:09 +0000 (16:05 -0400)
These changes fix 14335. When in getcontractinternal various things can ...

This patch further does not affect mobile.

1  2 
mcs/class/System.ServiceModel/System.ServiceModel.Description/ContractDescriptionGenerator.cs

index a7665b8cab6daa0a6a334a3cf3561d08c3bfdbd5,afd5ac2ac84c9ba565a0db9348b176f9c306b23c..80238d41ff89bf3ee89270260cc6cbfb82a4f12d
@@@ -139,6 -139,45 +139,45 @@@ namespace System.ServiceModel.Descripti
                        return ret;
                }
  
+               internal static Type GetContractAssignableToInterfaces(Type given, Type [] givenInterfaces)
+               {
+                       if ((given != null) && (givenInterfaces.Count() < 1))
+                               return null;
+                       Dictionary<Type,int> interfaceGraph = new Dictionary<Type,int> ();
+                       foreach (var smaller in givenInterfaces) {
+                               interfaceGraph [smaller] = 0;
+                               foreach (var bigger in givenInterfaces) {
+                                       if (smaller.IsAssignableFrom (bigger)) {
+                                               interfaceGraph [smaller]++;
+                                       }
+                               }
+                       }
+                       List<Type> possibleInterfaces = new List<Type> ();
+                       foreach (var node in interfaceGraph) {
+                               if (node.Value == 1) {
+                                       possibleInterfaces.Add(node.Key);
+                               }
+                       }
+                       // For this purpose a contract is a set of interfaces. it is necessary to find the interface representative of rest of the set. It will be assignable to all of 
+                       // the others but can only be assigned to by itself. This will give it  count of ! in its slot in the interfaceGraph. To be a valid set there can be only one with a count of one
+                       // in its slot.  More results in InvalidOperation exceptioni with ambigours error message, less means that the interface we want is the one passed in by the parameter given
+                       // and by returning null we give the callign method permission to use it..
+                       switch (possibleInterfaces.Count()) 
+                       {
+                               case 0:
+                               break;
+                               case 1:
+                               return possibleInterfaces [0];
+                               break;
+                               default:
+                               if (!given.IsInterface) 
+                                       throw new InvalidOperationException ("The contract type of " + given + " is ambiguous: can be either " + possibleInterfaces[0] + " or " + possibleInterfaces[1]);
+                               break;
+                       }
+                       return null;
+               }
                internal static ContractDescription GetContractInternal (Type givenContractType, Type givenServiceType, Type serviceTypeForCallback)
                {
                        if (givenContractType == null)
                                exactContractType = givenContractType;
                                sca = contracts [givenContractType];
                        } else {
-                               foreach (Type t in contracts.Keys)
-                                       if (t.IsAssignableFrom(givenContractType)) {
-                                               if (t.IsAssignableFrom (exactContractType)) // exact = IDerived, t = IBase
-                                                       continue;
-                                               if (sca != null && (exactContractType == null || !exactContractType.IsAssignableFrom (t))) // t = IDerived, exact = IBase
-                                                       throw new InvalidOperationException ("The contract type of " + givenContractType + " is ambiguous: can be either " + exactContractType + " or " + t);
-                                               exactContractType = t;
-                                               sca = contracts [t];
-                                       }
+                               Type[] contractTypes = contracts.Keys.ToArray() as Type [];
+                               exactContractType = GetContractAssignableToInterfaces(givenContractType, contractTypes);
+                               if (exactContractType != null)
+                                       sca = contracts[exactContractType];
                        }
                        if (exactContractType == null)
                                exactContractType = givenContractType;
                        if (sca == null) {
                                else
                                        return null; // no contract
                        }
                        string name = sca.Name ?? exactContractType.Name;
                        string ns = sca.Namespace ?? "http://tempuri.org/";
  
                         */
  
                        var inherited = new Collection<ContractDescription> ();
-                       foreach (var it in cd.ContractType.GetInterfaces ()) {
+               var interfaces = cd.ContractType.GetInterfaces ();
+                       foreach (var it in interfaces ) {
                                var icd = GetContractInternal (it, givenServiceType, null);
                                if (icd != null)
                                        inherited.Add (icd);
                        }
  
                        foreach (var icd in inherited) {
-                               foreach (var od in icd.Operations)
-                                       if (!cd.Operations.Any(o => o.Name == od.Name && o.SyncMethod == od.SyncMethod && 
-                                                              o.BeginMethod == od.BeginMethod && o.InCallbackContract == od.InCallbackContract))
+                               foreach (var od in icd.Operations) {
+                                       if (!cd.Operations.Any (o => o.Name == od.Name && o.SyncMethod == od.SyncMethod && o.BeginMethod == od.BeginMethod && o.InCallbackContract == od.InCallbackContract))
                                                cd.Operations.Add (od);
+                               }
                        }
  
                        FillOperationsForInterface (cd, cd.ContractType, givenServiceType, false);
                        
                        for (int i = 0; i < contractMethods.Length; ++i)
                        {
 -
                                MethodInfo mi = contractMethods [i];
                                OperationContractAttribute oca = GetOperationContractAttribute (mi);
                                if (oca == null)
                                        if (GetOperationContractAttribute (end) != null)
                                                throw new InvalidOperationException ("Async 'End' method must not have OperationContractAttribute. It is automatically treated as the EndMethod of the corresponding 'Begin' method.");
                                }
 -                              OperationDescription od = GetOrCreateOperation (cd, mi, serviceMethods [i], oca, end != null ? end.ReturnType : null, isCallback, givenServiceType);
 +                              OperationDescription od = GetOrCreateOperation (cd, mi, serviceMethods [i], oca, end, isCallback, givenServiceType);
                                if (end != null)
                                        od.EndMethod = end;
                        }
                static OperationDescription GetOrCreateOperation (
                        ContractDescription cd, MethodInfo mi, MethodInfo serviceMethod,
                        OperationContractAttribute oca,
 -                      Type asyncReturnType,
 +                      MethodInfo endMethod,
                        bool isCallback,
                        Type givenServiceType)
                {
  
                                od.Messages.Add (GetMessage (od, mi, oca, true, isCallback, null));
                                if (!od.IsOneWay) {
 -                                      var md = GetMessage (od, mi, oca, false, isCallback, asyncReturnType);
 +                                      var asyncReturnType = endMethod != null ? endMethod.ReturnType : null;
 +                                      var md = GetMessage (od, endMethod ?? mi, oca, false, isCallback, asyncReturnType);
                                        od.Messages.Add (md);
                                        var mpa = mi.ReturnParameter.GetCustomAttribute<MessageParameterAttribute> (true);
                                        if (mpa != null) {
                        int index = 0;
                        foreach (ParameterInfo pi in plist) {
                                // AsyncCallback and state are extraneous.
 -                              if (oca.AsyncPattern && pi.Position == plist.Length - 2)
 -                                      break;
 +                              if (oca.AsyncPattern) {
 +                                      if (isRequest && pi.Position == plist.Length - 2)
 +                                              break;
 +                                      if (!isRequest && pi.Position == plist.Length - 1)
 +                                              break;
 +                              }
  
                                // They are ignored:
                                // - out parameter in request