Skip to content

Commit 1cfb4f4

Browse files
authored
[generator] Add support for emitting [UnsupportedOSPlatform] (#1307)
Fixes: #1309 Context: dotnet/android@944d306 Sometimes, Android *removes* APIs. For example, API-23 *removed* the `android.Manifest.permission.CLEAR_APP_USER_DATA` field. We still list this field, though: [`Android.Manifest.Permission.ClearAppUserData` field][0]. > [Android.Runtime.Register("CLEAR_APP_USER_DATA")] > public const string ClearAppUserData; In dotnet/android@944d3061 we updated `api-merge` to emit `//*/@#removed-since`. Consider this snippet from [`android/src/Mono.Android/Profiles/api-35.xml`][1]: <field deprecated="not deprecated" final="true" name="CLEAR_APP_USER_DATA" jni-signature="Ljava/lang/String;" static="true" transient="false" type="java.lang.String" type-generic-aware="java.lang.String" value="&quot;android.permission.CLEAR_APP_USER_DATA&quot;" visibility="public" volatile="false" removed-since="23" /> Modify `generator` to consume this information and emit `[UnsupportedOSPlatformAttribute ("androidXX.0")]` as needed for API that has been removed from `Mono.Android`. This would update `Android.Manifest.Permission.ClearAppUserData` to be: [Android.Runtime.Register("CLEAR_APP_USER_DATA")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute ("android23.0")] public const string ClearAppUserData; [0]: https://learn.microsoft.com/en-us/dotnet/api/android.manifest.permission.clearappuserdata?view=net-android-34.0 [1]: https://github.com/dotnet/android/blob/d012fb811b4bcebe260400f5fc7a0287108336d7/src/Mono.Android/Profiles/api-35.xml
1 parent f30e420 commit 1cfb4f4

File tree

9 files changed

+87
-4
lines changed

9 files changed

+87
-4
lines changed

tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,36 @@ public void SupportedOSPlatformConstFields ()
14061406
StringAssert.Contains ("[global::System.Runtime.Versioning.SupportedOSPlatformAttribute (\"android30.0\")]", builder.ToString (), "Should contain SupportedOSPlatform!");
14071407
}
14081408

