Skip to content

Commit

Permalink
[Xamarin.Android.Tools.Bytecode] Only hide getters/setters for intern…
Browse files Browse the repository at this point in the history
…al properties.
  • Loading branch information
jpobst committed Oct 17, 2023
1 parent 3c83179 commit 4931251
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 19 deletions.
24 changes: 15 additions & 9 deletions src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinClassMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ public class KotlinProperty : KotlinMethodBase
}

public override string? ToString () => Name;

public bool IsInternalVisibility => (Flags & KotlinPropertyFlags.VisibilityMask) == KotlinPropertyFlags.Internal;

public bool IsPrivateVisibility => (Flags & KotlinPropertyFlags.VisibilityMask) == KotlinPropertyFlags.Private;
}

public class KotlinType
Expand Down Expand Up @@ -688,6 +692,8 @@ public enum KotlinPropertyFlags
PrivateToThis = 0b00_00_100_0,
Local = 0b00_00_101_0,

VisibilityMask = 0b00_00_111_0,

Final = 0b00_00_000_0,
Open = 0b00_01_000_0,
Abstract = 0b00_10_000_0,
Expand All @@ -698,15 +704,15 @@ public enum KotlinPropertyFlags
Delegation = 0b10_00_000_0,
Synthesized = 0b11_00_000_0,

IsVar = 0b_000000001_000_00_000_0,
HasGetter = 0b_000000010_000_00_000_0,
HasSetter = 0b_000000100_000_00_000_0,
IsConst = 0b_000001000_000_00_000_0,
IsLateInit = 0b_000010000_000_00_000_0,
HasConstant = 0b_000100000_000_00_000_0,
IsExternalProperty = 0b_001000000_000_00_000_0,
IsDelegated = 0b_010000000_000_00_000_0,
IsExpectProperty = 0b_100000000_000_00_000_0
IsVar = 0b_000000001_00_00_000_0,
HasGetter = 0b_000000010_00_00_000_0,
HasSetter = 0b_000000100_00_00_000_0,
IsConst = 0b_000001000_00_00_000_0,
IsLateInit = 0b_000010000_00_00_000_0,
HasConstant = 0b_000100000_00_00_000_0,
IsExternalProperty = 0b_001000000_00_00_000_0,
IsDelegated = 0b_010000000_00_00_000_0,
IsExpectProperty = 0b_100000000_00_00_000_0
}

