Skip to content

Commit a25f54e

Browse files
committed
[Java.Interop] Add JniAddNativeMethodRegistration attribute
Implementation of improvement noted in xamarin#205 comments. Instead of "hard-wiring" the name of a method to register native members in a given type, introduce new attribute to mark appropriate method in that type. It is now possible to have multiple registration methods. The `TestType.cs` is updated to test this new behavior. `JniNativeMethodRegistrationArguments::RegisterNativeMethods` is now internal to avoid calling it from 3rd party registration methods as it would be overriden later and the registrations would be lost. Updated all related code to use the new attribute. It also fixes fixes issues reported by Gendarme, `R: Gendarme.Rules.Performance.AvoidUnusedParametersRule` and gets us to 116 defects reported.
1 parent 61c70ec commit a25f54e

File tree

15 files changed

+123
-38
lines changed

15 files changed

+123
-38
lines changed

gendarme-ignore.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,3 +577,7 @@ T: Java.Interop.JavaObject
577577
R: Gendarme.Rules.Design.Generic.DoNotExposeGenericListsRule
578578
# We don't care here as we don't hold the list and create it on request. So to avoid performace penalty, we keep the list as return type
579579
M: System.Collections.Generic.List`1<Java.Interop.JniSurfacedPeerInfo> Java.Interop.JniRuntime/JniValueManager::GetSurfacedPeers()
580+
581+
R: Gendarme.Rules.Concurrency.DoNotUseLockedRegionOutsideMethodRule
582+
# Looks like Gendarme issue, as there are both Monitor.TryEnter and Monitor.Exit used in this method
583+
M: System.Boolean Java.Interop.JniRuntime/JniTypeManager::TryRegisterNativeMembers(Java.Interop.JniType,System.Type,System.String)

src/Java.Interop.Export/Tests/Java.Interop/ExportTest.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
2-
using System.Linq;
32
using System.Linq.Expressions;
43
using System.Reflection;
4+
using System.Collections.Generic;
55

66
using Java.Interop;
77
using Java.Interop.Expressions;
@@ -11,11 +11,10 @@ namespace Java.InteropTests
1111
[JniTypeSignature ("com/xamarin/interop/export/ExportType")]
1212
public class ExportTest : JavaObject
1313
{
14-
static void __RegisterNativeMembers (JniType type, string members)
14+
[JniAddNativeMethodRegistrationAttribute]
15+
static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args)
1516
{
16-
var methods = JniEnvironment.Runtime.MarshalMemberBuilder
17-
.GetExportedMemberRegistrations (typeof (ExportTest));
18-
type.RegisterNativeMethods (methods.ToArray ());
17+
args.AddRegistrations (JniEnvironment.Runtime.MarshalMemberBuilder.GetExportedMemberRegistrations (typeof (ExportTest)));
1918
}
2019

2120
public ExportTest (ref JniObjectReference reference, JniObjectReferenceOptions transfer)

src/Java.Interop/Java.Interop.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@
101101
<DependentUpon>JniPeerMembers.JniFields.tt</DependentUpon>
102102
</Compile>
103103
<Compile Include="Java.Interop\JniPeerMembers.JniStaticFields.cs" />
104+
<Compile Include="Java.Interop\JniAddNativeMethodRegistrationAttribute.cs" />
105+
<Compile Include="Java.Interop\JniNativeMethodRegistrationArguments.cs" />
104106
</ItemGroup>
105107
<ItemGroup Condition=" !$(Configuration.StartsWith('XAIntegration')) ">
106108
<Compile Include="Java.Interop\JavaArray.cs" />

src/Java.Interop/Java.Interop/JavaProxyObject.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ sealed class JavaProxyObject : JavaObject
1111
static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaProxyObject));
1212
static readonly ConditionalWeakTable<object, JavaProxyObject> CachedValues = new ConditionalWeakTable<object, JavaProxyObject> ();
1313

14-
static void __RegisterNativeMembers (JniType type, string members)
14+
[JniAddNativeMethodRegistrationAttribute]
15+
static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args)
1516
{
16-
_members.JniPeerType.RegisterNativeMethods (
17-
new JniNativeMethodRegistration ("equals", "(Ljava/lang/Object;)Z", (Func<IntPtr, IntPtr, IntPtr, bool>) _Equals),
18-
new JniNativeMethodRegistration ("hashCode", "()I", (Func<IntPtr, IntPtr, int>) _GetHashCode),
19-
new JniNativeMethodRegistration ("toString", "()Ljava/lang/String;", (Func<IntPtr, IntPtr, IntPtr>) _ToString));
17+
args.Registrations.Add (new JniNativeMethodRegistration ("equals", "(Ljava/lang/Object;)Z", (Func<IntPtr, IntPtr, IntPtr, bool>)_Equals));
18+
args.Registrations.Add (new JniNativeMethodRegistration ("hashCode", "()I", (Func<IntPtr, IntPtr, int>)_GetHashCode));
19+
args.Registrations.Add (new JniNativeMethodRegistration ("toString", "()Ljava/lang/String;", (Func<IntPtr, IntPtr, IntPtr>)_ToString));
2020
}
2121

2222
public override JniPeerMembers JniPeerMembers {

src/Java.Interop/Java.Interop/JavaProxyThrowable.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ sealed class JavaProxyThrowable : JavaException
77
{
88
new internal const string JniTypeName = "com/xamarin/java_interop/internal/JavaProxyThrowable";
99

10-
static void __RegisterNativeMembers (JniType type, string members)
10+
[JniAddNativeMethodRegistrationAttribute]
11+
static void RegisterNativeMembers ()
1112
{
1213
}
1314

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace Java.Interop
4+
{
5+
[AttributeUsage (AttributeTargets.Method)]
6+
public sealed class JniAddNativeMethodRegistrationAttribute : Attribute
7+
{
8+
}
9+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Java.Interop
5+
{
6+
public struct JniNativeMethodRegistrationArguments
7+
{
8+
public ICollection<JniNativeMethodRegistration> Registrations {
9+
get { return _registrations; }
10+
}
11+
public string Methods { get; }
12+
ICollection<JniNativeMethodRegistration> _registrations;
13+
14+
public JniNativeMethodRegistrationArguments (ICollection<JniNativeMethodRegistration> registrations, string methods)
15+
{
16+
_registrations = registrations ?? throw new ArgumentNullException (nameof (registrations));
17+
Methods = methods ?? throw new ArgumentNullException (nameof (methods));
18+
}
19+
20+
public void AddRegistrations (IEnumerable<JniNativeMethodRegistration> registrations)
21+
{
22+
if (_registrations == null)
23+
throw new InvalidOperationException ($"{nameof(JniNativeMethodRegistrationArguments)} state is invalid. Please use constructor with parameters.");
24+
25+
if (registrations is List<JniNativeMethodRegistration> list) {
26+
list.AddRange (registrations);
27+
} else {
28+
foreach (var registration in registrations)
29+
_registrations.Add (registration);
30+
}
31+
}
32+
}
33+
}

src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Diagnostics;
44
using System.Linq;
55
using System.Reflection;
6+
using System.Threading;
67

78
namespace Java.Interop {
89

@@ -243,15 +244,41 @@ static Assembly TryLoadAssembly (AssemblyName name)
243244
}
244245
}
245246

247+
static List<JniNativeMethodRegistration> sharedRegistrations = new List<JniNativeMethodRegistration> ();
248+
246249
static bool TryRegisterNativeMembers (JniType nativeClass, Type marshalType, string methods)
247250
{
248-
var registerMethod = marshalType.GetTypeInfo ().GetDeclaredMethod ("__RegisterNativeMembers");
249-
if (registerMethod == null) {
250-
return false;
251+
bool lockTaken = false;
252+
253+
try {
254+
Monitor.TryEnter (sharedRegistrations, ref lockTaken);
255+
List<JniNativeMethodRegistration> registrations;
256+
if (lockTaken) {
257+
sharedRegistrations.Clear ();
258+
registrations = sharedRegistrations;
259+
} else {
260+
registrations = new List<JniNativeMethodRegistration> ();
261+
}
262+
JniNativeMethodRegistrationArguments arguments = new JniNativeMethodRegistrationArguments (registrations, methods);
263+
foreach (var methodInfo in marshalType.GetRuntimeMethods ()) {
264+
if (methodInfo.GetCustomAttribute (typeof (JniAddNativeMethodRegistrationAttribute)) == null) {
265+
continue;
266+
}
267+
268+
if ((methodInfo.Attributes & MethodAttributes.Static) != MethodAttributes.Static) {
269+
throw new InvalidOperationException ($"The method {methodInfo} marked with {nameof (JniAddNativeMethodRegistrationAttribute)} must be static");
270+
}
271+
272+
var register = (Action<JniNativeMethodRegistrationArguments>)methodInfo.CreateDelegate (typeof (Action<JniNativeMethodRegistrationArguments>));
273+
register (arguments);
274+
}
275+
nativeClass.RegisterNativeMethods (registrations.ToArray ());
276+
} finally {
277+
if (lockTaken) {
278+
Monitor.Exit (sharedRegistrations);
279+
}
251280
}
252281

253-
var register = (Action<JniType, string>) registerMethod.CreateDelegate (typeof(Action<JniType, string>));
254-
register (nativeClass, methods);
255282
return true;
256283
}
257284
}

src/Java.Interop/Java.Interop/JniType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public bool IsInstanceOfType (JniObjectReference value)
135135
JniNativeMethodRegistration[] methods;
136136
#pragma warning restore 0414
137137

138-
public void RegisterNativeMethods (params JniNativeMethodRegistration[] methods)
138+
internal void RegisterNativeMethods (params JniNativeMethodRegistration[] methods)
139139
{
140140
AssertValid ();
141141

src/Java.Interop/Properties/AssemblyInfo.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@
2727
"814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0" +
2828
"d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b" +
2929
"2c9733db")]
30+
[assembly: InternalsVisibleTo (
31+
"Java.Interop-Tests, PublicKey=" +
32+
"0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf1" +
33+
"6cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2" +
34+
"814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0" +
35+
"d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b" +
36+
"2c9733db")]

0 commit comments

Comments
 (0)