[registar] Search the entire interface hierarchy for protocols. Fixes #6493. (#6524)

When we're searching for metadata for marshalling blocks, we must search the
entire interface hierarchy for protocols that implement optional members.

Fixes https://github.com/xamarin/xamarin-macios/issues/6493.
This commit is contained in:
monojenkins 2019-07-09 09:50:55 -04:00 коммит произвёл Rolf Bjarne Kvinge
Родитель f80e6b4032
Коммит a7f4c7011e
3 изменённых файлов: 183 добавлений и 4 удалений

Просмотреть файл

@ -135,6 +135,7 @@ namespace Registrar {
public CategoryAttribute CategoryAttribute;
public TType Type;
public ObjCType BaseType;
// This only contains the leaf protocols implemented by this type.
public ObjCType [] Protocols;
public string [] AdoptedProtocols;
public bool IsModel;
@ -159,6 +160,44 @@ namespace Registrar {
public bool IsCategory { get { return CategoryAttribute != null; } }
#if MTOUCH || MMP
HashSet<ObjCType> all_protocols;
// This contains all protocols in the type hierarchy.
// Given a type T that implements a protocol with super protocols:
// class T : NSObject, IProtocol2 {}
// [Protocol]
// interface IP1 {}
// [Protocol]
// interface IP2 : IP1 {}
// This property will contain both IP1 and IP2. The Protocols property only contains IP2.
public IEnumerable<ObjCType> AllProtocols {
get {
if (Protocols == null || Protocols.Length == 0)
return null;
if (all_protocols == null) {
var queue = new Queue<ObjCType> (Protocols);
var rv = new HashSet<ObjCType> ();
while (queue.Count > 0) {
var type = queue.Dequeue ();
if (rv.Add (type)) {
foreach (var iface in type.Type.Resolve ().Interfaces) {
if (!Registrar.Types.TryGetValue (iface.InterfaceType, out var superIface)) {
// This is not an interface that corresponds to a protocol.
continue;
}
queue.Enqueue (superIface);
}
}
}
all_protocols = rv;
}
return all_protocols;
}
}
#endif
public void VerifyRegisterAttribute (ref List<Exception> exceptions)
{
if (RegisterAttribute == null)

Просмотреть файл

@ -9,6 +9,9 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
#if XAMCORE_2_0
using Foundation;
#if MONOMAC
@ -66,5 +69,140 @@ namespace MonoTouchFixtures.Foundation {
}
}
#endif
#if !__WATCHOS__
[Test]
public void RegistrarTest ()
{
Exception ex = null;
var done = new ManualResetEvent (false);
var success = false;
Task.Run (async () => {
try {
var config = NSUrlSessionConfiguration.DefaultSessionConfiguration;
config.WeakProtocolClasses = NSArray.FromNSObjects (new Class (typeof (CustomUrlProtocol)));
var session = NSUrlSession.FromConfiguration (config);
var custom_url = new NSUrl ("foo://server");
using (var task = await session.CreateDownloadTaskAsync (custom_url)) {
success = true;
}
} catch (Exception e) {
ex = e;
} finally {
done.Set ();
}
});
Assert.IsTrue (TestRuntime.RunAsync (DateTime.Now.AddSeconds (10), () => { }, () => done.WaitOne (0)), "Timed out");
Assert.IsNull (ex, "Exception");
Assert.IsTrue (custom_url_protocol_instance.Called_DidCompleteWithError, "DidCompleteWithError");
// if DidReceiveChallenge is called or not seems to vary between test runs, so we can't assert it.
//Assert.IsFalse (custom_url_protocol_instance.Called_DidReceiveChallenge, "DidReceiveChallenge");
Assert.IsTrue (custom_url_protocol_instance.Called_DidReceiveData, "DidReceiveData");
Assert.IsTrue (custom_url_protocol_instance.Called_DidReceiveResponse, "DidReceiveResponse");
Assert.IsTrue (custom_url_protocol_instance.Called_StartLoading, "StartLoading");
Assert.IsTrue (custom_url_protocol_instance.Called_StopLoading, "StopLoading");
Assert.IsTrue (custom_url_protocol_instance.Called_WillPerformHttpRedirection, "WillPerformHttpRedirection");
Assert.IsTrue (CustomUrlProtocol.Called_CanInitWithRequest, "CanInitWithRequest");
Assert.IsTrue (CustomUrlProtocol.Called_GetCanonicalRequest, "GetCanonicalRequest");
Assert.IsTrue (success, "Success");
}
static CustomUrlProtocol custom_url_protocol_instance;
public class CustomUrlProtocol : NSUrlProtocol, INSUrlSessionDelegate, INSUrlSessionTaskDelegate, INSUrlSessionDataDelegate {
[Export ("canInitWithRequest:")]
public static new bool CanInitWithRequest (NSUrlRequest request)
{
Called_CanInitWithRequest = true;
return true;
}
public static bool Called_CanInitWithRequest;
[Export ("canonicalRequestForRequest:")]
public static new NSUrlRequest GetCanonicalRequest (NSUrlRequest request)
{
Called_GetCanonicalRequest = true;
return request;
}
public static bool Called_GetCanonicalRequest;
[Export ("initWithRequest:cachedResponse:client:")]
public CustomUrlProtocol (NSUrlRequest request, NSCachedUrlResponse cachedResponse, INSUrlProtocolClient client)
: base (request, cachedResponse, client)
{
custom_url_protocol_instance = this;
}
[Export ("startLoading")]
public override void StartLoading ()
{
Called_StartLoading = true;
var config = NSUrlSession.SharedSession.Configuration;
var session = NSUrlSession.FromConfiguration (config, this, new NSOperationQueue ());
var task = session.CreateDataTask (new NSUrlRequest (new NSUrl ("https://microsoft.com")));
task.Resume ();
}
public bool Called_StartLoading;
[Export ("stopLoading")]
public override void StopLoading ()
{
Called_StopLoading = true;
}
public bool Called_StopLoading;
//NSURLSessionTaskDelegate
[Export ("URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:")]
public virtual void WillPerformHttpRedirection (NSUrlSession session, NSUrlSessionTask task, NSHttpUrlResponse response, NSUrlRequest newRequest, Action<NSUrlRequest> completionHandler)
{
Called_WillPerformHttpRedirection = true;
completionHandler (newRequest);
}
public bool Called_WillPerformHttpRedirection;
[Export ("URLSession:task:didReceiveChallenge:completionHandler:")]
public virtual void DidReceiveChallenge (NSUrlSession session, NSUrlSessionTask task, NSUrlAuthenticationChallenge challenge, Action<NSUrlSessionAuthChallengeDisposition, NSUrlCredential> completionHandler)
{
Called_DidReceiveChallenge = true;
completionHandler (NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, null);
}
public bool Called_DidReceiveChallenge;
//NSURLSessionDataDelegate
[Export ("URLSession:dataTask:didReceiveResponse:completionHandler:")]
public virtual void DidReceiveResponse (NSUrlSession session, NSUrlSessionDataTask dataTask, NSUrlResponse response, Action<NSUrlSessionResponseDisposition> completionHandler)
{
Called_DidReceiveResponse = true;
completionHandler (NSUrlSessionResponseDisposition.Allow);
this.Client.ReceivedResponse (this, response, NSUrlCacheStoragePolicy.Allowed);
}
public bool Called_DidReceiveResponse;
[Export ("URLSession:dataTask:didReceiveData:")]
public virtual void DidReceiveData (NSUrlSession session, NSUrlSessionDataTask dataTask, NSData data)
{
Called_DidReceiveData = true;
this.Client.DataLoaded (this, data);
}
public bool Called_DidReceiveData;
[Export ("URLSession:task:didCompleteWithError:")]
public virtual void DidCompleteWithError (NSUrlSession session, NSUrlSessionTask task, NSError error)
{
Called_DidCompleteWithError = true;
if (error != null) {
this.Client.FailedWithError (this, error);
} else {
this.Client.FinishedLoading (this);
}
}
public bool Called_DidCompleteWithError;
}
#endif // !__WATCHOS__
}
}

Просмотреть файл

@ -4137,10 +4137,11 @@ namespace Registrar {
}
// Might be an implementation of an optional protocol member.
if (obj_method.DeclaringType.Protocols != null) {
var allProtocols = obj_method.DeclaringType.AllProtocols;
if (allProtocols != null) {
string selector = null;
foreach (var proto in obj_method.DeclaringType.Protocols) {
foreach (var proto in allProtocols) {
// We store the DelegateProxy type in the ProtocolMemberAttribute, so check those.
if (selector == null)
selector = obj_method.Selector ?? string.Empty;
@ -4183,10 +4184,11 @@ namespace Registrar {
}
// Might be an implementation of an optional protocol member.
if (obj_method.DeclaringType.Protocols != null) {
var allProtocols = obj_method.DeclaringType.AllProtocols;
if (allProtocols != null) {
string selector = null;
foreach (var proto in obj_method.DeclaringType.Protocols) {
foreach (var proto in allProtocols) {
// We store the BlockProxy type in the ProtocolMemberAttribute, so check those.
// We may run into binding assemblies built with earlier versions of the generator,
// which means we can't rely on finding the BlockProxy attribute in the ProtocolMemberAttribute.