Skip to content

Commit

Permalink
[generator] Fix an issue with regards to attributes from inlined prot…
Browse files Browse the repository at this point in the history
…ocols. Fixes #17268. (#17269)

In the following scenario:

* Type T is not available on a platform (say tvOS).
* Protocol P is available on said platform.
* A member M of P has its own availability attribute for said platform (for
  instance if P is available on tvOS 11.0, and the member is available on tvOS
  12.0).
* The protocol P is inlined into the type T.

We'd include the SupportedOSPlatform attribute from the inlined member on
generated code on other platforms (so the iOS assembly would say that the
inlined member M in T is available on tvOS).

Fixes #17268.
  • Loading branch information
rolfbjarne authored Jan 18, 2023
1 parent 49c3fd4 commit b1957c9
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3699,8 +3699,12 @@ AvailabilityBaseAttribute [] GetPlatformAttributesToPrint (MemberInfo mi, Type t
List<AvailabilityBaseAttribute> availabilityToConsider = new List<AvailabilityBaseAttribute> ();
if (inlinedTypeAvailability != null) {
availabilityToConsider.AddRange (inlinedTypeAvailability);
// Don't copy parent attributes if the conflict with the type we're inlining members into
// Example: don't copy Introduced on top of Unavailable.
CopyValidAttributes (availabilityToConsider, parentContextAvailability);
} else {
availabilityToConsider.AddRange (parentContextAvailability);
}
availabilityToConsider.AddRange (parentContextAvailability);

// We do not support Watch, so strip from both our input sources before any processing
memberAvailability = memberAvailability.Where (x => x.Platform != PlatformName.WatchOS).ToList ();
Expand All @@ -3712,6 +3716,23 @@ AvailabilityBaseAttribute [] GetPlatformAttributesToPrint (MemberInfo mi, Type t
// Copy down any unavailable from the parent before expanding, since a [NoMacCatalyst] on the type trumps [iOS] on a member
CopyValidAttributes (memberAvailability, availabilityToConsider.Where (attr => attr.AvailabilityKind != AvailabilityKind.Introduced));

if (inlinedType is not null && inlinedType != mi.DeclaringType && memberAvailability.Count > 1) {
// We might have gotten conflicting availability attributes for inlined members, where the inlined member
// might be available on a platform the target type isn't. The target type's unavailability will come
// later in the list, which means that if we have an unavailable attribute after an introduced attribute,
// then we need to remove the introduced attribute.
for (var i = memberAvailability.Count - 1; i >= 0; i--) {
if (memberAvailability [i].AvailabilityKind != AvailabilityKind.Unavailable)
continue;
for (var k = i - 1; k >= 0; k--) {
if (memberAvailability [k].AvailabilityKind == AvailabilityKind.Introduced && memberAvailability [k].Platform == memberAvailability [i].Platform) {
memberAvailability.RemoveAt (k);
i--;
}
}
}
}

// Add implied catalyst\TVOS from [iOS] _before_ copying down from parent if no catalyst\TVOS attributes
// As those take precedent. We will do this a second time later in a moment..
AddImpliedPlatforms (memberAvailability);
Expand Down
7 changes: 7 additions & 0 deletions tests/common/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,13 @@ public static string GetBaseLibrary (Profile profile)
}
}

public static string GetBaseLibrary (ApplePlatform platform, bool isDotNet)
{
if (isDotNet)
return Path.Combine (GetRefDirectory (platform), GetBaseLibraryName (platform, isDotNet));
return GetBaseLibrary (platform.AsProfile ());
}

static string GetBaseLibraryName (TargetFramework targetFramework)
{
return GetBaseLibraryName (targetFramework.Platform, targetFramework.IsDotNet);
Expand Down
21 changes: 21 additions & 0 deletions tests/common/Profile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum Profile {
macOSMobile,
macOSFull,
macOSSystem,
MacCatalyst,
}

public static class ProfileExtensions {
Expand All @@ -33,5 +34,25 @@ public static ApplePlatform AsPlatform (this Profile profile)
throw new NotImplementedException (profile.ToString ());
}
}

public static Profile AsProfile (this ApplePlatform platform)
{
switch (platform) {
case ApplePlatform.iOS:
return Profile.iOS;
case ApplePlatform.MacCatalyst:
return Profile.MacCatalyst;
case ApplePlatform.MacOSX:
return Profile.macOSMobile;
case ApplePlatform.None:
return Profile.None;
case ApplePlatform.TVOS:
return Profile.tvOS;
case ApplePlatform.WatchOS:
return Profile.watchOS;
default:
throw new NotImplementedException (platform.ToString ());
}
}
}
}
57 changes: 57 additions & 0 deletions tests/generator/BGenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,63 @@ public void CSharp10Syntax ()
BuildFile (Profile.iOS, "tests/csharp10syntax.cs");
}

