Skip to content

Commit 4931251

Browse files
committed
[Xamarin.Android.Tools.Bytecode] Only hide getters/setters for internal properties.
1 parent 3c83179 commit 4931251

File tree

6 files changed

+96
-19
lines changed

6 files changed

+96
-19
lines changed

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ public class KotlinProperty : KotlinMethodBase
318318
}
319319

320320
public override string? ToString () => Name;
321+
322+
public bool IsInternalVisibility => (Flags & KotlinPropertyFlags.VisibilityMask) == KotlinPropertyFlags.Internal;
323+
324+
public bool IsPrivateVisibility => (Flags & KotlinPropertyFlags.VisibilityMask) == KotlinPropertyFlags.Private;
321325
}
322326

323327
public class KotlinType
@@ -688,6 +692,8 @@ public enum KotlinPropertyFlags
688692
PrivateToThis = 0b00_00_100_0,
689693
Local = 0b00_00_101_0,
690694

695+
VisibilityMask = 0b00_00_111_0,
696+
691697
Final = 0b00_00_000_0,
692698
Open = 0b00_01_000_0,
693699
Abstract = 0b00_10_000_0,
@@ -698,15 +704,15 @@ public enum KotlinPropertyFlags
698704
Delegation = 0b10_00_000_0,
699705
Synthesized = 0b11_00_000_0,
700706

701-
IsVar = 0b_000000001_000_00_000_0,
702-
HasGetter = 0b_000000010_000_00_000_0,
703-
HasSetter = 0b_000000100_000_00_000_0,
704-
IsConst = 0b_000001000_000_00_000_0,
705-
IsLateInit = 0b_000010000_000_00_000_0,
706-
HasConstant = 0b_000100000_000_00_000_0,
707-
IsExternalProperty = 0b_001000000_000_00_000_0,
708-
IsDelegated = 0b_010000000_000_00_000_0,
709-
IsExpectProperty = 0b_100000000_000_00_000_0
707+
IsVar = 0b_000000001_00_00_000_0,
708+
HasGetter = 0b_000000010_00_00_000_0,
709+
HasSetter = 0b_000000100_00_00_000_0,
710+
IsConst = 0b_000001000_00_00_000_0,
711+
IsLateInit = 0b_000010000_00_00_000_0,
712+
HasConstant = 0b_000100000_00_00_000_0,
713+
IsExternalProperty = 0b_001000000_00_00_000_0,
714+
IsDelegated = 0b_010000000_00_00_000_0,
715+
IsExpectProperty = 0b_100000000_00_00_000_0
710716
}
711717

