Skip to content

Commit 5136ef9

Browse files
authored
[Xamarin.Android.Tools.Bytecode] Hide Kotlin nested types inside (#723)
Fixes: #682 Context: dotnet/android#4955 We added support for reading Kotlin-provided metadata in commit 439bd83. Part of this was recognizing when a type was "Kotlin-internal" and marking it as `private` or "package-private" to prevent `generator` from attempting to bind it. However, we didn't consider *nested* types of `private` or "package-private" types in 439bd83, leaving the nested type visibility unchanged. This could result in a warning when binding such nested types: warning BG8604: top ancestor ParentType not found for nested type ParentType.ChildEnum Note: this doesn't prevent building -- unless `csc /warnaserror` is used -- and the type is not bound, so we are doing the "correct" thing. This warning *does* create a "less-than-desirable" user experience, possibly confusing users. Update `generator` to change the visibility of nested types within non-`public` types so that `generator` doesn't attempt to bind the nested types. This avoids the BG8604 warning.
1 parent 53d6051 commit 5136ef9

File tree

6 files changed

+40
-7
lines changed

6 files changed

+40
-7
lines changed

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

+25-5
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,41 @@ static void FixupClassVisibility (ClassFile klass, KotlinClass metadata)
6767
// Hide class if it isn't Public/Protected
6868
if (klass.AccessFlags.IsPubliclyVisible () && !metadata.Visibility.IsPubliclyVisible ()) {
6969

70-
// Interfaces should be set to "package-private", which is "no visibility flags"
70+
// Interfaces should be set to "package-private"
7171
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;
72+
Log.Debug ($"Kotlin: Setting internal interface {klass.ThisClass.Name.Value} to package-private");
73+
klass.AccessFlags = SetPackagePrivate (klass.AccessFlags);
74+
75+
foreach (var ic in klass.InnerClasses) {
76+
Log.Debug ($"Kotlin: Setting nested type {ic.InnerClass.Name.Value} in an internal interface to package-private");
77+
ic.InnerClassAccessFlags = SetPackagePrivate (ic.InnerClassAccessFlags);
78+
}
79+
7680
return;
7781
}
7882

7983
Log.Debug ($"Kotlin: Hiding internal class {klass.ThisClass.Name.Value}");
8084
klass.AccessFlags = ClassAccessFlags.Private;
85+
86+
foreach (var ic in klass.InnerClasses) {
87+
Log.Debug ($"Kotlin: Hiding nested internal type {ic.InnerClass.Name.Value}");
88+
ic.InnerClassAccessFlags = ClassAccessFlags.Private;
89+
}
90+
8191
return;
8292
}
8393
}
8494

95+
static ClassAccessFlags SetPackagePrivate (ClassAccessFlags flags)
96+
{
97+
// Package-private is stored as "no visibility flags"
98+
flags = (flags ^ ClassAccessFlags.Public) & flags;
99+
flags = (flags ^ ClassAccessFlags.Protected) & flags;
100+
flags = (flags ^ ClassAccessFlags.Private) & flags;
101+
102+
return flags;
103+
}
104+
85105
static void FixupJavaMethods (Methods methods)
86106
{
87107
// We do the following method level fixups here because we can operate on all methods,

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

+9
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,36 @@ public class KotlinFixupsTests : ClassFileFixture
1515
public void HideInternalClass ()
1616
{
1717
var klass = LoadClassFile ("InternalClass.class");
18+
var inner_class = klass.InnerClasses.First ();
1819

1920
Assert.True (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
21+
Assert.True (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Public));
2022

2123
KotlinFixups.Fixup (new [] { klass });
2224

2325
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
26+
Assert.False (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Public));
2427
}
2528

2629
[Test]
2730
public void MakeInternalInterfacePackagePrivate ()
2831
{
2932
var klass = LoadClassFile ("InternalInterface.class");
33+
var inner_class = klass.InnerClasses.First ();
3034

3135
Assert.True (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
36+
Assert.True (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Public));
3237

3338
KotlinFixups.Fixup (new [] { klass });
3439

3540
// "package-private" is denoted as no visibility flags
3641
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Public));
3742
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Protected));
3843
Assert.False (klass.AccessFlags.HasFlag (ClassAccessFlags.Private));
44+
45+
Assert.False (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Public));
46+
Assert.False (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Protected));
47+
Assert.False (inner_class.InnerClassAccessFlags.HasFlag (ClassAccessFlags.Private));
3948
}
4049

4150
[Test]
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
internal class InternalClass
1+
internal class InternalClass {
2+
enum class ChildEnum { VALUE1, VALUE2 }
3+
}
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
internal interface InternalInterface
1+
internal interface InternalInterface {
2+
enum class ChildEnum { VALUE1, VALUE2 }
3+
}

0 commit comments

Comments
 (0)