Skip to content

Commit

Permalink
[generator] Support [Obsolete]/[SupportedOSPlatform] attributes for e…
Browse files Browse the repository at this point in the history
…num members.
  • Loading branch information
jpobst committed Dec 12, 2022
1 parent 149d70f commit c2f4169
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 21 deletions.
87 changes: 80 additions & 7 deletions tests/generator-Tests/Unit-Tests/EnumGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Java.Interop.Tools.Generator.Enumification;
using MonoDroid.Generation;
using NUnit.Framework;
using NUnit.Framework.Internal;
using Xamarin.Android.Binder;

namespace generatortests
{
[TestFixture]
class EnumGeneratorTests
class EnumGeneratorTests : CodeGeneratorTestBase
{
protected EnumGenerator generator;
protected StringBuilder builder;
protected StringWriter writer;
protected new EnumGenerator generator;

protected override CodeGenerationTarget Target => CodeGenerationTarget.XAJavaInterop1;

[SetUp]
public void SetUp ()
public new void SetUp ()
{
builder = new StringBuilder ();
writer = new StringWriter (builder);
Expand Down Expand Up @@ -60,7 +62,78 @@ public void WriteEnumWithGens ()
Assert.AreEqual (GetExpected (nameof (WriteEnumWithGens)), writer.ToString ().NormalizeLineEndings ());
}

protected string GetExpected (string testName)
[Test]
public void ObsoletedOSPlatformAttributeSupport ()
{
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.app' jni-name='android/app'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
<field deprecated='deprecated' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
</class>
</package>
</api>";

options.UseObsoletedOSPlatformAttributes = true;

var enu = CreateEnum ();
var gens = ParseApiDefinition (xml);

generator.WriteEnumeration (options, enu, gens.ToArray ());

// Ensure [ObsoletedOSPlatform] and [SupportedOSPlatform] are written
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Runtime.Versioning.SupportedOSPlatformAttribute(\"android30.0\")][global::System.Runtime.Versioning.ObsoletedOSPlatform(\"android31.0\")]WithExcluded=1"), writer.ToString ());
}

[Test]
public void ObsoleteAttributeSupport ()
{
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.app' jni-name='android/app'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
<field deprecated='deprecated' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
</class>
</package>
</api>";

var enu = CreateEnum ();
var gens = ParseApiDefinition (xml);

generator.WriteEnumeration (options, enu, gens.ToArray ());

// Ensure [Obsolete] is written
Assert.True (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]WithExcluded=1"), writer.ToString ());
}

[Test]
public void ObsoleteFieldButNotEnumAttributeSupport ()
{
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.app' jni-name='android/app'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='ActivityManager' static='false' visibility='public' jni-signature='Landroid/app/ActivityManager;'>
<field deprecated='This constant will be removed in the future version. Use Android.App.RecentTaskFlags enum directly instead of this field.' final='true' name='RECENT_IGNORE_UNAVAILABLE' jni-signature='Ljava/lang/String;' static='true' transient='false' type='java.lang.String' type-generic-aware='java.lang.String' value='&quot;android.permission.BIND_CHOOSER_TARGET_SERVICE&quot;' visibility='public' volatile='false' deprecated-since='31' api-since='30' />
</class>
</package>
</api>";

var enu = CreateEnum ();
var gens = ParseApiDefinition (xml);

generator.WriteEnumeration (options, enu, gens.ToArray ());

// [Obsolete] should not be written because the value isn't deprecated, just the _field_ is deprecated because we want people to use the enum instead
Assert.False (writer.ToString ().NormalizeLineEndings ().Contains ("[global::System.Obsolete(@\"deprecated\")]WithExcluded=1"), writer.ToString ());
}

protected new string GetExpected (string testName)
{
var root = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);

Expand All @@ -71,7 +144,7 @@ protected string GetExpected (string testName)
{
var enu = new EnumMappings.EnumDescription {
Members = new List<ConstantEntry> {
new ConstantEntry { EnumMember = "WithExcluded", Value = "1", JavaSignature = "android/app/ActivityManager.RECENT_IGNORE_UNAVAILABLE" },
new ConstantEntry { EnumMember = "WithExcluded", Value = "1", JavaSignature = "android/app/ActivityManager.RECENT_IGNORE_UNAVAILABLE", ApiLevel = 30 },
new ConstantEntry { EnumMember = "IgnoreUnavailable", Value = "2", JavaSignature = "android/app/ActivityManager.RECENT_WITH_EXCLUDED" }
},
BitField = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,36 +50,43 @@ EnumWriter CreateWriter (CodeGenerationOptions opt, KeyValuePair<string, EnumDes
Value = member.Value.Trim (),
};

// Try to find the original field in our model
var managedMember = FindManagedMember (enu.Value, member, gens);
var managedMemberName = managedMember != null ? $"{managedMember.Value.Cls.FullName}.{managedMember.Value.Field.Name}" : null;

if (opt.CodeGenerationTarget != CodeGenerationTarget.JavaInterop1)
m.Attributes.Add (new IntDefinitionAttr (managedMember, StripExtraInterfaceSpec (member.JavaSignature)));
m.Attributes.Add (new IntDefinitionAttr (managedMemberName, StripExtraInterfaceSpec (member.JavaSignature)));

SourceWriterExtensions.AddSupportedOSPlatform (m.Attributes, member.ApiLevel, opt);

// Some of our source fields may have been marked with:
// "This constant will be removed in the future version. Use XXX enum directly instead of this field."
// We don't want this message to propogate to the enum.
if (managedMember != null && !managedMember.Value.Field.DeprecatedComment.Contains ("enum directly instead of this field"))
SourceWriterExtensions.AddObsolete (m.Attributes, managedMember.Value.Field.DeprecatedComment, opt, deprecatedSince: managedMember.Value.Field.DeprecatedSince);

enoom.Members.Add (m);
}

return enoom;
}

string FindManagedMember (EnumDescription desc, ConstantEntry member, IEnumerable<GenBase> gens)
WeakReference cache_found_class;

(GenBase Cls, Field Field)? FindManagedMember (EnumDescription desc, ConstantEntry constant, IEnumerable<GenBase> gens)
{
if (desc.FieldsRemoved)
return null;

var jniMember = member.JavaSignature;
var jniMember = constant.JavaSignature;

if (string.IsNullOrWhiteSpace (jniMember)) {
// enum values like "None" falls here.
return null;
}
return FindManagedMember (jniMember, gens);
}

WeakReference cache_found_class;
ParseJniMember (jniMember, out var package, out var type, out var member);

string FindManagedMember (string jniMember, IEnumerable<GenBase> gens)
{
string package, type, member;
ParseJniMember (jniMember, out package, out type, out member);
var fullJavaType = (string.IsNullOrEmpty (package) ? "" : package + ".") + type;

var cls = cache_found_class != null ? cache_found_class.Target as GenBase : null;
Expand All @@ -96,7 +103,7 @@ string FindManagedMember (string jniMember, IEnumerable<GenBase> gens)
// The field was not found e.g. removed by metadata fixup.
return null;
}
return cls.FullName + "." + fld.Name;
return (cls, fld);
}

internal void ParseJniMember (string jniMember, out string package, out string type, out string member)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,14 @@ public static void AddParameterListCallArgs (List<string> body, ParameterList pa
}

public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, ApiVersionsSupport.IApiAvailability member, CodeGenerationOptions opt)
=> AddSupportedOSPlatform (attributes, member.ApiAvailableSince, opt);

public static void AddSupportedOSPlatform (List<AttributeWriter> attributes, int since, CodeGenerationOptions opt)
{
// There's no sense in writing say 'android15' because we do not support older APIs,
// so those APIs will be available in all of our versions.
if (member.ApiAvailableSince > 21 && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
attributes.Add (new SupportedOSPlatformAttr (member.ApiAvailableSince));

if (since > 21 && opt.CodeGenerationTarget == Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
attributes.Add (new SupportedOSPlatformAttr (since));
}

public static void AddObsolete (List<AttributeWriter> attributes, string message, CodeGenerationOptions opt, bool forceDeprecate = false, bool isError = false, int? deprecatedSince = null)
Expand Down

0 comments on commit c2f4169

Please sign in to comment.