1409+
[Test]
1410+
public void UnsupportedOSPlatform ()
1411+
{
1412+
var klass = SupportTypeBuilder.CreateClass ("java.code.MyClass", options);
1413+
klass.ApiRemovedSince = 30;
1414+
1415+
generator.Context.ContextTypes.Push (klass);
1416+
generator.WriteType (klass, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
1417+
generator.Context.ContextTypes.Pop ();
1418+
1419+
StringAssert.Contains ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", builder.ToString (), "Should contain UnsupportedOSPlatform!");
1420+
}
1421+
1422+
[Test]
1423+
public void UnsupportedOSPlatformConstFields ()
1424+
{
1425+
var klass = new TestClass ("java.lang.Object", "com.mypackage.foo");
1426+
var field = new TestField ("java.lang.String", "bar").SetConstant ("MY_VALUE");
1427+
1428+
field.ApiRemovedSince = 30;
1429+
1430+
klass.Fields.Add (field);
1431+
1432+
generator.Context.ContextTypes.Push (klass);
1433+
generator.WriteType (klass, string.Empty, new GenerationInfo ("", "", "MyAssembly"));
1434+
generator.Context.ContextTypes.Pop ();
1435+
1436+
StringAssert.Contains ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", builder.ToString (), "Should contain UnsupportedOSPlatform!");
1437+
}
1438+
14091439
[Test]
14101440
public void StringPropertyOverride ([Values ("true", "false")] string final)
14111441
{

tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public void CreateClass_CorrectApiSinceOverridePackage ()
5151
Assert.AreEqual (9, klass.ApiAvailableSince);
5252
}
5353

54+
[Test]
55+
public void CreateClass_CorrectApiRemoved ()
56+
{
57+
var xml = XDocument.Parse ("<package name='com.example.test' jni-name='com/example/test'><class name='myclass' removed-since='7' /></package>");
58+
var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class"), opt);
59+
60+
Assert.AreEqual (7, klass.ApiRemovedSince);
61+
}
62+
5463
[Test]
5564
public void CreateCtor_EnsureValidName ()
5665
{

tools/generator/ApiVersionsSupport.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static class ApiVersionsSupport
1818
public interface IApiAvailability
1919
{
2020
int ApiAvailableSince { get; set; }
21+
int ApiRemovedSince { get; set; }
2122
}
2223

2324
static IEnumerable<GenBase> FlattenGens (IEnumerable<GenBase> gens)

tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerat
159159
var ctor = new Ctor (declaringType) {
160160
AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"),
161161
ApiAvailableSince = declaringType.ApiAvailableSince,
162+
ApiRemovedSince = declaringType.ApiRemovedSince,
162163
CustomAttributes = elem.XGetAttribute ("customAttributes"),
163164
Deprecated = elem.Deprecated (),
164165
DeprecatedSince = elem.XGetAttributeAsIntOrNull ("deprecated-since"),
@@ -211,6 +212,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener
211212
var field = new Field {
212213
AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"),
213214
ApiAvailableSince = declaringType.ApiAvailableSince,
215+
ApiRemovedSince = declaringType.ApiRemovedSince,
214216
DeprecatedComment = elem.XGetAttribute ("deprecated"),
215217
DeprecatedSince = elem.XGetAttributeAsIntOrNull ("deprecated-since"),
216218
IsAcw = true,
@@ -369,6 +371,7 @@ public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGen
369371
var method = new Method (declaringType) {
370372
AnnotatedVisibility = elem.XGetAttribute ("annotated-visibility"),
371373
ApiAvailableSince = declaringType.ApiAvailableSince,
374+
ApiRemovedSince = declaringType.ApiRemovedSince,
372375
ArgsType = elem.Attribute ("argsType")?.Value,
373376
CustomAttributes = elem.XGetAttribute ("customAttributes"),
374377
Deprecated = elem.Deprecated (),
@@ -515,9 +518,12 @@ static XElement GetPreviousClass (XNode n, string nameValue)
515518
// Elements need to be passed in the above order. (package, class, member)
516519
static void FillApiSince (ApiVersionsSupport.IApiAvailability model, params XElement[] elems)
517520
{
518-
foreach (var elem in elems)
521+
foreach (var elem in elems) {
519522
if (int.TryParse (elem.XGetAttribute ("api-since"), out var result))
520523
model.ApiAvailableSince = result;
524+
if (int.TryParse (elem.XGetAttribute ("removed-since"), out var removed))
525+
model.ApiRemovedSince = removed;
526+
}
521527
}
522528

523529
static bool IsObfuscatedName (int threshold, string name)

tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo
99
public string AnnotatedVisibility { get; set; }
1010
public string Annotation { get; set; }
1111
public int ApiAvailableSince { get; set; }
12+
public int ApiRemovedSince { get; set; }
1213
public string DeprecatedComment { get; set; }
1314
public int? DeprecatedSince { get; set; }
1415
public bool IsAcw { get; set; }

tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ IEnumerable<GenBase> Ancestors ()
188188

189189
public int ApiAvailableSince { get; set; }
190190

191+
public int ApiRemovedSince { get; set; }
192+
191193
public virtual ClassGen BaseGen => null;
192194

193195
public GenBase BaseSymbol =>

tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ protected MethodBase (GenBase declaringType)
1616
public string AnnotatedVisibility { get; set; }
1717
public string Annotation { get; internal set; }
1818
public int ApiAvailableSince { get; set; }
19+
public int ApiRemovedSince { get; set; }
1920
public string AssemblyName { get; set; }
2021
public GenBase DeclaringType { get; }
2122
public string Deprecated { get; set; }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Xamarin.SourceWriter;
7+
8+
namespace generator.SourceWriters
9+
{
10+
public class UnsupportedOSPlatformAttr : AttributeWriter
11+
{
12+
public int Version { get; }
13+
14+
public UnsupportedOSPlatformAttr (int version) => Version = version;
15+
16+
public override void WriteAttribute (CodeWriter writer)
17+
{
18+
writer.WriteLine ($"[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android{Version}.0\")]");
19+
}
20+
}
21+
}

tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace generator.SourceWriters
1212
{
1313
public static class SourceWriterExtensions
1414
{
15+
const int MINIMUM_API_LEVEL = 21;
16+
1517
public static void AddField (TypeWriter tw, GenBase type, Field field, CodeGenerationOptions opt)
1618
{
1719
if (field.NeedsProperty)
@@ -306,16 +308,26 @@ public static void AddParameterListCallArgs (List<string> body, ParameterList pa
306308
}
307309

308310
public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, ApiVersionsSupport.IApiAvailability member, CodeGenerationOptions opt)
309-
=> AddSupportedOSPlatform (attributes, member.ApiAvailableSince, opt);
311+
{
312+
AddSupportedOSPlatform (attributes, member.ApiAvailableSince, opt);
313+
AddUnsupportedOSPlatform (attributes, member.ApiRemovedSince, opt);
314+
}
310315

311316
public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, int since, CodeGenerationOptions opt)
312317
{
313318
// There's no sense in writing say 'android15' because we do not support older APIs,
314319
// so those APIs will be available in all of our versions.
315-
if (since > 21 && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
320+
if (since > MINIMUM_API_LEVEL && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
316321
attributes.Add (new SupportedOSPlatformAttr (since));
317322
}
318323

324+
public static void AddUnsupportedOSPlatform (List<AttributeWriter> attributes, int since, CodeGenerationOptions opt)
325+
{
326+
// Here it makes sense to still write 'android15' because it will be missing in later versions like `android35`.
327+
if (since > 0 && opt.CodeGenerationTarget == CodeGenerationTarget.XAJavaInterop1)
328+
attributes.Add (new UnsupportedOSPlatformAttr (since));
329+
}
330+
319331
public static void AddObsolete (List<AttributeWriter> attributes, string message, CodeGenerationOptions opt, bool forceDeprecate = false, bool isError = false, int? deprecatedSince = null)
320332
{
321333
// Bail if we're not obsolete
@@ -336,7 +348,7 @@ static bool AddObsoletedOSPlatformAttribute (List<AttributeWriter> attributes, s
336348
return false;
337349

338350
// If it was obsoleted in a version earlier than we support (like 15), use a regular [Obsolete] instead
339-
if (!deprecatedSince.HasValue || deprecatedSince <= 21)
351+
if (!deprecatedSince.HasValue || deprecatedSince <= MINIMUM_API_LEVEL)
340352
return false;
341353

342354
// This is the default Android message, but it isn't useful so remove it

0 commit comments

Comments
 (0)