Skip to content

Commit 005c914

Browse files
authored
[Java.Interop] Avoid Type.GetType() in ManagedPeer (#1168)
Fixes: #1165 Context: #1153 Context: #1157 Context: f60906c When building for NativeAOT (#1153) or when building .NET Android apps with `-p:IsAotcompatible=true` (#1157), we get [IL2057][0] warnings from `ManagedPeer.cs`: ManagedPeer.cs(93,19,93,112): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. ManagedPeer.cs(156,18,156,65): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. ManagedPeer.cs(198,35,198,92): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. These warnings are because `ManagedPeer.Construct()` and `ManagedPeer.RegisterNativeMembers()` use `Type.GetType()` on string values provided *from Java code*, and thus the IL trimmer does not have visibility into those strings, and thus cannot reliably determine which types need to be preserved: // Java Callable Wrapper /* partial */ class ManagedType { public static final String __md_methods; static { __md_methods = "n_GetString:()Ljava/lang/String;:__export__\n" + ""; net.dot.jni.ManagedPeer.registerNativeMembers ( /* nativeClass */ ManagedType.class, /* assemblyQualifiedName */ "Example.ManagedType, Hello-NativeAOTFromJNI", /* methods */ __md_methods); } public ManagedType (int p0) { super (); if (getClass () == ManagedType.class) { net.dot.jni.ManagedPeer.construct ( /* self */ this, /* assemblyQualifiedName */ "Example.ManagedType, Hello-NativeAOTFromJNI", /* constructorSignature */ "System.Int32, System.Runtime", /* arguments */ new java.lang.Object[] { p0 }); } } } `ManagedPeer.construct()` passes *two* sets of assembly-qualified type names: `assemblyQualifiedName` contains the type to construct, while `constructorSignature` contains a `:`-separated list of assembly-qualified type names for the constructor parameters. Each of these are passed to `Type.GetType()`. `ManagedPeer.registerNativeMembers()` passes an assembly-qualified type name to `ManagedPeer.RegisterNativeMembers()`, which passes the assembly-qualified type name to `Type.GetType()` to find the type to register native methods for. If we more strongly rely on JNI signatures, we can remove the need for Java Callable Wrappers to contain assembly-qualified type names entirely, thus removing the need for `ManagedPeer` to use `Type.GetType()`, removing the IL2057 warnings. For `ManagedPeer.construct()`, `assemblyQualifiedName` can be replaced with getting the JNI type signature from `self.getClass()`, and `constructorSignature` can be replaced with a *JNI method signature* of the calling constructor. For `ManagedPeer.registerNativeMembers()`, `assemblyQualifiedName` can be replaced with getting the JNI type signature from `nativeClass`. `jcw-gen --codegen-target=JavaInterop1` output becomes: // New JavaInterop1 Java Callable Wrapper /* partial */ class ManagedType { public static final String __md_methods; static { __md_methods = "n_GetString:()Ljava/lang/String;:__export__\n" + ""; net.dot.jni.ManagedPeer.registerNativeMembers ( /* nativeClass */ ManagedType.class, /* methods */ __md_methods); } public ManagedType (int p0) { super (); if (getClass () == ManagedType.class) { net.dot.jni.ManagedPeer.construct ( /* self */ this, /* constructorSignature */ "(I)V", /* arguments */ new java.lang.Object[] { p0 }); } } } This does not alter `jcw-gen --codegen-target=XAJavaInterop1` output; .NET Android will continue to require `Type.GetType()` calls within xamarin/xamarin-android, e.g. [`AndroidTypeManager.RegisterNativeMembers()`][2]. Furthermore, if we add `[DynamicallyAccessedMembers]` to `JniRuntime.JniTypeManager.GetType()`, we can fix some [IL2075][1] warnings which appeared after fixing the IL2057 warnings. Aside: Excising assembly-qualified type names from Java Callable Wrappers had some "interesting" knock-on effects in the unit tests, requiring that more typemap information be explicitly provided. (This same information was *implicitly* provided before, via the provision of assembly-qualified type names everywhere…) One problem with the approach of using JNI signatures instead of using assembly-qualified names is *ambiguity*: there can be multiple managed types which correspond to a given JNI signature. Consider the JNI signature `[I`, which is a Java `int[]`. This is bound as: * C# `int[]` * `JavaArray<int>` * `JavaPrimitiveArray<int>` * `JavaInt32Array` How do we know which to use? Using assembly-qualified type names for constructor parameters nicely solved this issue, but if we're not using them anymore… Update `JavaCallableExample` to demonstrate this: partial class JavaCallableExample { [JavaCallableConstructor(SuperConstructorExpression="")] public JavaCallableExample (int[] a, JavaInt32Array b); } The intention is twofold: 1. This should result in a Java Callable Wrapper constructor with signature `JavaCallableExample(int[] p0, int[] p1)`, and 2. Java code should be able to invoke this constructor. Turns out, neither of these worked when `Type.GetType()` is not used for constructor argument lookup: `JavaCallableWrapperGenerator` didn't fully support e.g. `[JniTypeSignature("I", ArrayRank=1)]` (present on `JavaInt32Array`), so it didn't know what to do with the `JavaInt32Array` parameter. Once (1) was fixed, (2) would fail because `JniRuntime.JniTypeManager.GetType(JniTypeSignature.Parse("[I"))` would return `JavaPrimitiveArray<int>`, which wasn't used in `JavaCallableExample`, resulting in: System.NotSupportedException : Unable to find constructor Java.InteropTests.JavaCallableExample(Java.Interop.JavaPrimitiveArray`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Java.Interop.JavaPrimitiveArray`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]). Please provide the missing constructor. ----> Java.Interop.JniLocationException : Exception of type 'Java.Interop.JniLocationException' was thrown. Stack Trace: at Java.Interop.ManagedPeer.GetConstructor(JniTypeManager typeManager, Type type, String signature, Type[]& parameterTypes) at Java.Interop.ManagedPeer.Construct(IntPtr jnienv, IntPtr klass, IntPtr n_self, IntPtr n_constructorSignature, IntPtr n_constructorArguments) … --- End of managed Java.Interop.JavaException stack trace --- java.lang.Throwable at net.dot.jni.ManagedPeer.construct(Native Method) at net.dot.jni.test.JavaCallableExample.<init>(JavaCallableExample.java:32) at net.dot.jni.test.UseJavaCallableExample.test(UseJavaCallableExample.java:8) The constructor couldn't be found because `JniRuntime.JniTypeManager.GetTypes()` was incomplete, which is a longstanding limitation from f60906c: for `[I`, it would only return `JavaPrimitiveArray<int>` and `int[]`, in that order. Fix both of these. `JniRuntime.JniTypeManager.GetTypes(JniTypeSignature.Parse("[I"))` will now include: * `JavaArray<int>` * `JavaPrimitiveArray<int>` * `JavaInt32Array` * `int[]` This now allows the `JavaCallableExample` constructor to be invoked from Java. Because `ManagedPeer.Construct()` is now doing so much extra work in order to find the `ConstructorInfo` to invoke, cache the lookups. (Technically this is a "memory leak," as cache entries are never removed.) Finally, update `CecilCompilerExpressionVisitor` to emit `newobj` in certain `VisitNew()` invocations. This was needed while trying: partial class JavaCallableExample { [JavaCallable ("getA")] public int[] GetA() => this.a; } in order to fix the IL error: % $HOME/.dotnet/tools/ilverify bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll \ --tokens --system-module System.Private.CoreLib \ -r 'bin/TestDebug-net7.0/*.dll' \ -r '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.10/*.dll' [IL]: Error [StackUnderflow]: […/bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll : .__<$>_jni_marshal_methods::n_GetA(native int, native int)][offset 0x0000002F] Stack underflow. Unfortunately, even after the above fix invalid IL was generated during `jnimarshalmethod-gen` processing, which will be investigated later. [0]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/IL2057 [1]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/il2075 [2]: https://github.com/xamarin/xamarin-android/blob/main/src/Mono.Android/Android.Runtime/AndroidRuntime.cs#L481-L577
1 parent 0f1efeb commit 005c914

File tree

29 files changed

+526
-173
lines changed

29 files changed

+526
-173
lines changed

src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,10 @@ protected override Expression VisitNew (
464464
base.VisitNew (node);
465465
if (node.Constructor == null && node.Type.IsValueType) {
466466
il.Emit (OpCodes.Initobj, assemblyDef.MainModule.ImportReference (node.Type));
467-
} else {
467+
} else if (node.Type.IsValueType) {
468468
il.Emit (OpCodes.Call, assemblyDef.MainModule.ImportReference (node.Constructor));
469+
} else {
470+
il.Emit (OpCodes.Newobj, assemblyDef.MainModule.ImportReference (node.Constructor));
469471
}
470472
return node;
471473
}

src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,12 @@ void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerTy
387387
} else if (v.Name == "GenerateJavaPeer") {
388388
r.DoNotGenerateAcw = ! (bool) v.Argument.Value;
389389
}
390+
var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword");
391+
var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true;
392+
var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank");
393+
if (arrRankProp.Name != null && arrRankProp.Argument.Value is int rank) {
394+
r.Name = new string ('[', rank) + (isKeyword ? r.Name : "L" + r.Name + ";");
395+
}
390396
}
391397
return r;
392398
}
@@ -825,9 +831,7 @@ void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, str
825831
case JavaPeerStyle.JavaInterop1:
826832
sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers (");
827833
sw.Write (self.name);
828-
sw.Write (".class, \"");
829-
sw.Write (managedTypeName);
830-
sw.Write ("\", ");
834+
sw.Write (".class, ");
831835
sw.Write (field);
832836
sw.WriteLine (");");
833837
break;
@@ -1025,9 +1029,7 @@ void GenerateConstructor (Signature ctor, TextWriter sw)
10251029
switch (CodeGenerationTarget) {
10261030
case JavaPeerStyle.JavaInterop1:
10271031
sw.Write ("net.dot.jni.ManagedPeer.construct (this, \"");
1028-
sw.Write (type.GetPartialAssemblyQualifiedName (cache));
1029-
sw.Write ("\", \"");
1030-
sw.Write (ctor.ManagedParameters);
1032+
sw.Write (ctor.JniSignature);
10311033
sw.Write ("\", new java.lang.Object[] { ");
10321034
sw.Write (ctor.ActivateCall);
10331035
sw.WriteLine (" });");

src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public static string ToJniName (string jniType, int rank)
192192
if (rank == 0)
193193
return jniType;
194194

195-
if (jniType.Length > 1)
195+
if (jniType.Length > 1 && jniType [0] != '[')
196196
jniType = "L" + jniType + ";";
197197
return new string ('[', rank) + jniType;
198198
}
@@ -358,7 +358,9 @@ public static int GetArrayInfo (Type type, out Type elementType)
358358
if (pJniName == null) {
359359
return null;
360360
}
361-
return rank == 0 && pJniName.Length > 1 ? "L" + pJniName + ";" : ToJniName (pJniName, rank);
361+
return (rank == 0 && pJniName.Length > 1 && pJniName[0] != '[')
362+
? "L" + pJniName + ";"
363+
: ToJniName (pJniName, rank);
362364
}
363365