[Flags]
Expand Down
36 changes: 28 additions & 8 deletions src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinFixups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ static void FixupProperty (MethodInfo? getter, MethodInfo? setter, KotlinPropert
if (getter is null && setter is null)
return;

// Hide property if it isn't Public/Protected
if (!metadata.Flags.IsPubliclyVisible ()) {
// Hide getters/setters if property is Internal
if (metadata.IsInternalVisibility) {

if (getter?.IsPubliclyVisible == true) {
Log.Debug ($"Kotlin: Hiding internal getter method {getter.DeclaringType?.ThisClass.Name.Value} - {getter.Name}");
Expand Down Expand Up @@ -384,7 +384,17 @@ static void FixupField (FieldInfo? field, KotlinProperty metadata)

static MethodInfo? FindJavaPropertyGetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
{
var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"get{property.Name}", StringComparison.OrdinalIgnoreCase) == 0 &&
// Private properties do not have getters
if (property.IsPrivateVisibility)
return null;

// Public/protected getters look like "getFoo"
// Internal getters look like "getFoo$main"
var possible_methods = property.IsInternalVisibility ?
klass.Methods.Where (method => method.Name.StartsWith ($"get{property.Name.Capitalize ()}$", StringComparison.Ordinal)) :
klass.Methods.Where (method => method.Name.Equals ($"get{property.Name.Capitalize ()}", StringComparison.Ordinal));

possible_methods = possible_methods.Where (method =>
method.GetParameters ().Length == 0 &&
property.ReturnType != null &&
TypesMatch (method.ReturnType, property.ReturnType, kotlinClass));
Expand All @@ -394,11 +404,21 @@ static void FixupField (FieldInfo? field, KotlinProperty metadata)

static MethodInfo? FindJavaPropertySetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
{
var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", StringComparison.OrdinalIgnoreCase) == 0 &&
property.ReturnType != null &&
method.GetParameters ().Length == 1 &&
method.ReturnType.BinaryName == "V" &&
TypesMatch (method.GetParameters () [0].Type, property.ReturnType, kotlinClass));
// Private properties do not have setters
if (property.IsPrivateVisibility)
return null;

// Public/protected setters look like "setFoo"
// Internal setters look like "setFoo$main"
var possible_methods = property.IsInternalVisibility ?
klass.Methods.Where (method => method.Name.StartsWith ($"set{property.Name.Capitalize ()}$", StringComparison.Ordinal)) :
klass.Methods.Where (method => method.Name.Equals ($"set{property.Name.Capitalize ()}", StringComparison.Ordinal));

possible_methods = possible_methods.Where (method =>
property.ReturnType != null &&
method.GetParameters ().Length == 1 &&
method.ReturnType.BinaryName == "V" &&
TypesMatch (method.GetParameters () [0].Type, property.ReturnType, kotlinClass));

return possible_methods.FirstOrDefault ();
}
Expand Down
13 changes: 13 additions & 0 deletions src/Xamarin.Android.Tools.Bytecode/Kotlin/KotlinUtilities.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -8,6 +9,18 @@ namespace Xamarin.Android.Tools.Bytecode
{
public static class KotlinUtilities
{
[return: NotNullIfNotNull (nameof (value))]
public static string? Capitalize (this string? value)
{
if (string.IsNullOrWhiteSpace (value))
return value;

if (value.Length < 1)
return value;

return char.ToUpperInvariant (value [0]) + value.Substring (1);
}

public static string ConvertKotlinTypeSignature (KotlinType? type, KotlinFile? metadata = null, bool convertUnsignedToPrimitive = true)
{
if (type is null)
Expand Down
22 changes: 22 additions & 0 deletions tests/Xamarin.Android.Tools.Bytecode-Tests/KotlinFixupsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,29 @@ public void HandleKotlinNameShadowing ()

Assert.True (klass.Methods.Single (m => m.Name == "count").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "hitCount").AccessFlags.HasFlag (MethodAccessFlags.Public));

// Private property and explicit getter/setter
// There is no generated getter/setter, and explicit ones should be public
Assert.True (klass.Methods.Single (m => m.Name == "getType").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "setType").AccessFlags.HasFlag (MethodAccessFlags.Public));

// Private immutable property and explicit getter/setter
// There is no generated getter/setter, and explicit ones should be public
Assert.True (klass.Methods.Single (m => m.Name == "getType2").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "setType2").AccessFlags.HasFlag (MethodAccessFlags.Public));

// Internal property and explicit getter/setter
// Generated getter/setter should not be public, and explicit ones should be public
Assert.False (klass.Methods.Single (m => m.Name == "getItype$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.False (klass.Methods.Single (m => m.Name == "setItype$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "getItype").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "setItype").AccessFlags.HasFlag (MethodAccessFlags.Public));

// Internal immutable property and explicit getter/setter
// Generated getter should not be public, and explicit ones should be public
Assert.False (klass.Methods.Single (m => m.Name == "getItype2$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "getItype2").AccessFlags.HasFlag (MethodAccessFlags.Public));
Assert.True (klass.Methods.Single (m => m.Name == "setItype2").AccessFlags.HasFlag (MethodAccessFlags.Public));
}

[Test]
Expand Down
Binary file not shown.
20 changes: 18 additions & 2 deletions tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/NameShadowing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,23 @@ public class NameShadowing {
private var hitCount = 0
fun hitCount(): Int = hitCount

// Property and setter
// Private property and explicit getter/setter
private var type = 0
fun setType(type: Int) = { println (type); }
fun getType(): Int = type
fun setType(type: Int) { }

// Private immutable property and explicit getter/setter
private val type2 = 0
fun getType2(): Int = type2
fun setType2(type: Int) { }

// Internal property and explicit getter/setter
internal var itype = 0
fun getItype(): Int = itype
fun setItype(type: Int) { }

// Internal immutable property and explicit getter/setter
internal val itype2 = 0
fun getItype2(): Int = itype2
fun setItype2(type: Int) { }
}

0 comments on commit 4931251

Please sign in to comment.