712718
[Flags]

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

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ static void FixupProperty (MethodInfo? getter, MethodInfo? setter, KotlinPropert
272272
if (getter is null && setter is null)
273273
return;
274274

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

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

385385
static MethodInfo? FindJavaPropertyGetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
386386
{
387-
var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"get{property.Name}", StringComparison.OrdinalIgnoreCase) == 0 &&
387+
// Private properties do not have getters
388+
if (property.IsPrivateVisibility)
389+
return null;
390+
391+
// Public/protected getters look like "getFoo"
392+
// Internal getters look like "getFoo$main"
393+
var possible_methods = property.IsInternalVisibility ?
394+
klass.Methods.Where (method => method.Name.StartsWith ($"get{property.Name.Capitalize ()}$", StringComparison.Ordinal)) :
395+
klass.Methods.Where (method => method.Name.Equals ($"get{property.Name.Capitalize ()}", StringComparison.Ordinal));
396+
397+
possible_methods = possible_methods.Where (method =>
388398
method.GetParameters ().Length == 0 &&
389399
property.ReturnType != null &&
390400
TypesMatch (method.ReturnType, property.ReturnType, kotlinClass));
@@ -394,11 +404,21 @@ static void FixupField (FieldInfo? field, KotlinProperty metadata)
394404

395405
static MethodInfo? FindJavaPropertySetter (KotlinFile kotlinClass, KotlinProperty property, ClassFile klass)
396406
{
397-
var possible_methods = klass.Methods.Where (method => string.Compare (method.GetMethodNameWithoutSuffix (), $"set{property.Name}", StringComparison.OrdinalIgnoreCase) == 0 &&
398-
property.ReturnType != null &&
399-
method.GetParameters ().Length == 1 &&
400-
method.ReturnType.BinaryName == "V" &&
401-
TypesMatch (method.GetParameters () [0].Type, property.ReturnType, kotlinClass));
407+
// Private properties do not have setters
408+
if (property.IsPrivateVisibility)
409+
return null;
410+
411+
// Public/protected setters look like "setFoo"
412+
// Internal setters look like "setFoo$main"
413+
var possible_methods = property.IsInternalVisibility ?
414+
klass.Methods.Where (method => method.Name.StartsWith ($"set{property.Name.Capitalize ()}$", StringComparison.Ordinal)) :
415+
klass.Methods.Where (method => method.Name.Equals ($"set{property.Name.Capitalize ()}", StringComparison.Ordinal));
416+
417+
possible_methods = possible_methods.Where (method =>
418+
property.ReturnType != null &&
419+
method.GetParameters ().Length == 1 &&
420+
method.ReturnType.BinaryName == "V" &&
421+
TypesMatch (method.GetParameters () [0].Type, property.ReturnType, kotlinClass));
402422

403423
return possible_methods.FirstOrDefault ();
404424
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using System.Text;
56
using System.Threading.Tasks;
@@ -8,6 +9,18 @@ namespace Xamarin.Android.Tools.Bytecode
89
{
910
public static class KotlinUtilities
1011
{
12+
[return: NotNullIfNotNull (nameof (value))]
13+
public static string? Capitalize (this string? value)
14+
{
15+
if (string.IsNullOrWhiteSpace (value))
16+
return value;
17+
18+
if (value.Length < 1)
19+
return value;
20+
21+
return char.ToUpperInvariant (value [0]) + value.Substring (1);
22+
}
23+
1124
public static string ConvertKotlinTypeSignature (KotlinType? type, KotlinFile? metadata = null, bool convertUnsignedToPrimitive = true)
1225
{
1326
if (type is null)

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,29 @@ public void HandleKotlinNameShadowing ()
324324

325325
Assert.True (klass.Methods.Single (m => m.Name == "count").AccessFlags.HasFlag (MethodAccessFlags.Public));
326326
Assert.True (klass.Methods.Single (m => m.Name == "hitCount").AccessFlags.HasFlag (MethodAccessFlags.Public));
327+
328+
// Private property and explicit getter/setter
329+
// There is no generated getter/setter, and explicit ones should be public
330+
Assert.True (klass.Methods.Single (m => m.Name == "getType").AccessFlags.HasFlag (MethodAccessFlags.Public));
327331
Assert.True (klass.Methods.Single (m => m.Name == "setType").AccessFlags.HasFlag (MethodAccessFlags.Public));
332+
333+
// Private immutable property and explicit getter/setter
334+
// There is no generated getter/setter, and explicit ones should be public
335+
Assert.True (klass.Methods.Single (m => m.Name == "getType2").AccessFlags.HasFlag (MethodAccessFlags.Public));
336+
Assert.True (klass.Methods.Single (m => m.Name == "setType2").AccessFlags.HasFlag (MethodAccessFlags.Public));
337+
338+
// Internal property and explicit getter/setter
339+
// Generated getter/setter should not be public, and explicit ones should be public
340+
Assert.False (klass.Methods.Single (m => m.Name == "getItype$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
341+
Assert.False (klass.Methods.Single (m => m.Name == "setItype$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
342+
Assert.True (klass.Methods.Single (m => m.Name == "getItype").AccessFlags.HasFlag (MethodAccessFlags.Public));
343+
Assert.True (klass.Methods.Single (m => m.Name == "setItype").AccessFlags.HasFlag (MethodAccessFlags.Public));
344+
345+
// Internal immutable property and explicit getter/setter
346+
// Generated getter should not be public, and explicit ones should be public
347+
Assert.False (klass.Methods.Single (m => m.Name == "getItype2$main").AccessFlags.HasFlag (MethodAccessFlags.Public));
348+
Assert.True (klass.Methods.Single (m => m.Name == "getItype2").AccessFlags.HasFlag (MethodAccessFlags.Public));
349+
Assert.True (klass.Methods.Single (m => m.Name == "setItype2").AccessFlags.HasFlag (MethodAccessFlags.Public));
328350
}
329351

330352
[Test]
Binary file not shown.

tests/Xamarin.Android.Tools.Bytecode-Tests/kotlin/NameShadowing.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,23 @@ public class NameShadowing {
88
private var hitCount = 0
99
fun hitCount(): Int = hitCount
1010

11-
// Property and setter
11+
// Private property and explicit getter/setter
1212
private var type = 0
13-
fun setType(type: Int) = { println (type); }
13+
fun getType(): Int = type
14+
fun setType(type: Int) { }
15+
16+
// Private immutable property and explicit getter/setter
17+
private val type2 = 0
18+
fun getType2(): Int = type2
19+
fun setType2(type: Int) { }
20+
21+
// Internal property and explicit getter/setter
22+
internal var itype = 0
23+
fun getItype(): Int = itype
24+
fun setItype(type: Int) { }
25+
26+
// Internal immutable property and explicit getter/setter
27+
internal val itype2 = 0
28+
fun getItype2(): Int = itype2
29+
fun setItype2(type: Int) { }
1430
}

0 commit comments

Comments
 (0)