364366
static ExportParameterKind GetExportKind (System.Reflection.ICustomAttributeProvider p)
@@ -556,7 +558,15 @@ public static string ToJniName (TypeDefinition type, IMetadataResolver resolver)
556558
var carg = attr.ConstructorArguments.FirstOrDefault ();
557559
if (carg.Type == null || carg.Type.FullName != "System.String")
558560
return null;
559-
return (string) carg.Value;
561+
var jniType = (string) carg.Value;
562+
var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword");
563+
var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true;
564+
var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank");
565+
var arrayRank = arrRankProp.Name != null && arrRankProp.Argument.Value is int rank ? rank : 0;
566+
jniType = arrayRank == 0
567+
? jniType
568+
: new string ('[', arrayRank) + (isKeyword ? jniType : "L" + jniType + ";");
569+
return jniType;
560570
}
561571

562572
static string? ToJniNameFromAttributesForAndroid (TypeDefinition type, IMetadataResolver resolver)

src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#nullable enable
1+
#nullable enable
22

33
using System;
44
using System.Collections.Generic;
@@ -12,50 +12,44 @@
1212
namespace Java.Interop {
1313

1414
partial class JniRuntime {
15-
static JniTypeSignature __BooleanTypeArraySignature;
16-
static JniTypeSignature __SByteTypeArraySignature;
17-
static JniTypeSignature __CharTypeArraySignature;
18-
static JniTypeSignature __Int16TypeArraySignature;
19-
static JniTypeSignature __Int32TypeArraySignature;
20-
static JniTypeSignature __Int64TypeArraySignature;
21-
static JniTypeSignature __SingleTypeArraySignature;
22-
static JniTypeSignature __DoubleTypeArraySignature;
23-
24-
static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature)
25-
{
26-
if (type == typeof (JavaArray<Boolean>) || type == typeof (JavaPrimitiveArray<Boolean>)) {
27-
signature = GetCachedTypeSignature (ref __BooleanTypeArraySignature, "Z", arrayRank: 1, keyword: true);
28-
return true;
29-
}
30-
if (type == typeof (JavaArray<SByte>) || type == typeof (JavaPrimitiveArray<SByte>)) {
31-
signature = GetCachedTypeSignature (ref __SByteTypeArraySignature, "B", arrayRank: 1, keyword: true);
32-
return true;
33-
}
34-
if (type == typeof (JavaArray<Char>) || type == typeof (JavaPrimitiveArray<Char>)) {
35-
signature = GetCachedTypeSignature (ref __CharTypeArraySignature, "C", arrayRank: 1, keyword: true);
36-
return true;
37-
}
38-
if (type == typeof (JavaArray<Int16>) || type == typeof (JavaPrimitiveArray<Int16>)) {
39-
signature = GetCachedTypeSignature (ref __Int16TypeArraySignature, "S", arrayRank: 1, keyword: true);
40-
return true;
41-
}
42-
if (type == typeof (JavaArray<Int32>) || type == typeof (JavaPrimitiveArray<Int32>)) {
43-
signature = GetCachedTypeSignature (ref __Int32TypeArraySignature, "I", arrayRank: 1, keyword: true);
44-
return true;
45-
}
46-
if (type == typeof (JavaArray<Int64>) || type == typeof (JavaPrimitiveArray<Int64>)) {
47-
signature = GetCachedTypeSignature (ref __Int64TypeArraySignature, "J", arrayRank: 1, keyword: true);
48-
return true;
49-
}
50-
if (type == typeof (JavaArray<Single>) || type == typeof (JavaPrimitiveArray<Single>)) {
51-
signature = GetCachedTypeSignature (ref __SingleTypeArraySignature, "F", arrayRank: 1, keyword: true);
52-
return true;
53-
}
54-
if (type == typeof (JavaArray<Double>) || type == typeof (JavaPrimitiveArray<Double>)) {
55-
signature = GetCachedTypeSignature (ref __DoubleTypeArraySignature, "D", arrayRank: 1, keyword: true);
56-
return true;
57-
}
58-
return false;
15+
16+
partial class JniTypeManager {
17+
18+
readonly struct JniPrimitiveArrayInfo {
19+
public readonly JniTypeSignature JniTypeSignature;
20+
public readonly Type PrimitiveType;
21+
public readonly Type[] ArrayTypes;
22+
23+
public JniPrimitiveArrayInfo (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes)
24+
{
25+
JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true);
26+
PrimitiveType = primitiveType;
27+
ArrayTypes = arrayTypes;
28+
}
29+
}
30+
31+
static readonly JniPrimitiveArrayInfo[] JniPrimitiveArrayTypes = new JniPrimitiveArrayInfo[]{
32+
new ("Z", typeof (Boolean), typeof (Boolean[]), typeof (JavaArray<Boolean>), typeof (JavaPrimitiveArray<Boolean>), typeof (JavaBooleanArray)),
33+
new ("B", typeof (SByte), typeof (SByte[]), typeof (JavaArray<SByte>), typeof (JavaPrimitiveArray<SByte>), typeof (JavaSByteArray)),
34+
new ("C", typeof (Char), typeof (Char[]), typeof (JavaArray<Char>), typeof (JavaPrimitiveArray<Char>), typeof (JavaCharArray)),
35+
new ("S", typeof (Int16), typeof (Int16[]), typeof (JavaArray<Int16>), typeof (JavaPrimitiveArray<Int16>), typeof (JavaInt16Array)),
36+
new ("I", typeof (Int32), typeof (Int32[]), typeof (JavaArray<Int32>), typeof (JavaPrimitiveArray<Int32>), typeof (JavaInt32Array)),
37+
new ("J", typeof (Int64), typeof (Int64[]), typeof (JavaArray<Int64>), typeof (JavaPrimitiveArray<Int64>), typeof (JavaInt64Array)),
38+
new ("F", typeof (Single), typeof (Single[]), typeof (JavaArray<Single>), typeof (JavaPrimitiveArray<Single>), typeof (JavaSingleArray)),
39+
new ("D", typeof (Double), typeof (Double[]), typeof (JavaArray<Double>), typeof (JavaPrimitiveArray<Double>), typeof (JavaDoubleArray)),
40+
};
41+
42+
static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature)
43+
{
44+
foreach (var e in JniPrimitiveArrayTypes) {
45+
if (Array.IndexOf (e.ArrayTypes, type) < 0)
46+
continue;
47+
signature = e.JniTypeSignature;
48+
return true;
49+
}
50+
signature = default;
51+
return false;
52+
}
5953
}
6054

