Skip to content

Commit be6cc8f

Browse files
authored
[Java.Interop] Add JniTypeSignatureAttribute.InvokerType (#1284)
Context: #1263 Context: #1263 (comment) We want the default trimmer infrastructure to be able to automatically preserve the `*Invoker` types which are required for interacting with `abstract` classes and interfaces. The most straightforward way to do this is to add a new `InvokerType` property to `JniTypeSignatureAttribute` (and eventually `RegisterAttribute`): partial class JniTypeSignatureAttribute { [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public Type? InvokerType {get; set;} } Update `generator` so that `generator --codegen-target=JavaInterop1` output sets this new property on abstract classes and interfaces: namespace Java.Lang { [Java.Interop.JniTypeSignatureAttribute("java/lang/Runnable", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.IRunnableInvoker))] public partial interface IRunnable { } internal partial class IRunnableInvoker { } [Java.Interop.JniTypeSignatureAttribute("java/lang/Number", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.NumberInvoker))] public abstract partial class Number { } internal partial class NumberInvoker { } } This allows the default trimmer to automatically preserve the `*Invoker` type and constructors. Update `Java.Interop.JniRuntime.JniValueManager` to no longer look for `*Invoker` types "by string", and instead require use of the `JniTypeSignatureAttribute.InvokerType` property. Update unit tests and expected output so that everything works.
1 parent 619286d commit be6cc8f

37 files changed

+203
-201
lines changed

src/Java.Base-ref.cs

Lines changed: 147 additions & 147 deletions
Large diffs are not rendered by default.

src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -386,18 +386,9 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
386386
[return: DynamicallyAccessedMembers (Constructors)]
387387
static Type? GetInvokerType (Type type)
388388
{
389-
const string suffix = "Invoker";
390-
391389
// https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186
392-
const string assemblyGetTypeMessage = "'Invoker' types are preserved by the MarkJavaObjects trimmer step.";
393390
const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step.";
394391

395-
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = assemblyGetTypeMessage)]
396-
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = assemblyGetTypeMessage)]
397-
[return: DynamicallyAccessedMembers (Constructors)]
398-
static Type? AssemblyGetType (Assembly assembly, string typeName) =>
399-
assembly.GetType (typeName);
400-
401392
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)]
402393
[return: DynamicallyAccessedMembers (Constructors)]
403394
static Type MakeGenericType (
@@ -409,18 +400,16 @@ static Type MakeGenericType (
409400
type.MakeGenericType (arguments);
410401
#pragma warning restore IL3050
411402

403+
var signature = type.GetCustomAttribute<JniTypeSignatureAttribute> ();
404+
if (signature == null || signature.InvokerType == null) {
405+
return null;
406+
}
407+
412408
Type[] arguments = type.GetGenericArguments ();
413409
if (arguments.Length == 0)
414-
return AssemblyGetType (type.Assembly, type + suffix);
415-
Type definition = type.GetGenericTypeDefinition ();
416-
int bt = definition.FullName!.IndexOf ("`", StringComparison.Ordinal);
417-
if (bt == -1)
418-
throw new NotSupportedException ("Generic type doesn't follow generic type naming convention! " + type.FullName);
419-
Type? suffixDefinition = AssemblyGetType (definition.Assembly,
420-
definition.FullName.Substring (0, bt) + suffix + definition.FullName.Substring (bt));
421-
if (suffixDefinition == null)
422-
return null;
423-
return MakeGenericType (suffixDefinition, arguments);
410+
return signature.InvokerType;
411+
412+
return MakeGenericType (signature.InvokerType, arguments);
424413
}
425414

426415
public object? CreateValue (

src/Java.Interop/Java.Interop/JniTypeSignatureAttribute.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#nullable enable
22

33
using System;
4+
using System.Diagnostics.CodeAnalysis;
45

56
namespace Java.Interop
67
{
@@ -31,6 +32,9 @@ public int ArrayRank {
3132
}
3233

3334
public bool GenerateJavaPeer {get; set;}
35+
36+
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
37+
public Type? InvokerType {get; set;}
3438
}
3539
}
3640

src/Java.Interop/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ static Java.Interop.JniEnvironment.BeginMarshalMethod(nint jnienv, out Java.Inte
33
static Java.Interop.JniEnvironment.EndMarshalMethod(ref Java.Interop.JniTransition transition) -> void
44
virtual Java.Interop.JniRuntime.OnEnterMarshalMethod() -> void
55
virtual Java.Interop.JniRuntime.OnUserUnhandledException(ref Java.Interop.JniTransition transition, System.Exception! e) -> void
6+
Java.Interop.JniTypeSignatureAttribute.InvokerType.get -> System.Type?
7+
Java.Interop.JniTypeSignatureAttribute.InvokerType.set -> void

tests/Java.Interop-Tests/Java.Interop/JavaPeerableExtensionsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public unsafe MyJavaInterfaceImpl ()
8080
}
8181
}
8282

