Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,123 @@ public void UnsupportedOSPlatformIgnoresPropertyOverrides ()
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", ifaceActual, "Should not contain UnsupportedOSPlatform on interface property override!");
}

[Test]
public void UnsupportedOSPlatformIgnoresPropertySetterMovedToStandaloneBaseMethod ()
{
var xml = @$"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='com.example' jni-name='com/example'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='Base' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='setValue' bridge='false' managedName='SetRawValue' native='false' propertyName='RawValue' return='void' static='false' synchronized='false' synthetic='false' visibility='public'>
<parameter name='value' type='java.lang.Object' />
</method>
</class>
<class abstract='false' deprecated='not deprecated' extends='com.example.Base' extends-generic-aware='com.example.Base' final='false' name='Derived' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getValue' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' />
<method abstract='false' deprecated='not deprecated' final='false' name='setValue' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
<parameter name='value' type='java.lang.Object' />
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var klass = gens.Single (g => g.Name == "Derived");
var property = klass.Properties.Single (p => p.Name == "Value");
var actual = GetGeneratedTypeOutput (klass);

Assert.IsNotNull (property.Setter, "The removed setter must be tested as a property accessor.");
StringAssert.Contains ("public virtual unsafe Java.Lang.Object Value {", actual);
StringAssert.Contains ("set {", actual);
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual);
}

[Test]
public void UnsupportedOSPlatformIgnoresSpecializedGenericBasePropertySetter ()
{
var xml = @$"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='android.widget' jni-name='android/widget'>
<interface abstract='true' deprecated='not deprecated' final='false' name='Adapter' static='false' visibility='public' />
<interface abstract='true' deprecated='not deprecated' final='false' name='ListAdapter' static='false' visibility='public'>
<implements name='android.widget.Adapter' name-generic-aware='android.widget.Adapter' />
</interface>
<class abstract='true' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='AdapterView' static='false' visibility='public'>
<typeParameters>
<typeParameter name='T' interfaceBounds='android.widget.Adapter'>
<genericConstraints>
<genericConstraint type='android.widget.Adapter' />
</genericConstraints>
</typeParameter>
</typeParameters>
<method abstract='true' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='T' static='false' synchronized='false' synthetic='false' visibility='public' />
<method abstract='true' deprecated='not deprecated' final='false' name='setAdapter' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public'>
<parameter name='value' type='T' />
</method>
</class>
<class abstract='false' deprecated='not deprecated' extends='android.widget.AdapterView' extends-generic-aware='android.widget.AdapterView&lt;android.widget.ListAdapter&gt;' final='false' name='GridView' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='android.widget.ListAdapter' static='false' synchronized='false' synthetic='false' visibility='public' />
<method abstract='false' deprecated='not deprecated' final='false' name='setAdapter' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
<parameter name='value' type='android.widget.ListAdapter' />
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var klass = gens.Single (g => g.Name == "GridView");
var property = klass.Properties.Single (p => p.Name == "Adapter");
var actual = GetGeneratedTypeOutput (klass);

Assert.IsNotNull (property.Setter, "The specialized setter must be tested as a property accessor.");
StringAssert.Contains ("set {", actual);
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual);
}

[Test]
public void UnsupportedOSPlatformPreservesPropertySetterForDifferentGenericBaseParameter ()
{
var xml = @$"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='true' name='String' static='false' visibility='public' />
</package>
<package name='com.example' jni-name='com/example'>
<interface abstract='true' deprecated='not deprecated' final='false' name='Container' static='false' visibility='public'>
<typeParameters>
<typeParameter name='E' />
</typeParameters>
</interface>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='Base' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='setValue' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public'>
<parameter name='value' type='com.example.Container&lt;java.lang.String&gt;' />
</method>
</class>
<class abstract='false' deprecated='not deprecated' extends='com.example.Base' extends-generic-aware='com.example.Base' final='false' name='Derived' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getValue' bridge='false' native='false' return='java.lang.String' static='false' synchronized='false' synthetic='false' visibility='public' />
<method abstract='false' deprecated='not deprecated' final='false' name='setValue' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
<parameter name='value' type='java.lang.String' />
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var baseSetter = gens.Single (g => g.Name == "Base").GetAllMethods ().Single (m => m.JavaName == "setValue");
var klass = gens.Single (g => g.Name == "Derived");
var property = klass.Properties.Single (p => p.Name == "Value");
var actual = GetGeneratedTypeOutput (klass);

Assert.IsTrue (baseSetter.Parameters [0].IsGeneric, $"Expected a concrete generic parameter, but found {baseSetter.Parameters [0].Symbol.GetType ().FullName}.");
Assert.IsNotNull (property.Setter, "The removed setter must be tested as a property accessor.");
StringAssert.Contains ("set {", actual);
StringAssert.Contains ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual);
}