6155
static readonly Lazy<KeyValuePair<Type, JniValueMarshaler>[]> JniPrimitiveArrayMarshalers = new Lazy<KeyValuePair<Type, JniValueMarshaler>[]> (InitJniPrimitiveArrayMarshalers);

src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,43 @@ namespace Java.Interop {
2929
};
3030
#>
3131
partial class JniRuntime {
32+
33+
partial class JniTypeManager {
34+
35+
readonly struct JniPrimitiveArrayInfo {
36+
public readonly JniTypeSignature JniTypeSignature;
37+
public readonly Type PrimitiveType;
38+
public readonly Type[] ArrayTypes;
39+
40+
public JniPrimitiveArrayInfo (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes)
41+
{
42+
JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true);
43+
PrimitiveType = primitiveType;
44+
ArrayTypes = arrayTypes;
45+
}
46+
}
47+
48+
static readonly JniPrimitiveArrayInfo[] JniPrimitiveArrayTypes = new JniPrimitiveArrayInfo[]{
3249
<#
3350
foreach (var type in arrayTypeInfo) {
3451
#>
35-
static JniTypeSignature __<#= type.ManagedType #>TypeArraySignature;
52+
new ("<#= type.JniType #>", typeof (<#= type.ManagedType #>), typeof (<#= type.ManagedType #>[]), typeof (JavaArray<<#= type.ManagedType #>>), typeof (JavaPrimitiveArray<<#= type.ManagedType #>>), typeof (Java<#= type.ManagedType #>Array)),
3653
<#
3754
}
3855
#>
56+
};
3957

40-
static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature)
41-
{
42-
<#
43-
foreach (var info in arrayTypeInfo) {
44-
#>
45-
if (type == typeof (JavaArray<<#= info.ManagedType #>>) || type == typeof (JavaPrimitiveArray<<#= info.ManagedType #>>)) {
46-
signature = GetCachedTypeSignature (ref __<#= info.ManagedType #>TypeArraySignature, "<#= info.JniType #>", arrayRank: 1, keyword: true);
47-
return true;
58+
static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature)
59+
{
60+
foreach (var e in JniPrimitiveArrayTypes) {
61+
if (Array.IndexOf (e.ArrayTypes, type) < 0)
62+
continue;
63+
signature = e.JniTypeSignature;
64+
return true;
65+
}
66+
signature = default;
67+
return false;
4868
}
49-
<#
50-
}
51-
#>
52-
return false;
5369
}
5470

5571
static readonly Lazy<KeyValuePair<Type, JniValueMarshaler>[]> JniPrimitiveArrayMarshalers = new Lazy<KeyValuePair<Type, JniValueMarshaler>[]> (InitJniPrimitiveArrayMarshalers);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Diagnostics.CodeAnalysis;
55
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
67

78
namespace Java.Interop {
89

@@ -72,6 +73,7 @@ public override bool Equals (object? obj)
7273
}
7374

7475
// TODO: Keep in sync with the code generated by ExportedMemberBuilder
76+
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
7577
delegate bool EqualsMarshalMethod (IntPtr jnienv, IntPtr n_self, IntPtr n_value);
7678
static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value)
7779
{
@@ -92,6 +94,7 @@ static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value)
9294
}
9395

9496
// TODO: Keep in sync with the code generated by ExportedMemberBuilder
97+
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
9598
delegate int GetHashCodeMarshalMethod (IntPtr jnienv, IntPtr n_self);
9699
static int GetHashCode (IntPtr jnienv, IntPtr n_self)
97100
{
@@ -109,6 +112,7 @@ static int GetHashCode (IntPtr jnienv, IntPtr n_self)
109112
}
110113
}
111114

115+
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
112116
delegate IntPtr ToStringMarshalMethod (IntPtr jnienv, IntPtr n_self);
113117
static IntPtr ToString (IntPtr jnienv, IntPtr n_self)
114118
{

src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ static Dictionary<string, Type> InitJniBuiltinSimpleReferenceToType ()
130130
{
131131
return new Dictionary<string, Type> (StringComparer.Ordinal) {
132132
{"java/lang/String", typeof (string)},
133+
{"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)},
134+
{"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)},
135+
{"net/dot/jni/ManagedPeer", typeof (ManagedPeer)},
133136
{"V", typeof (void)},
134137
{"Z", typeof (Boolean)},
135138
{"java/lang/Boolean", typeof (Boolean?)},
@@ -156,6 +159,7 @@ static KeyValuePair<Type, JniValueMarshaler>[] InitJniBuiltinMarshalers ()
156159
{
157160
return new []{
158161
new KeyValuePair<Type, JniValueMarshaler>(typeof (string), JniStringValueMarshaler.Instance),
162+
new KeyValuePair<Type, JniValueMarshaler>(typeof (JavaProxyObject), ProxyValueMarshaler.Instance),
159163
new KeyValuePair<Type, JniValueMarshaler>(typeof (Boolean), JniBooleanValueMarshaler.Instance),
160164
new KeyValuePair<Type, JniValueMarshaler>(typeof (Boolean?), JniNullableBooleanValueMarshaler.Instance),
161165
new KeyValuePair<Type, JniValueMarshaler>(typeof (SByte), JniSByteValueMarshaler.Instance),

src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ namespace Java.Interop {
101101
{
102102
return new Dictionary<string, Type> (StringComparer.Ordinal) {
103103
{"java/lang/String", typeof (string)},
104+
{"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)},
105+
{"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)},
106+
{"net/dot/jni/ManagedPeer", typeof (ManagedPeer)},
104107
{"V", typeof (void)},
105108
<#
106109
foreach (var type in types) {
@@ -119,6 +122,7 @@ namespace Java.Interop {
119122
{
120123
return new []{
121124
new KeyValuePair<Type, JniValueMarshaler>(typeof (string), JniStringValueMarshaler.Instance),
125+
new KeyValuePair<Type, JniValueMarshaler>(typeof (JavaProxyObject), ProxyValueMarshaler.Instance),
122126
<#
123127
foreach (var type in types) {
124128
#>

src/Java.Interop/Java.Interop/JniMemberSignature.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ public JniMemberSignature (string memberName, string memberSignature)
3434
this.memberSignature = memberSignature;
3535
}
3636

37+
internal static IEnumerable<JniTypeSignature> GetParameterTypesFromMethodSignature (string jniMethodSignature)
38+
{
39+
if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) {
40+
throw new ArgumentException (
41+
$"Member signature `{jniMethodSignature}` is not a method signature. Method signatures must start with `(`.",
42+
nameof (jniMethodSignature));
43+
}
44+
int index = 1;
45+
while (index < jniMethodSignature.Length &&
46+
jniMethodSignature [index] != ')') {
47+
var (start, length) = ExtractType (jniMethodSignature, ref index);
48+
var jniType = jniMethodSignature.Substring (start, length);
49+
yield return JniTypeSignature.Parse (jniType);
50+
}
51+
}
52+
3753
public static int GetParameterCountFromMethodSignature (string jniMethodSignature)
3854
{
3955
if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) {

src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,42 @@ internal JniInstanceMethods GetConstructorsForType (Type declaringType)
7272
if (declaringType == DeclaringType)
7373
return this;
7474

75+
JniInstanceMethods? methods;
76+
7577
lock (SubclassConstructors) {
76-
if (!SubclassConstructors.TryGetValue (declaringType, out var methods)) {
77-
methods = new JniInstanceMethods (declaringType);
78-
SubclassConstructors.Add (declaringType, methods);
79-
}
78+
if (SubclassConstructors.TryGetValue (declaringType, out methods))
79+
return methods;
80+
}
81+
// Init outside of `lock` in case we have recursive access:
82+
// System.ArgumentException: An item with the same key has already been added. Key: Java.Interop.JavaProxyThrowable
83+
// at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
84+
// at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
85+
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 80
86+
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 80
87+
// at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 146
88+
// at Java.Interop.JavaException..ctor(String message) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaException.cs:line 52
89+
// at Java.Interop.JavaProxyThrowable..ctor(Exception exception) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs:line 15
90+
// at Java.Interop.JniEnvironment.Exceptions.Throw(Exception e) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Errors.cs:line 39
91+
// at Java.Interop.JniRuntime.RaisePendingException(Exception pendingException) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniRuntime.cs:line 444
92+
// at Java.Interop.JniTransition.Dispose() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniTransition.cs:line 39
93+
// at Java.Interop.ManagedPeer.RegisterNativeMembers(IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, IntPtr n_methods) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/ManagedPeer.cs:line 195
94+
// at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname)
95+
// at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname)
96+
// at Java.Interop.JniEnvironment.Types.TryRawFindClass(IntPtr env, String classname, IntPtr& klass, IntPtr& thrown) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 135
97+
// at Java.Interop.JniEnvironment.Types.TryFindClass(String classname, Boolean throwOnError) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 49
98+
// at Java.Interop.JniEnvironment.Types.FindClass(String classname) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 37
99+
// at Java.Interop.JniType..ctor(String classname) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniType.cs:line 51
100+
// at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 27
101+
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
102+
// at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 146
103+
// at Java.Lang.Object..ctor() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Base/obj/Debug-net7.0/mcw/Java.Lang.Object.cs:line 32
104+
// at Java.BaseTests.MyIntConsumer..ctor(Action`1 action) in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs:line 77
105+
// at Java.BaseTests.JavaToManagedTests.InterfaceInvokerMethod() in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs:line 26
106+
methods = new JniInstanceMethods (declaringType);
107+
lock (SubclassConstructors) {
108+
if (SubclassConstructors.TryGetValue (declaringType, out var m))
109+
return m;
110+
SubclassConstructors.Add (declaringType, methods);
80111
return methods;
81112
}
82113
}

0 commit comments

Comments
 (0)