83-
[JniTypeSignature (JniTypeName, GenerateJavaPeer=false)]
83+
[JniTypeSignature (JniTypeName, GenerateJavaPeer=false, InvokerType=typeof(IJavaInterfaceInvoker))]
8484
interface IJavaInterface : IJavaPeerable {
8585
internal const string JniTypeName = "net/dot/jni/test/JavaInterface";
8686

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteDuplicateInterfaceEventArgs.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='AnimatorListener']"
2-
[global::Java.Interop.JniTypeSignature ("java/code/AnimatorListener", GenerateJavaPeer=false)]
2+
[global::Java.Interop.JniTypeSignature ("java/code/AnimatorListener", GenerateJavaPeer=false, InvokerType=typeof (java.code.AnimatorListenerInvoker))]
33
public partial interface AnimatorListener : IJavaPeerable {
44
// Metadata.xml XPath method reference: path="/api/package[@name='java.code']/interface[@name='AnimatorListener']/method[@name='OnAnimationEnd' and count(parameter)=1 and parameter[1][@type='int']]"
55
[global::Java.Interop.JniMethodSignature ("OnAnimationEnd", "(I)Z")]

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteInterface.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Metadata.xml XPath interface reference: path="/api/package[@name='java.code']/interface[@name='IMyInterface']"
2-
[global::Java.Interop.JniTypeSignature ("java/code/IMyInterface", GenerateJavaPeer=false)]
2+
[global::Java.Interop.JniTypeSignature ("java/code/IMyInterface", GenerateJavaPeer=false, InvokerType=typeof (java.code.IMyInterfaceInvoker))]
33
public partial interface IMyInterface : IJavaPeerable {
44
private static readonly JniPeerMembers _members = new JniPeerMembers ("java/code/IMyInterface", typeof (IMyInterface), isInterface: true);
55

tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.IExtendedInterface.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Xamarin.Test {
66

77
// Metadata.xml XPath interface reference: path="/api/package[@name='xamarin.test']/interface[@name='ExtendedInterface']"
8-
[global::Java.Interop.JniTypeSignature ("xamarin/test/ExtendedInterface", GenerateJavaPeer=false)]
8+
[global::Java.Interop.JniTypeSignature ("xamarin/test/ExtendedInterface", GenerateJavaPeer=false, InvokerType=typeof (Xamarin.Test.IExtendedInterfaceInvoker))]
99
public partial interface IExtendedInterface : IJavaPeerable {
1010
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/interface[@name='ExtendedInterface']/method[@name='extendedMethod' and count(parameter)=0]"
1111
[global::Java.Interop.JniMethodSignature ("extendedMethod", "()V")]

tests/generator-Tests/expected.ji/AccessModifiers/Xamarin.Test.PublicClass.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Xamarin.Test {
1818
[global::Java.Interop.JniTypeSignature ("xamarin/test/PublicClass", GenerateJavaPeer=false)]
1919
public partial class PublicClass : global::Java.Lang.Object {
2020
// Metadata.xml XPath interface reference: path="/api/package[@name='xamarin.test']/interface[@name='PublicClass.ProtectedInterface']"
21-
[global::Java.Interop.JniTypeSignature ("xamarin/test/PublicClass$ProtectedInterface", GenerateJavaPeer=false)]
21+
[global::Java.Interop.JniTypeSignature ("xamarin/test/PublicClass$ProtectedInterface", GenerateJavaPeer=false, InvokerType=typeof (Xamarin.Test.PublicClass.IProtectedInterfaceInvoker))]
2222
protected internal partial interface IProtectedInterface : IJavaPeerable {
2323
// Metadata.xml XPath method reference: path="/api/package[@name='xamarin.test']/interface[@name='PublicClass.ProtectedInterface']/method[@name='foo' and count(parameter)=0]"
2424
[global::Java.Interop.JniMethodSignature ("foo", "()V")]

tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AbsSpinner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace Xamarin.Test {
1616

1717
// Metadata.xml XPath class reference: path="/api/package[@name='xamarin.test']/class[@name='AbsSpinner']"
18-
[global::Java.Interop.JniTypeSignature ("xamarin/test/AbsSpinner", GenerateJavaPeer=false)]
18+
[global::Java.Interop.JniTypeSignature ("xamarin/test/AbsSpinner", GenerateJavaPeer=false, InvokerType=typeof (AbsSpinnerInvoker))]
1919
public abstract partial class AbsSpinner : Xamarin.Test.AdapterView<Xamarin.Test.ISpinnerAdapter> {
2020
static readonly JniPeerMembers _members = new JniPeerMembers ("xamarin/test/AbsSpinner", typeof (AbsSpinner));
2121

0 commit comments

Comments
 (0)