Skip to content

Commit b6c1539

Browse files
jpobstjonpryor
authored andcommitted
[XAT.Bytecode] Bind Kotlin internal interfaces as package-private (#645)
Context: dotnet/android#4661 In 439bd83, we made a change to hide Kotlin `internal` members as we shouldn't be binding them. However a `public class` can inherit from an `internal interface`, so we need to emit those interfaces. Instead, emit `internal interface` types as "package private" types; `visibility=""` in `api.xml` parlance. This allows us to resolve the `interface` and thus successfully bind the `class`. In C#-land, the `interface` is not actually bound and the C# class doesn't actually implement it. This has no effect to the public API end user, who shouldn't be seeing the `internal interface` anyways. If a user really wants to publicly expose the `interface`, its `visibility` can be changed via `metadata`. ***Note***: Changing the `visibility` of a type may result in subsequent Java Callable Wrapper generation errors, if/when a Java Callable Wrapper attempts to implement the package-private type.
1 parent d6024f1 commit b6c1539

File tree

4 files changed

+26
-0
lines changed

4 files changed

+26
-0
lines changed

src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ static void FixupClassVisibility (ClassFile klass, KotlinClass metadata)
6666
{
6767
// Hide class if it isn't Public/Protected
6868
if (klass.AccessFlags.IsPubliclyVisible () && !metadata.Visibility.IsPubliclyVisible ()) {
69+
70+
// Interfaces should be set to "package-private", which is "no visibility flags"
71+
if (klass.AccessFlags.HasFlag (ClassAccessFlags.Interface)) {
72+
Log.Debug ($"Kotlin: Setting interface {klass.ThisClass.Name.Value} to package-private");
73+
klass.AccessFlags = (klass.AccessFlags ^ ClassAccessFlags.Public) & klass.AccessFlags;
74+
klass.AccessFlags = (klass.AccessFlags ^ ClassAccessFlags.Protected) & klass.AccessFlags;
75+
klass.AccessFlags = (klass.AccessFlags ^ ClassAccessFlags.Private) & klass.AccessFlags;
76+
return;
77+
}
78+
6979
Log.Debug ($"Kotlin: Hiding internal class {klass.ThisClass.Name.Value}");
7080
klass.AccessFlags = ClassAccessFlags.Private;
7181
return;

tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ public void HideInternalClass ()
2323
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
2424
}
2525

26+
[Test]
27+
public void MakeInternalInterfacePackagePrivate ()
28+
{
29+
var klass = LoadClassFile ("InternalInterface.class");
30+
31+
Assert.True (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
32+
33+
KotlinFixups.Fixup (new [] { klass });
34+
35+
// "package-private" is denoted as no visibility flags
36+
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
37+
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Protected));
38+
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Private));
39+
}
40+
2641
[Test]
2742
public void HideInternalConstructor ()
2843
{
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
internal interface InternalInterface

0 commit comments

Comments
 (0)