Skip to content

Commit

Permalink
[registar] Search the entire interface hierarchy for protocols. Fixes #…
Browse files Browse the repository at this point in the history
…6493. (#6525)

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

Fixes #6493.
  • Loading branch information
monojenkins authored and rolfbjarne committed Jul 9, 2019
1 parent 775cedf commit 14efd53
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 4 deletions.
39 changes: 39 additions & 0 deletions src/ObjCRuntime/Registrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ internal class ObjCType {
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;
Expand All @@ -159,6 +160,44 @@ internal class ObjCType {

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)
Expand Down
138 changes: 138 additions & 0 deletions tests/monotouch-test/Foundation/UrlProtocolTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

#if XAMCORE_2_0
using Foundation;
#if MONOMAC
Expand Down Expand Up @@ -66,5 +69,140 @@ public void Task ()
}
}
#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__
}
}
10 changes: 6 additions & 4 deletions tools/common/StaticRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4129,10 +4129,11 @@ TypeDefinition GetDelegateProxyType (ObjCMethod obj_method)
}

// 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;
Expand Down Expand Up @@ -4175,10 +4176,11 @@ MethodDefinition GetBlockWrapperCreator (ObjCMethod obj_method, int parameter)
}

// 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.
Expand Down

4 comments on commit 14efd53

@xamarin-release-manager
Copy link
Collaborator

@xamarin-release-manager xamarin-release-manager commented on 14efd53 Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build was (probably) aborted

🔥 Jenkins job (on internal Jenkins) failed in stage(s) 'Test run, Test run' 🔥

Build succeeded
✅ Packages: xamarin.ios-12.16.0.8.pkg xamarin.mac-5.16.0.8.pkg
API Diff (from stable)
🔥 Failed to compare API and create generator diff 🔥
    ** Error: Working directory isn't clean:
    HEAD detached at 14efd53
    Untracked files:
    (use "git add ..." to include in what will be committed)
    
    tests/bcl-test/BCL tests group 1.csproj
    tests/bcl-test/BCL tests group 2.csproj
    tests/bcl-test/BCL tests group 3.csproj
    tests/bcl-test/BCL tests group 4.csproj
    tests/bcl-test/BCL tests group 6.csproj
    tests/bcl-test/Mac OS X BCL tests group 1-mac-full.csproj
    tests/bcl-test/Mac OS X BCL tests group 1-mac-modern.csproj
    tests/bcl-test/Mac OS X BCL tests group 2-mac-full.csproj
    tests/bcl-test/Mac OS X BCL tests group 2-mac-modern.csproj
    tests/bcl-test/Mac OS X BCL tests group 3-mac-full.csproj
    tests/bcl-test/Mac OS X BCL tests group 3-mac-modern.csproj
    tests/bcl-test/Mac OS X BCL tests group 4-mac-full.csproj
    tests/bcl-test/Mac OS X BCL tests group 4-mac-modern.csproj
    tests/bcl-test/generated/
    tests/bcl-test/mscorlib-mac-full.csproj
    tests/bcl-test/mscorlib-mac-modern.csproj
    tests/bcl-test/mscorlib.csproj
    
    nothing added to commit but untracked files present (use "git add" to track)
    Search for Comparing API & creating generator diff in the log to view the complete log.
🔥 Test run failed 🔥

Test results

1 tests failed, 170 tests passed.

Failed tests

  • introspection/watchOS 32-bits - simulator/Debug (watchOS 2.0): Crashed

Failure analysis

Final verdict: 🤥

@xamarin-release-manager
Copy link
Collaborator

@xamarin-release-manager xamarin-release-manager commented on 14efd53 Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 Device tests completed (Failed) on iOS on Azure DevOps(iOS): Html Report 🔥

Test results

3 tests failed, 110 tests passed.

Failed tests

  • [NUnit] Mono BCL tests group 1/iOS Unified 64-bits - device/Debug: TimedOut (HE0041: Error while processing device notifications from fff50:32:37:a1:c8:31fff (0x7fbab95b74e0): Failed to communicate with the device. Please ensure the cable is properly connected, and try rebooting the device (error: 0xe8000065 kAMDMuxConnectError))
  • [NUnit] Mono BCL tests group 1/iOS Unified 64-bits - device/AssemblyBuildTarget: SDK framework (debug): TimedOut (HE0041: Error while processing device notifications from fff50:32:37:a1:c8:31fff (0x7f9c6d35b190): Failed to communicate with the device. Please ensure the cable is properly connected, and try rebooting the device (error: 0xe8000065 kAMDMuxConnectError))
  • [xUnit] Mono BCL tests group 3/iOS Unified 64-bits - device/AssemblyBuildTarget: SDK framework (debug, profiling): TimedOut

Failure analysis

Final verdict: 🤥

@xamarin-release-manager
Copy link
Collaborator

@xamarin-release-manager xamarin-release-manager commented on 14efd53 Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 Device tests completed (Failed) on TvOS on Azure DevOps(TvOS): Html Report 🔥

Test results

14 tests failed, 99 tests passed.

Failed tests

  • monotouch-test/tvOS - device/Debug: TimedOut
  • monotouch-test/tvOS - device/AssemblyBuildTarget: dylib (debug): TimedOut
  • monotouch-test/tvOS - device/AssemblyBuildTarget: SDK framework (debug): TimedOut
  • monotouch-test/tvOS - device/AssemblyBuildTarget: dylib (debug, profiling): TimedOut
  • monotouch-test/tvOS - device/AssemblyBuildTarget: SDK framework (debug, profiling): TimedOut
  • monotouch-test/tvOS - device/Release: TimedOut
  • monotouch-test/tvOS - device/AssemblyBuildTarget: SDK framework (release): TimedOut
  • monotouch-test/tvOS - device/Debug (dynamic registrar): TimedOut
  • monotouch-test/tvOS - device/Release (all optimizations): TimedOut
  • monotouch-test/tvOS - device/Debug (all optimizations): TimedOut
  • monotouch-test/tvOS - device/Debug: SGenConc: TimedOut
  • monotouch-test/tvOS - device/Debug (interpreter): TimedOut
  • monotouch-test/tvOS - device/Debug (interpreter -mscorlib): TimedOut
  • monotouch-test/tvOS - device/Release (interpreter -mscorlib): TimedOut

Failure analysis

Final verdict: 🤥

@rolfbjarne
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting state to success for all statuses.

Only 🤥 (gred) issues found.

Please sign in to comment.