From 005c914170a0af9069ff18fd4dd9d45463dd5dc6 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Sat, 2 Dec 2023 10:16:48 -0500 Subject: [PATCH] [Java.Interop] Avoid `Type.GetType()` in `ManagedPeer` (#1168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/java.interop/issues/1165 Context: https://github.com/xamarin/java.interop/pull/1153 Context: https://github.com/xamarin/java.interop/issues/1157 Context: f60906cf717c3aa305454040d6faf6ec2e041e34 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` * `JavaPrimitiveArray` * `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`, 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.(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 f60906cf: for `[I`, it would only return `JavaPrimitiveArray` and `int[]`, in that order. Fix both of these. `JniRuntime.JniTypeManager.GetTypes(JniTypeSignature.Parse("[I"))` will now include: * `JavaArray` * `JavaPrimitiveArray` * `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 --- .../CecilCompilerExpressionVisitor.cs | 4 +- .../JavaCallableWrapperGenerator.cs | 14 +- .../JavaNativeTypeManager.cs | 16 +- .../Java.Interop/JavaPrimitiveArrays.cs | 84 +++++---- .../Java.Interop/JavaPrimitiveArrays.tt | 42 +++-- .../Java.Interop/JavaProxyObject.cs | 4 + .../Java.Interop/JniBuiltinMarshalers.cs | 4 + .../Java.Interop/JniBuiltinMarshalers.tt | 4 + .../Java.Interop/JniMemberSignature.cs | 16 ++ .../JniPeerMembers.JniInstanceMethods.cs | 39 ++++- .../Java.Interop/JniRuntime.JniTypeManager.cs | 43 ++++- src/Java.Interop/Java.Interop/ManagedPeer.cs | 159 +++++++++++++----- .../java/net/dot/jni/ManagedPeer.java | 2 - .../net/dot/jni/internal/JavaProxyObject.java | 2 - .../dot/jni/internal/JavaProxyThrowable.java | 2 - .../Java.Base/JavaVMFixture.cs | 1 + .../Java.Interop/JavaPrimitiveArraysTiming.cs | 129 ++++++++++++++ .../Java.Interop/JavaVMFixture.cs | 3 + .../Java.Interop/JniTypeManagerTests.cs | 68 +++++--- .../CallVirtualFromConstructorDerived.java | 5 +- .../java/net/dot/jni/test/GetThis.java | 5 +- .../java/net/dot/jni/test/TestType.java | 8 +- .../Java.Interop/ExportTest.cs | 4 +- .../Java.Interop/JavaCallableExample.cs | 23 ++- .../Java.Interop/JavaCallableExampleTests.cs | 2 +- .../Java.Interop/JavaVMFixture.cs | 6 +- .../java/net/dot/jni/test/ExportType.java | 2 - .../dot/jni/test/UseJavaCallableExample.java | 4 +- .../JavaCallableWrapperGeneratorTests.cs | 4 +- 29 files changed, 526 insertions(+), 173 deletions(-) create mode 100644 tests/Java.Interop-PerformanceTests/Java.Interop/JavaPrimitiveArraysTiming.cs diff --git a/src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs b/src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs index 19dc12a81..cbfdc6756 100644 --- a/src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs +++ b/src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs @@ -464,8 +464,10 @@ protected override Expression VisitNew ( base.VisitNew (node); if (node.Constructor == null && node.Type.IsValueType) { il.Emit (OpCodes.Initobj, assemblyDef.MainModule.ImportReference (node.Type)); - } else { + } else if (node.Type.IsValueType) { il.Emit (OpCodes.Call, assemblyDef.MainModule.ImportReference (node.Constructor)); + } else { + il.Emit (OpCodes.Newobj, assemblyDef.MainModule.ImportReference (node.Constructor)); } return node; } diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs index 59d434ed0..081481c47 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs @@ -387,6 +387,12 @@ void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerTy } else if (v.Name == "GenerateJavaPeer") { r.DoNotGenerateAcw = ! (bool) v.Argument.Value; } + var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword"); + var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true; + var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank"); + if (arrRankProp.Name != null && arrRankProp.Argument.Value is int rank) { + r.Name = new string ('[', rank) + (isKeyword ? r.Name : "L" + r.Name + ";"); + } } return r; } @@ -825,9 +831,7 @@ void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, str case JavaPeerStyle.JavaInterop1: sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers ("); sw.Write (self.name); - sw.Write (".class, \""); - sw.Write (managedTypeName); - sw.Write ("\", "); + sw.Write (".class, "); sw.Write (field); sw.WriteLine (");"); break; @@ -1025,9 +1029,7 @@ void GenerateConstructor (Signature ctor, TextWriter sw) switch (CodeGenerationTarget) { case JavaPeerStyle.JavaInterop1: sw.Write ("net.dot.jni.ManagedPeer.construct (this, \""); - sw.Write (type.GetPartialAssemblyQualifiedName (cache)); - sw.Write ("\", \""); - sw.Write (ctor.ManagedParameters); + sw.Write (ctor.JniSignature); sw.Write ("\", new java.lang.Object[] { "); sw.Write (ctor.ActivateCall); sw.WriteLine (" });"); diff --git a/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs b/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs index 176e478a4..5531acc50 100644 --- a/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs +++ b/src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs @@ -192,7 +192,7 @@ public static string ToJniName (string jniType, int rank) if (rank == 0) return jniType; - if (jniType.Length > 1) + if (jniType.Length > 1 && jniType [0] != '[') jniType = "L" + jniType + ";"; return new string ('[', rank) + jniType; } @@ -358,7 +358,9 @@ public static int GetArrayInfo (Type type, out Type elementType) if (pJniName == null) { return null; } - return rank == 0 && pJniName.Length > 1 ? "L" + pJniName + ";" : ToJniName (pJniName, rank); + return (rank == 0 && pJniName.Length > 1 && pJniName[0] != '[') + ? "L" + pJniName + ";" + : ToJniName (pJniName, rank); } static ExportParameterKind GetExportKind (System.Reflection.ICustomAttributeProvider p) @@ -556,7 +558,15 @@ public static string ToJniName (TypeDefinition type, IMetadataResolver resolver) var carg = attr.ConstructorArguments.FirstOrDefault (); if (carg.Type == null || carg.Type.FullName != "System.String") return null; - return (string) carg.Value; + var jniType = (string) carg.Value; + var isKeyProp = attr.Properties.FirstOrDefault (p => p.Name == "IsKeyword"); + var isKeyword = isKeyProp.Name != null && ((bool) isKeyProp.Argument.Value) == true; + var arrRankProp = attr.Properties.FirstOrDefault (p => p.Name == "ArrayRank"); + var arrayRank = arrRankProp.Name != null && arrRankProp.Argument.Value is int rank ? rank : 0; + jniType = arrayRank == 0 + ? jniType + : new string ('[', arrayRank) + (isKeyword ? jniType : "L" + jniType + ";"); + return jniType; } static string? ToJniNameFromAttributesForAndroid (TypeDefinition type, IMetadataResolver resolver) diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs index 06bb10f3a..10a41623a 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System; using System.Collections.Generic; @@ -12,50 +12,44 @@ namespace Java.Interop { partial class JniRuntime { - static JniTypeSignature __BooleanTypeArraySignature; - static JniTypeSignature __SByteTypeArraySignature; - static JniTypeSignature __CharTypeArraySignature; - static JniTypeSignature __Int16TypeArraySignature; - static JniTypeSignature __Int32TypeArraySignature; - static JniTypeSignature __Int64TypeArraySignature; - static JniTypeSignature __SingleTypeArraySignature; - static JniTypeSignature __DoubleTypeArraySignature; - - static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature) - { - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __BooleanTypeArraySignature, "Z", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __SByteTypeArraySignature, "B", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __CharTypeArraySignature, "C", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __Int16TypeArraySignature, "S", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __Int32TypeArraySignature, "I", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __Int64TypeArraySignature, "J", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __SingleTypeArraySignature, "F", arrayRank: 1, keyword: true); - return true; - } - if (type == typeof (JavaArray) || type == typeof (JavaPrimitiveArray)) { - signature = GetCachedTypeSignature (ref __DoubleTypeArraySignature, "D", arrayRank: 1, keyword: true); - return true; - } - return false; + + partial class JniTypeManager { + + readonly struct JniPrimitiveArrayInfo { + public readonly JniTypeSignature JniTypeSignature; + public readonly Type PrimitiveType; + public readonly Type[] ArrayTypes; + + public JniPrimitiveArrayInfo (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes) + { + JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true); + PrimitiveType = primitiveType; + ArrayTypes = arrayTypes; + } + } + + static readonly JniPrimitiveArrayInfo[] JniPrimitiveArrayTypes = new JniPrimitiveArrayInfo[]{ + new ("Z", typeof (Boolean), typeof (Boolean[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaBooleanArray)), + new ("B", typeof (SByte), typeof (SByte[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSByteArray)), + new ("C", typeof (Char), typeof (Char[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaCharArray)), + new ("S", typeof (Int16), typeof (Int16[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt16Array)), + new ("I", typeof (Int32), typeof (Int32[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt32Array)), + new ("J", typeof (Int64), typeof (Int64[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt64Array)), + new ("F", typeof (Single), typeof (Single[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSingleArray)), + new ("D", typeof (Double), typeof (Double[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaDoubleArray)), + }; + + static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature) + { + foreach (var e in JniPrimitiveArrayTypes) { + if (Array.IndexOf (e.ArrayTypes, type) < 0) + continue; + signature = e.JniTypeSignature; + return true; + } + signature = default; + return false; + } } static readonly Lazy[]> JniPrimitiveArrayMarshalers = new Lazy[]> (InitJniPrimitiveArrayMarshalers); diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt index d5599c35d..83a3aa160 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt @@ -29,27 +29,43 @@ namespace Java.Interop { }; #> partial class JniRuntime { + + partial class JniTypeManager { + + readonly struct JniPrimitiveArrayInfo { + public readonly JniTypeSignature JniTypeSignature; + public readonly Type PrimitiveType; + public readonly Type[] ArrayTypes; + + public JniPrimitiveArrayInfo (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes) + { + JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true); + PrimitiveType = primitiveType; + ArrayTypes = arrayTypes; + } + } + + static readonly JniPrimitiveArrayInfo[] JniPrimitiveArrayTypes = new JniPrimitiveArrayInfo[]{ <# foreach (var type in arrayTypeInfo) { #> - static JniTypeSignature __<#= type.ManagedType #>TypeArraySignature; + new ("<#= type.JniType #>", typeof (<#= type.ManagedType #>), typeof (<#= type.ManagedType #>[]), typeof (JavaArray<<#= type.ManagedType #>>), typeof (JavaPrimitiveArray<<#= type.ManagedType #>>), typeof (Java<#= type.ManagedType #>Array)), <# } #> + }; - static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature) - { -<# - foreach (var info in arrayTypeInfo) { -#> - if (type == typeof (JavaArray<<#= info.ManagedType #>>) || type == typeof (JavaPrimitiveArray<<#= info.ManagedType #>>)) { - signature = GetCachedTypeSignature (ref __<#= info.ManagedType #>TypeArraySignature, "<#= info.JniType #>", arrayRank: 1, keyword: true); - return true; + static bool GetBuiltInTypeArraySignature (Type type, ref JniTypeSignature signature) + { + foreach (var e in JniPrimitiveArrayTypes) { + if (Array.IndexOf (e.ArrayTypes, type) < 0) + continue; + signature = e.JniTypeSignature; + return true; + } + signature = default; + return false; } -<# - } -#> - return false; } static readonly Lazy[]> JniPrimitiveArrayMarshalers = new Lazy[]> (InitJniPrimitiveArrayMarshalers); diff --git a/src/Java.Interop/Java.Interop/JavaProxyObject.cs b/src/Java.Interop/Java.Interop/JavaProxyObject.cs index 00f0ff86d..28f48ef7a 100644 --- a/src/Java.Interop/Java.Interop/JavaProxyObject.cs +++ b/src/Java.Interop/Java.Interop/JavaProxyObject.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Java.Interop { @@ -72,6 +73,7 @@ public override bool Equals (object? obj) } // TODO: Keep in sync with the code generated by ExportedMemberBuilder + [UnmanagedFunctionPointer (CallingConvention.Winapi)] delegate bool EqualsMarshalMethod (IntPtr jnienv, IntPtr n_self, IntPtr n_value); static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value) { @@ -92,6 +94,7 @@ static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value) } // TODO: Keep in sync with the code generated by ExportedMemberBuilder + [UnmanagedFunctionPointer (CallingConvention.Winapi)] delegate int GetHashCodeMarshalMethod (IntPtr jnienv, IntPtr n_self); static int GetHashCode (IntPtr jnienv, IntPtr n_self) { @@ -109,6 +112,7 @@ static int GetHashCode (IntPtr jnienv, IntPtr n_self) } } + [UnmanagedFunctionPointer (CallingConvention.Winapi)] delegate IntPtr ToStringMarshalMethod (IntPtr jnienv, IntPtr n_self); static IntPtr ToString (IntPtr jnienv, IntPtr n_self) { diff --git a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs index fd6b78712..b75e46b6d 100644 --- a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs +++ b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs @@ -130,6 +130,9 @@ static Dictionary InitJniBuiltinSimpleReferenceToType () { return new Dictionary (StringComparer.Ordinal) { {"java/lang/String", typeof (string)}, + {"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)}, + {"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)}, + {"net/dot/jni/ManagedPeer", typeof (ManagedPeer)}, {"V", typeof (void)}, {"Z", typeof (Boolean)}, {"java/lang/Boolean", typeof (Boolean?)}, @@ -156,6 +159,7 @@ static KeyValuePair[] InitJniBuiltinMarshalers () { return new []{ new KeyValuePair(typeof (string), JniStringValueMarshaler.Instance), + new KeyValuePair(typeof (JavaProxyObject), ProxyValueMarshaler.Instance), new KeyValuePair(typeof (Boolean), JniBooleanValueMarshaler.Instance), new KeyValuePair(typeof (Boolean?), JniNullableBooleanValueMarshaler.Instance), new KeyValuePair(typeof (SByte), JniSByteValueMarshaler.Instance), diff --git a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt index a3cf69968..d5910cf62 100644 --- a/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt +++ b/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt @@ -101,6 +101,9 @@ namespace Java.Interop { { return new Dictionary (StringComparer.Ordinal) { {"java/lang/String", typeof (string)}, + {"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)}, + {"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)}, + {"net/dot/jni/ManagedPeer", typeof (ManagedPeer)}, {"V", typeof (void)}, <# foreach (var type in types) { @@ -119,6 +122,7 @@ namespace Java.Interop { { return new []{ new KeyValuePair(typeof (string), JniStringValueMarshaler.Instance), + new KeyValuePair(typeof (JavaProxyObject), ProxyValueMarshaler.Instance), <# foreach (var type in types) { #> diff --git a/src/Java.Interop/Java.Interop/JniMemberSignature.cs b/src/Java.Interop/Java.Interop/JniMemberSignature.cs index 946cf7bb5..30ba2b4c5 100644 --- a/src/Java.Interop/Java.Interop/JniMemberSignature.cs +++ b/src/Java.Interop/Java.Interop/JniMemberSignature.cs @@ -34,6 +34,22 @@ public JniMemberSignature (string memberName, string memberSignature) this.memberSignature = memberSignature; } + internal static IEnumerable GetParameterTypesFromMethodSignature (string jniMethodSignature) + { + if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) { + throw new ArgumentException ( + $"Member signature `{jniMethodSignature}` is not a method signature. Method signatures must start with `(`.", + nameof (jniMethodSignature)); + } + int index = 1; + while (index < jniMethodSignature.Length && + jniMethodSignature [index] != ')') { + var (start, length) = ExtractType (jniMethodSignature, ref index); + var jniType = jniMethodSignature.Substring (start, length); + yield return JniTypeSignature.Parse (jniType); + } + } + public static int GetParameterCountFromMethodSignature (string jniMethodSignature) { if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) { diff --git a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs index cee7af696..04d96870e 100644 --- a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs +++ b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs @@ -72,11 +72,42 @@ internal JniInstanceMethods GetConstructorsForType (Type declaringType) if (declaringType == DeclaringType) return this; + JniInstanceMethods? methods; + lock (SubclassConstructors) { - if (!SubclassConstructors.TryGetValue (declaringType, out var methods)) { - methods = new JniInstanceMethods (declaringType); - SubclassConstructors.Add (declaringType, methods); - } + if (SubclassConstructors.TryGetValue (declaringType, out methods)) + return methods; + } + // Init outside of `lock` in case we have recursive access: + // System.ArgumentException: An item with the same key has already been added. Key: Java.Interop.JavaProxyThrowable + // at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) + // at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) + // 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 + // 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 + // 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 + // at Java.Interop.JavaException..ctor(String message) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaException.cs:line 52 + // at Java.Interop.JavaProxyThrowable..ctor(Exception exception) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs:line 15 + // 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 + // at Java.Interop.JniRuntime.RaisePendingException(Exception pendingException) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniRuntime.cs:line 444 + // at Java.Interop.JniTransition.Dispose() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniTransition.cs:line 39 + // 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 + // at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname) + // at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname) + // 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 + // 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 + // 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 + // at Java.Interop.JniType..ctor(String classname) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniType.cs:line 51 + // 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 + // 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 + // 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 + // 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 + // 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 + // at Java.BaseTests.JavaToManagedTests.InterfaceInvokerMethod() in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs:line 26 + methods = new JniInstanceMethods (declaringType); + lock (SubclassConstructors) { + if (SubclassConstructors.TryGetValue (declaringType, out var m)) + return m; + SubclassConstructors.Add (declaringType, methods); return methods; } } diff --git a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs index e87e9b129..6251e48ee 100644 --- a/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs +++ b/src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs @@ -79,7 +79,7 @@ public override string ToString () } #endif // NET - public class JniTypeManager : IDisposable, ISetRuntime { + public partial class JniTypeManager : IDisposable, ISetRuntime { JniRuntime? runtime; bool disposed; @@ -267,6 +267,7 @@ protected virtual IEnumerable GetSimpleReferences (Type type) static readonly Type[] EmptyTypeArray = Array.Empty (); + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public Type? GetType (JniTypeSignature typeSignature) { AssertValid (); @@ -293,13 +294,16 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) continue; } + if (typeSignature.IsKeyword) { + foreach (var t in GetPrimitiveArrayTypesForSimpleReference (typeSignature, type)) { + yield return t; + } + continue; + } + if (typeSignature.ArrayRank > 0) { var rank = typeSignature.ArrayRank; var arrayType = type; - if (typeSignature.IsKeyword) { - arrayType = typeof (JavaPrimitiveArray<>).MakeGenericType (arrayType); - rank--; - } while (rank-- > 0) { arrayType = typeof (JavaObjectArray<>).MakeGenericType (arrayType); } @@ -317,6 +321,35 @@ IEnumerable CreateGetTypesEnumerator (JniTypeSignature typeSignature) } } + IEnumerable GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typeSignature, Type type) + { + int index = -1; + for (int i = 0; i < JniPrimitiveArrayTypes.Length; ++i) { + if (JniPrimitiveArrayTypes [i].PrimitiveType == type) { + index = i; + break; + } + } + if (index == -1) { + throw new InvalidOperationException ($"Should not be reached; Could not find JniPrimitiveArrayInfo for {type}"); + } + foreach (var t in JniPrimitiveArrayTypes [index].ArrayTypes) { + var rank = typeSignature.ArrayRank-1; + var arrayType = t; + while (rank-- > 0) { + arrayType = typeof (JavaObjectArray<>).MakeGenericType (arrayType); + } + yield return arrayType; + + rank = typeSignature.ArrayRank-1; + arrayType = t; + while (rank-- > 0) { + arrayType = arrayType.MakeArrayType (); + } + yield return arrayType; + } + } + protected virtual IEnumerable GetTypesForSimpleReference (string jniSimpleReference) { AssertValid (); diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 7df4134f8..2757d2cdc 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -1,7 +1,9 @@ #nullable enable using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -47,20 +49,18 @@ public override JniPeerMembers JniPeerMembers { get {return _members;} } - const string ConstructSignature = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V"; + const string ConstructSignature = "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V"; // TODO: Keep in sync with the code generated by ExportedMemberBuilder delegate void ConstructMarshalMethod (IntPtr jnienv, IntPtr klass, IntPtr n_self, - IntPtr n_assemblyQualifiedName, IntPtr n_constructorSignature, IntPtr n_constructorArguments); static void Construct ( IntPtr jnienv, IntPtr klass, IntPtr n_self, - IntPtr n_assemblyQualifiedName, IntPtr n_constructorSignature, IntPtr n_constructorArguments) { @@ -90,19 +90,19 @@ static void Construct ( return; } - var type = Type.GetType (JniEnvironment.Strings.ToString (n_assemblyQualifiedName)!, throwOnError: true)!; + var typeSig = new JniTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (r_self)); + var type = GetTypeFromSignature (runtime.TypeManager, typeSig); + if (type.IsGenericTypeDefinition) { throw new NotSupportedException ( "Constructing instances of generic types from Java is not supported, as the type parameters cannot be determined.", CreateJniLocationException ()); } - var ptypes = GetParameterTypes (JniEnvironment.Strings.ToString (n_constructorSignature)); - var pvalues = GetValues (runtime, new JniObjectReference (n_constructorArguments), ptypes); - var cinfo = type.GetConstructor (ptypes); - if (cinfo == null) { - throw CreateMissingConstructorException (type, ptypes); - } + var ctorSig = JniEnvironment.Strings.ToString (n_constructorSignature) ?? "()V"; + var cinfo = GetConstructor (type, typeSig.SimpleReference!, ctorSig) ?? + throw CreateMissingConstructorException (type, ctorSig); + var pvalues = GetValues (runtime, new JniObjectReference (n_constructorArguments), cinfo); if (self != null) { cinfo.Invoke (self, pvalues); @@ -119,26 +119,18 @@ static void Construct ( } } - static Exception CreateMissingConstructorException (Type type, Type [] ptypes) + static Exception CreateMissingConstructorException (Type type, string signature) { var message = new StringBuilder (); - message.Append ("Unable to find constructor "); + message.Append ("Unable to find constructor for type `"); message.Append (type.FullName); - message.Append ("("); - - if (ptypes.Length > 0) { - message.Append (ptypes [0].FullName); - for (int i = 1; i < ptypes.Length; ++i) - message.Append (", ").Append (ptypes [i].FullName); - } - - message.Append (")"); - message.Append (". Please provide the missing constructor."); + message.Append ("` with JNI signature `"); + message.Append (signature); + message.Append ("`. Please provide the missing constructor."); return new NotSupportedException (message.ToString (), CreateJniLocationException ()); } - static Exception CreateJniLocationException () { using (var e = new JavaException ()) { @@ -146,60 +138,134 @@ static Exception CreateJniLocationException () } } - static Type[] GetParameterTypes (string? signature) + static Dictionary ConstructorCache = new Dictionary (); + + static ConstructorInfo? GetConstructor (Type type, string jniTypeName, string signature) { - if (string.IsNullOrEmpty (signature)) - return Array.Empty (); - var typeNames = signature!.Split (':'); - var ptypes = new Type [typeNames.Length]; - for (int i = 0; i < typeNames.Length; i++) - ptypes [i] = Type.GetType (typeNames [i], throwOnError:true)!; - return ptypes; + var ctorCacheKey = jniTypeName + "." + signature; + lock (ConstructorCache) { + if (ConstructorCache.TryGetValue (ctorCacheKey, out var ctor)) { + return ctor; + } + } + + var candidateParameterTypes = GetConstructorCandidateParameterTypes (signature); + if (candidateParameterTypes.Length == 0) { + return CacheConstructor (ctorCacheKey, type.GetConstructor (Array.Empty ())); + } + + var constructors = new List(type.GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)); + + // Filter out wrong parameter count + for (int c = constructors.Count; c > 0; --c) { + if (constructors [c-1].GetParameters ().Length != candidateParameterTypes.Length) { + constructors.RemoveAt (c-1); + } + } + + // Filter out mismatched types + for (int c = constructors.Count; c > 0; --c) { + var parameters = constructors [c-1].GetParameters (); + for (int i = 0; i < parameters.Length; ++i) { + if (!candidateParameterTypes [i].Contains (parameters [i].ParameterType)) { + constructors.RemoveAt (c-1); + break; + } + } + } + + if (constructors.Count == 0) + return CacheConstructor (ctorCacheKey, null); + + if (constructors.Count != 1) { + var message = new StringBuilder ($"Found {constructors.Count} constructors matching JNI signature {signature}:") + .Append (Environment.NewLine); + foreach (var c in constructors) { + message.Append (" ").Append (c).Append (Environment.NewLine); + } + throw new NotSupportedException (message.ToString (), CreateJniLocationException ()); + } + + return CacheConstructor (ctorCacheKey, constructors [0]); } - static object?[]? GetValues (JniRuntime runtime, JniObjectReference values, Type[] types) + static ConstructorInfo? CacheConstructor (string cacheKey, ConstructorInfo? ctor) + { + lock (ConstructorCache) { + if (ConstructorCache.TryGetValue (cacheKey, out var existing)) { + return existing; + } + ConstructorCache.Add (cacheKey, ctor); + } + return ctor; + } + + static List[] GetConstructorCandidateParameterTypes (string signature) + { + var parameterCount = JniMemberSignature.GetParameterCountFromMethodSignature (signature); + if (parameterCount == 0) { + return Array.Empty> (); + } + var typeManager = JniEnvironment.Runtime.TypeManager; + var candidateParameterTypes = new List[parameterCount]; + int i = 0; + foreach (var jniType in JniMemberSignature.GetParameterTypesFromMethodSignature (signature)) { + var possibleTypes = new List (typeManager.GetTypes (jniType)); + if (possibleTypes.Count == 0) { + throw new NotSupportedException ( + $"Could not find System.Type corresponding to Java type `{jniType}` within constructor signature `{signature}`.", + CreateJniLocationException ()); + } + candidateParameterTypes [i++] = possibleTypes; + } + return candidateParameterTypes; + } + + static object?[]? GetValues (JniRuntime runtime, JniObjectReference values, ConstructorInfo cinfo) { if (!values.IsValid) return null; + var parameters = cinfo.GetParameters (); + int len = JniEnvironment.Arrays.GetArrayLength (values); - Debug.Assert (len == types.Length, - string.Format ("Unexpected number of parameter types! Expected {0}, got {1}", types.Length, len)); - var pvalues = new object? [types.Length]; - for (int i = 0; i < pvalues.Length; ++i) { + Debug.Assert (len == parameters.Length, + $"Unexpected number of parameter types! Expected {parameters.Length}, got {len}"); + var pvalues = new object? [len]; + for (int i = 0; i < len; ++i) { var n_value = JniEnvironment.Arrays.GetObjectArrayElement (values, i); - var value = runtime.ValueManager.GetValue (ref n_value, JniObjectReferenceOptions.CopyAndDispose, types [i]); + var value = runtime.ValueManager.GetValue (ref n_value, JniObjectReferenceOptions.CopyAndDispose, parameters [i].ParameterType); pvalues [i] = value; } return pvalues; } - const string RegisterNativeMembersSignature = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V"; + const string RegisterNativeMembersSignature = "(Ljava/lang/Class;Ljava/lang/String;)V"; delegate void RegisterMarshalMethod (IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, - IntPtr n_assemblyQualifiedName, IntPtr n_methods); static unsafe void RegisterNativeMembers ( IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, - IntPtr n_assemblyQualifiedName, IntPtr n_methods) { var envp = new JniTransition (jnienv); try { var r_nativeClass = new JniObjectReference (n_nativeClass); +#pragma warning disable CA2000 var nativeClass = new JniType (ref r_nativeClass, JniObjectReferenceOptions.Copy); +#pragma warning restore CA2000 - var assemblyQualifiedName = JniEnvironment.Strings.ToString (new JniObjectReference (n_assemblyQualifiedName)); - var type = Type.GetType (assemblyQualifiedName!, throwOnError: true)!; var methodsRef = new JniObjectReference (n_methods); -#if NET + var typeSig = new JniTypeSignature (nativeClass.Name); + var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig); +#if NET int methodsLength = JniEnvironment.Strings.GetStringLength (methodsRef); var methodsChars = JniEnvironment.Strings.GetStringChars (methodsRef, null); var methods = new ReadOnlySpan(methodsChars, methodsLength); @@ -223,6 +289,13 @@ static unsafe void RegisterNativeMembers ( envp.Dispose (); } } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + static Type GetTypeFromSignature (JniRuntime.JniTypeManager typeManager, JniTypeSignature typeSignature, string? context = null) + { + return typeManager.GetType (typeSignature) ?? + throw new NotSupportedException ($"Could not find System.Type corresponding to Java type {typeSignature} {(context == null ? "" : "within `" + context + "`")}."); + } } sealed class JniLocationException : Exception { diff --git a/src/Java.Interop/java/net/dot/jni/ManagedPeer.java b/src/Java.Interop/java/net/dot/jni/ManagedPeer.java index 50ff8aae6..dc66d3122 100644 --- a/src/Java.Interop/java/net/dot/jni/ManagedPeer.java +++ b/src/Java.Interop/java/net/dot/jni/ManagedPeer.java @@ -6,12 +6,10 @@ private ManagedPeer () { public static native void registerNativeMembers ( java.lang.Class nativeClass, - String assemblyQualifiedName, String methods); public static native void construct ( Object self, - String assemblyQualifiedName, String constructorSignature, Object... arguments ); diff --git a/src/Java.Interop/java/net/dot/jni/internal/JavaProxyObject.java b/src/Java.Interop/java/net/dot/jni/internal/JavaProxyObject.java index 5e7122789..013cb0708 100644 --- a/src/Java.Interop/java/net/dot/jni/internal/JavaProxyObject.java +++ b/src/Java.Interop/java/net/dot/jni/internal/JavaProxyObject.java @@ -8,11 +8,9 @@ extends java.lang.Object implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.Interop.JavaProxyObject, Java.Interop"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( JavaProxyObject.class, - assemblyQualifiedName, ""); } diff --git a/src/Java.Interop/java/net/dot/jni/internal/JavaProxyThrowable.java b/src/Java.Interop/java/net/dot/jni/internal/JavaProxyThrowable.java index 56abfc00a..066025ac5 100644 --- a/src/Java.Interop/java/net/dot/jni/internal/JavaProxyThrowable.java +++ b/src/Java.Interop/java/net/dot/jni/internal/JavaProxyThrowable.java @@ -8,11 +8,9 @@ extends java.lang.Error implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.Interop.JavaProxyThrowable, Java.Interop"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( JavaProxyThrowable.class, - assemblyQualifiedName, ""); } diff --git a/tests/Java.Base-Tests/Java.Base/JavaVMFixture.cs b/tests/Java.Base-Tests/Java.Base/JavaVMFixture.cs index f1729d6cb..63d9a5ced 100644 --- a/tests/Java.Base-Tests/Java.Base/JavaVMFixture.cs +++ b/tests/Java.Base-Tests/Java.Base/JavaVMFixture.cs @@ -17,6 +17,7 @@ static partial void CreateJavaVM () var c = new TestJVM ( jars: new[]{ "java.base-tests.jar" }, typeMappings: new Dictionary () { + ["example/MyIntConsumer"] = typeof (MyIntConsumer), ["example/MyRunnable"] = typeof (MyRunnable), [JavaInvoker.JniTypeName] = typeof (JavaInvoker), } diff --git a/tests/Java.Interop-PerformanceTests/Java.Interop/JavaPrimitiveArraysTiming.cs b/tests/Java.Interop-PerformanceTests/Java.Interop/JavaPrimitiveArraysTiming.cs new file mode 100644 index 000000000..4ca3cebe8 --- /dev/null +++ b/tests/Java.Interop-PerformanceTests/Java.Interop/JavaPrimitiveArraysTiming.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Java.Interop; + +using NUnit.Framework; + +namespace Java.Interop.PerformanceTests { + + [TestFixture] + class JavaPrimitiveArraysTiming : Java.InteropTests.JavaVMFixture { + + [Test] + public void CreationTiming () + { + const int CreationCount = 1000000; + var a = Stopwatch.StartNew (); + for (int i = 0; i < CreationCount; ++i) { + CreateArray (); + } + a.Stop (); + + var d = Stopwatch.StartNew (); + for (int i = 0; i < CreationCount; ++i) { + CreateDict (); + } + d.Stop (); + + Console.WriteLine ($"# {nameof (CreationTiming)}: Array Creation: {a.ElapsedMilliseconds}ms"); + Console.WriteLine ($"# {nameof (CreationTiming)}: Dict Creation: {d.ElapsedMilliseconds}ms"); + } + + [Test] + public void LookupTiming () + { + const int LookupCount = 1000000; + var av = CreateArray (); + var a = Stopwatch.StartNew (); + for (int i = 0; i < LookupCount; ++i) { + TryArrayLookup (av, typeof (int)); + } + a.Stop (); + var bv = CreateDict (); + var d = Stopwatch.StartNew (); + for (int i = 0; i < LookupCount; ++i) { + TryDictLookup (bv, typeof (int)); + } + d.Stop (); + Console.WriteLine ($"# {nameof (LookupTiming)}: Array Lookup: {a.ElapsedMilliseconds}ms"); + Console.WriteLine ($"# {nameof (LookupTiming)}: Dict Lookup: {d.ElapsedMilliseconds}ms"); + } + + static JniPrimitiveArrayInfo_Array[] CreateArray () + { + return new JniPrimitiveArrayInfo_Array[]{ + new ("Z", typeof (Boolean), typeof (Boolean[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaBooleanArray)), + new ("B", typeof (SByte), typeof (SByte[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSByteArray)), + new ("C", typeof (Char), typeof (Char[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaCharArray)), + new ("S", typeof (Int16), typeof (Int16[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt16Array)), + new ("I", typeof (Int32), typeof (Int32[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt32Array)), + new ("J", typeof (Int64), typeof (Int64[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt64Array)), + new ("F", typeof (Single), typeof (Single[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSingleArray)), + new ("D", typeof (Double), typeof (Double[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaDoubleArray)), + }; + } + + static bool TryArrayLookup (JniPrimitiveArrayInfo_Array[] array, Type type) + { + foreach (var e in array) { + if (Array.IndexOf (e.ArrayTypes, type) < 0) + continue; + return true; + } + return false; + } + + static Dictionary CreateDict () + { + return new Dictionary () { + [typeof (Boolean)] = new ("Z", typeof (Boolean), typeof (Boolean[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaBooleanArray)), + [typeof (SByte) ] = new ("B", typeof (SByte), typeof (SByte[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSByteArray)), + [typeof (Char) ] = new ("C", typeof (Char), typeof (Char[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaCharArray)), + [typeof (Int16) ] = new ("S", typeof (Int16), typeof (Int16[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt16Array)), + [typeof (Int32) ] = new ("I", typeof (Int32), typeof (Int32[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt32Array)), + [typeof (Int64) ] = new ("J", typeof (Int64), typeof (Int64[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaInt64Array)), + [typeof (Single) ] = new ("F", typeof (Single), typeof (Single[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaSingleArray)), + [typeof (Double) ] = new ("D", typeof (Double), typeof (Double[]), typeof (JavaArray), typeof (JavaPrimitiveArray), typeof (JavaDoubleArray)), + }; + } + + static bool TryDictLookup (Dictionary dict, Type type) + { + foreach (var v in dict.Values) { + if (v.ArrayTypes.Contains (type)) + return true; + } + return false; + } + } + + readonly struct JniPrimitiveArrayInfo_Array { + public readonly JniTypeSignature JniTypeSignature; + public readonly Type PrimitiveType; + public readonly Type[] ArrayTypes; + public JniPrimitiveArrayInfo_Array (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes) + { + JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true); + PrimitiveType = primitiveType; + ArrayTypes = arrayTypes; + } + } + + readonly struct JniPrimitiveArrayInfo_Hash { + public readonly JniTypeSignature JniTypeSignature; + public readonly Type PrimitiveType; + public readonly HashSet ArrayTypes; + public JniPrimitiveArrayInfo_Hash (string jniSimpleReference, Type primitiveType, params Type[] arrayTypes) + { + JniTypeSignature = new JniTypeSignature (jniSimpleReference, arrayRank: 1, keyword: true); + PrimitiveType = primitiveType; + ArrayTypes = new (arrayTypes); + } + } +} diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index 2aea0aac7..ba74d62c8 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -37,6 +37,9 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { [GenericHolder.JniTypeName] = typeof (GenericHolder<>), [RenameClassBase.JniTypeName] = typeof (RenameClassBase), [RenameClassDerived.JniTypeName] = typeof (RenameClassDerived), + [CallVirtualFromConstructorBase.JniTypeName] = typeof (CallVirtualFromConstructorBase), + [CallVirtualFromConstructorDerived.JniTypeName] = typeof (CallVirtualFromConstructorDerived), + [GetThis.JniTypeName] = typeof (GetThis), }; public JavaVMFixtureTypeManager () diff --git a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index 53705805f..c6be06530 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Java.Interop; @@ -115,25 +117,25 @@ static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeywo Assert.AreEqual (null, GetType ("Lcom/example/does/not/exist;")); Assert.AreEqual (null, GetType ("[Lcom/example/does/not/exist;")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[Z")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[C")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[B")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[S")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[I")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[J")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[F")); - Assert.AreEqual (typeof (JavaPrimitiveArray), GetType ("[D")); - Assert.AreEqual (typeof (JavaObjectArray), GetType ("[Ljava/lang/String;")); - - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[Z")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[C")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[B")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[S")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[I")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[J")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[F")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[D")); - Assert.AreEqual (typeof (JavaObjectArray>), GetType ("[[Ljava/lang/String;")); + AssertPrimitiveArrayTypesFromSignature (manager, "[Z", typeof (JavaBooleanArray)); + AssertPrimitiveArrayTypesFromSignature (manager, "[C", typeof (JavaCharArray)); + AssertPrimitiveArrayTypesFromSignature (manager, "[B", typeof (JavaSByteArray)); + AssertPrimitiveArrayTypesFromSignature (manager, "[S", typeof (JavaInt16Array)); + AssertPrimitiveArrayTypesFromSignature (manager, "[I", typeof (JavaInt32Array)); + AssertPrimitiveArrayTypesFromSignature (manager, "[J", typeof (JavaInt64Array)); + AssertPrimitiveArrayTypesFromSignature (manager, "[F", typeof (JavaSingleArray)); + AssertPrimitiveArrayTypesFromSignature (manager, "[D", typeof (JavaDoubleArray)); + AssertArrayTypesFromSignature (manager, "[Ljava/lang/String;", typeof (JavaObjectArray)); + + AssertArrayTypesFromSignature (manager, "[[Z", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[C", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[B", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[S", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[I", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[J", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[F", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[D", typeof (JavaObjectArray>)); + AssertArrayTypesFromSignature (manager, "[[Ljava/lang/String;", typeof (JavaObjectArray>)); // Yes, these look weird... // Assume: class II {} @@ -142,6 +144,34 @@ static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeywo Assert.AreEqual (null, GetType ("Ljava/lang/String")); } + static void AssertPrimitiveArrayTypesFromSignature (JniRuntime.JniTypeManager manager, string signature, params Type[] expectedTypes) + { + var sig = JniTypeSignature.Parse (signature); + var types = manager.GetTypes (sig).ToList (); + var messageFormat = $"Types for signature `{signature}` should contain `{{0}}`, instead contains: {string.Join (", ", types)}"; + var arrayTypes = new[]{ + typeof (JavaArray), + typeof (JavaPrimitiveArray), + typeof (T[]), + }.Concat (expectedTypes); + foreach (var t in arrayTypes) { + Assert.IsTrue (types.Contains (t), string.Format (messageFormat, t)); + } + } + + static void AssertArrayTypesFromSignature (JniRuntime.JniTypeManager manager, string signature, params Type[] expectedTypes) + { + var sig = JniTypeSignature.Parse (signature); + var types = manager.GetTypes (sig).ToList (); + var messageFormat = $"Types for signature `{signature}` should contain `{{0}}`, instead contains: {string.Join (", ", types)}"; + var arrayTypes = new[]{ + typeof (T[]), + }.Concat (expectedTypes); + foreach (var t in arrayTypes) { + Assert.IsTrue (types.Contains (t), string.Format (messageFormat, t)); + } + } + [Test] public void GetTypeSignature () { diff --git a/tests/Java.Interop-Tests/java/net/dot/jni/test/CallVirtualFromConstructorDerived.java b/tests/Java.Interop-Tests/java/net/dot/jni/test/CallVirtualFromConstructorDerived.java index 44bab4560..5ae143daf 100644 --- a/tests/Java.Interop-Tests/java/net/dot/jni/test/CallVirtualFromConstructorDerived.java +++ b/tests/Java.Interop-Tests/java/net/dot/jni/test/CallVirtualFromConstructorDerived.java @@ -8,11 +8,9 @@ public class CallVirtualFromConstructorDerived extends CallVirtualFromConstructorBase implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.InteropTests.CallVirtualFromConstructorDerived, Java.Interop-Tests"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( CallVirtualFromConstructorDerived.class, - assemblyQualifiedName, ""); } @@ -23,8 +21,7 @@ public CallVirtualFromConstructorDerived (int value) { if (CallVirtualFromConstructorDerived.class == getClass ()) { net.dot.jni.ManagedPeer.construct ( this, - assemblyQualifiedName, - "System.Int32", + "(I)V", value ); } diff --git a/tests/Java.Interop-Tests/java/net/dot/jni/test/GetThis.java b/tests/Java.Interop-Tests/java/net/dot/jni/test/GetThis.java index 3013ebb75..f3a27d025 100644 --- a/tests/Java.Interop-Tests/java/net/dot/jni/test/GetThis.java +++ b/tests/Java.Interop-Tests/java/net/dot/jni/test/GetThis.java @@ -6,11 +6,9 @@ public class GetThis implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.InteropTests.GetThis, Java.Interop-Tests"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( GetThis.class, - assemblyQualifiedName, ""); } @@ -20,8 +18,7 @@ public GetThis () { if (GetThis.class == getClass ()) { net.dot.jni.ManagedPeer.construct ( this, - assemblyQualifiedName, - "" + "()V" ); } } diff --git a/tests/Java.Interop-Tests/java/net/dot/jni/test/TestType.java b/tests/Java.Interop-Tests/java/net/dot/jni/test/TestType.java index adf4eebc7..456bef7b5 100644 --- a/tests/Java.Interop-Tests/java/net/dot/jni/test/TestType.java +++ b/tests/Java.Interop-Tests/java/net/dot/jni/test/TestType.java @@ -6,11 +6,9 @@ public class TestType implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.InteropTests.TestType, Java.Interop-Tests"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( TestType.class, - assemblyQualifiedName, ""); } @@ -20,8 +18,7 @@ public TestType () { if (TestType.class == getClass ()) { net.dot.jni.ManagedPeer.construct ( this, - assemblyQualifiedName, - "" + "()V" ); } } @@ -31,8 +28,7 @@ public TestType (TestType a, int b) { if (TestType.class == getClass ()) { net.dot.jni.ManagedPeer.construct ( this, - assemblyQualifiedName, - assemblyQualifiedName + ":System.Int32", + "(Lnet/dot/jni/test/TestType;I)V", a, b ); } diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs index 69c5ca490..9c709a38b 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs @@ -30,9 +30,11 @@ long p namespace Java.InteropTests { - [JniTypeSignature ("net/dot/jni/test/ExportType")] + [JniTypeSignature (JniTypeName)] public class ExportTest : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/ExportType"; + [JniAddNativeMethodRegistrationAttribute] static void RegisterNativeMembers (JniNativeMethodRegistrationArguments args) { diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs b/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs index 4b6362dfc..d3aeb7183 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs @@ -1,3 +1,6 @@ +using System; +using System.Text; + using Java.Interop; namespace Java.InteropTests; @@ -8,16 +11,26 @@ class JavaCallableExample : Java.Lang.Object { internal const string TypeSignature = "net/dot/jni/test/JavaCallableExample"; [JavaCallableConstructor(SuperConstructorExpression="")] - public JavaCallableExample (int a) + public JavaCallableExample (int[] a, JavaInt32Array b) { this.a = a; + this.b = b; + + var bDescription = new StringBuilder (); + for (int i = 0; i < b.Length; ++i) { + if (i > 0) + bDescription.Append (", "); + bDescription.Append (b [i]); + } + Console.WriteLine ($"JavaCallableExample ({{{string.Join (", ", a)}}}, {{{bDescription}}})"); } - int a; + int[] a; + JavaInt32Array b; - [JavaCallable ("getA")] - public int GetA () + [JavaCallable ("getFirstA")] + public int GetFirstA () { - return a; + return a [0]; } } diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs b/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs index 52efcfb22..99e3e5aa7 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs @@ -19,7 +19,7 @@ public void JavaCallableWrappers () [Test] public void ManagedCtorInvokesJavaDefaultCtor () { - using var o = new JavaCallableExample (42); + using var o = new JavaCallableExample (new[]{1,2}, new JavaInt32Array (new[]{3,4})); } static JniType CreateUseJavaCallableExampleType () => diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop.Export-Tests/Java.Interop/JavaVMFixture.cs index 0f252b2b1..c22d6dae1 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/JavaVMFixture.cs @@ -12,7 +12,11 @@ public abstract partial class JavaVMFixture { static JavaVMFixture () { var c = new TestJVM ( - jars: new[]{ "export-test.jar" } + jars: new[]{ "export-test.jar" }, + typeMappings: new() { + [ExportTest.JniTypeName] = typeof (ExportTest), + [JavaCallableExample.TypeSignature] = typeof (JavaCallableExample), + } ); JniRuntime.SetCurrent (c); } diff --git a/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/ExportType.java b/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/ExportType.java index 1d12e7092..28e1f3b55 100644 --- a/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/ExportType.java +++ b/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/ExportType.java @@ -8,11 +8,9 @@ public class ExportType implements GCUserPeerable { - static final String assemblyQualifiedName = "Java.InteropTests.ExportTest, Java.Interop.Export-Tests"; static { net.dot.jni.ManagedPeer.registerNativeMembers ( ExportType.class, - assemblyQualifiedName, ""); } diff --git a/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/UseJavaCallableExample.java b/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/UseJavaCallableExample.java index 780daf4c8..24d603e78 100644 --- a/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/UseJavaCallableExample.java +++ b/tests/Java.Interop.Export-Tests/java/net/dot/jni/test/UseJavaCallableExample.java @@ -5,7 +5,7 @@ public class UseJavaCallableExample { public static boolean test() { - JavaCallableExample e = new JavaCallableExample(42); - return e.getA() == 42; + JavaCallableExample e = new JavaCallableExample(new int[]{1,2}, new int[]{3, 4}); + return e.getFirstA() == 1; } } diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs index 0a8702c76..e64033543 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs @@ -641,7 +641,7 @@ extends java.lang.Object __md_methods = ""n_Example:()V:__export__\n"" + """"; - net.dot.jni.ManagedPeer.registerNativeMembers (JavaInteropExample.class, ""Xamarin.Android.ToolsTests.JavaInteropExample, Java.Interop.Tools.JavaCallableWrappers-Tests"", __md_methods); + net.dot.jni.ManagedPeer.registerNativeMembers (JavaInteropExample.class, __md_methods); } @@ -649,7 +649,7 @@ public JavaInteropExample (int p0, int p1) { super (); if (getClass () == JavaInteropExample.class) { - net.dot.jni.ManagedPeer.construct (this, ""Xamarin.Android.ToolsTests.JavaInteropExample, Java.Interop.Tools.JavaCallableWrappers-Tests"", ""System.Int32, System.Private.CoreLib:System.Int32, System.Private.CoreLib"", new java.lang.Object[] { p0, p1 }); + net.dot.jni.ManagedPeer.construct (this, ""(II)V"", new java.lang.Object[] { p0, p1 }); } }