[Test]
public void StringPropertyOverride ([Values ("true", "false")] string final)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,37 +339,30 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
}
}

// Process property getter/setter methods for ApiRemovedSince fixup
// A property accessor can correspond to a standalone method in a base class
// when the accessor types differ. Compare the underlying Java methods.
foreach (var prop in Properties) {
for (var bt = GetBaseGen (opt); bt != null; bt = bt.GetBaseGen (opt)) {
var baseProp = bt.Properties.FirstOrDefault (p => p.Name == prop.Name && p.Type == prop.Type);
if (baseProp == null) {
foreach (var m in new [] { prop.Getter, prop.Setter }) {
if (m == null || m.ApiRemovedSince == 0) {
continue;
}

bool shouldBreak = false;
if (prop.Getter != null && prop.Getter.ApiRemovedSince > 0 && baseProp.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
if (baseProp.Getter.Visibility == prop.Getter.Visibility &&
ParameterList.Equals (baseProp.Getter.Parameters, prop.Getter.Parameters) &&
baseProp.Getter.RetVal.FullName == prop.Getter.RetVal.FullName) {
// If a "removed" property getter overrides a "not removed" getter, the method was
// likely moved to a base class, so don't mark it as removed.
prop.Getter.ApiRemovedSince = default;
shouldBreak = true;
}
}
if (prop.Setter != null && prop.Setter.ApiRemovedSince > 0 && baseProp.Setter != null && baseProp.Setter.ApiRemovedSince == 0) {
if (baseProp.Setter.Visibility == prop.Setter.Visibility &&
ParameterList.Equals (baseProp.Setter.Parameters, prop.Setter.Parameters)) {
// If a "removed" property setter overrides a "not removed" setter, the method was
// likely moved to a base class, so don't mark it as removed.
prop.Setter.ApiRemovedSince = default;
shouldBreak = true;
for (var bt = GetBaseGen (opt); bt != null; bt = bt.GetBaseGen (opt)) {
var bm = bt.GetAllMethods ().FirstOrDefault (mm =>
mm.ApiRemovedSince == 0 &&
mm.JavaName == m.JavaName &&
mm.Visibility == m.Visibility &&
((m == prop.Getter &&
mm.RetVal.FullName == m.RetVal.FullName &&
ParameterList.Equals (mm.Parameters, m.Parameters)) ||
(m == prop.Setter &&
(ParameterList.Equals (mm.Parameters, m.Parameters) ||
(mm.Parameters.Count == 1 && mm.Parameters [0].Symbol is GenericTypeParameter)))));
if (bm != null) {
m.ApiRemovedSince = default;
break;
}
}
if (shouldBreak) {
break;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,18 @@ public void BuildHasNoWarnings (bool isRelease, bool multidex, string packageFor
proj.SetProperty ("XamarinAndroidSupportSkipVerifyVersions", "True"); // Disables API 29 warning in Xamarin.Build.Download
proj.SetProperty ("AndroidPackageFormat", packageFormat);
proj.SetProperty ("TrimmerSingleWarn", "false");
// Regression tests for:
// https://github.com/dotnet/android/issues/10509
// https://github.com/dotnet/android/issues/12107
proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}",
"""
// These should not cause warnings
new FrameLayout (this).Foreground = null;
new GridView (this).Adapter = null;
new ListView (this).Adapter = null;
Console.WriteLine (Android.Provider.MediaStore.Video.IVideoColumns.DateTaken);
Console.WriteLine (Android.Provider.MediaStore.Images.IImageColumns.DateTaken);
""");
using (var b = CreateApkBuilder ()) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");

Expand Down