[Test]
[TestCase (Profile.iOS)]
#if !NET
[Ignore ("This only applies to .NET")]
#endif
public void AttributesFromInlinedProtocols (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());

var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("tests/attributes-from-inlined-protocols.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");

var type = bgen.ApiAssembly.MainModule.GetType ("NS", "TypeA");
var someMethod1 = type.Methods.Single (v => v.Name == "SomeMethod1");
var someMethod2 = type.Methods.Single (v => v.Name == "SomeMethod2");
var someMethod3 = type.Methods.Single (v => v.Name == "SomeMethod3");
var someMethod4 = type.Methods.Single (v => v.Name == "SomeMethod4");

var renderedSomeMethod1 = string.Join ("\n", someMethod1.CustomAttributes.Select (ca => RenderSupportedOSPlatformAttribute (ca)).OrderBy (v => v));
var renderedSomeMethod2 = string.Join ("\n", someMethod2.CustomAttributes.Select (ca => RenderSupportedOSPlatformAttribute (ca)).OrderBy (v => v));
var renderedSomeMethod3 = string.Join ("\n", someMethod3.CustomAttributes.Select (ca => RenderSupportedOSPlatformAttribute (ca)).OrderBy (v => v));
var renderedSomeMethod4 = string.Join ("\n", someMethod4.CustomAttributes.Select (ca => RenderSupportedOSPlatformAttribute (ca)).OrderBy (v => v));

const string expectedAttributes1 =
@"[BindingImpl(3)]
[Export(""someMethod1:"")]
[SupportedOSPlatform(""ios12.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]";
const string expectedAttributes2 =
@"[BindingImpl(3)]
[Export(""someMethod2:"")]
[SupportedOSPlatform(""ios12.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]";
const string expectedAttributes3 =
@"[BindingImpl(3)]
[Export(""someMethod3:"")]
[SupportedOSPlatform(""ios11.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]";
const string expectedAttributes4 =
@"[BindingImpl(3)]
[Export(""someMethod4:"")]
[SupportedOSPlatform(""ios11.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]";

Assert.AreEqual (expectedAttributes1, renderedSomeMethod1, "SomeMethod1");
Assert.AreEqual (expectedAttributes2, renderedSomeMethod2, "SomeMethod2");
Assert.AreEqual (expectedAttributes3, renderedSomeMethod3, "SomeMethod3");
Assert.AreEqual (expectedAttributes4, renderedSomeMethod4, "SomeMethod4");
}

[Test]
public void NFloatType ()
{
Expand Down
4 changes: 4 additions & 0 deletions tests/generator/BGenTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,11 @@ AssemblyDefinition LoadAssembly ()
if (assembly is null) {
var parameters = new ReaderParameters ();
var resolver = new DefaultAssemblyResolver ();
#if NET
var searchdir = Path.GetDirectoryName (Configuration.GetBaseLibrary (Profile.AsPlatform (), true));
#else
var searchdir = Path.GetDirectoryName (Configuration.GetBaseLibrary (Profile));
#endif
resolver.AddSearchDirectory (searchdir);
parameters.AssemblyResolver = resolver;
assembly = AssemblyDefinition.ReadAssembly (Out ?? (Path.Combine (TmpDirectory, Path.GetFileNameWithoutExtension (ApiDefinitions [0]).Replace ('-', '_') + ".dll")), parameters);
Expand Down
32 changes: 32 additions & 0 deletions tests/generator/tests/attributes-from-inlined-protocols.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

using Foundation;
using ObjCRuntime;

namespace NS {
[NoTV]
[BaseType (typeof (NSObject))]
interface TypeA : ProtocolA {
}

[Introduced (PlatformName.iOS, 11, 0)]
[Introduced (PlatformName.TvOS, 11, 0)]
[Protocol]
interface ProtocolA {
[Introduced (PlatformName.iOS, 12, 0)]
[Introduced (PlatformName.TvOS, 12, 0)]
[Export ("someMethod1:")]
void SomeMethod1 (nint row);

[Introduced (PlatformName.iOS, 12, 0)]
[Export ("someMethod2:")]
void SomeMethod2 (nint row);

[Introduced (PlatformName.TvOS, 12, 0)]
[Export ("someMethod3:")]
void SomeMethod3 (nint row);

[Export ("someMethod4:")]
void SomeMethod4 (nint row);
}
}

4 comments on commit b1957c9

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.