From 07c73009571d3b5d36ae79d0d4d69a02062dd6e8 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 2 Feb 2024 08:29:09 -0500 Subject: [PATCH] [Java.Interop] Typemap support for JavaObject & `[JniTypeSignature]` (#1181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: https://github.com/xamarin/xamarin-android/pull/8543 Context: https://github.com/xamarin/xamarin-android/pull/8625 Context: https://github.com/xamarin/xamarin-android/pull/8681 Context: https://github.com/xamarin/java.interop/pull/1168 Context: def5bc0df68a5908fac85e5dd4f02f1e27c75882 Context: 005c914170a0af9069ff18fd4dd9d45463dd5dc6 xamarin/xamarin-android#8543 tested PR #1168, was Totally Green™ -- finding no issues -- and so we merged PR #1168 into 005c9141. Enter xamarin/xamarin-android#8625, which bumps xamarin-android to use def5bc0d, which includes 005c9141. xamarin/xamarin-android#8625 contains *failing unit tests* (?!), including `Java.InteropTests.InvokeVirtualFromConstructorTests()`: Java.Lang.LinkageError : net.dot.jni.test.CallVirtualFromConstructorDerived ----> System.NotSupportedException : Could not find System.Type corresponding to Java type JniTypeSignature(TypeName=net/dot/jni/test/CallVirtualFromConstructorDerived ArrayRank=0 Keyword=False) . at Java.Interop.JniEnvironment.StaticMethods.GetStaticMethodID(JniObjectReference , String , String ) at Java.Interop.JniType.GetStaticMethod(String , String ) at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String , String ) at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String ) at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String , JniArgumentValue* ) at Java.InteropTests.CallVirtualFromConstructorDerived.NewInstance(Int32 value) at Java.InteropTests.InvokeVirtualFromConstructorTests.ActivationConstructor() at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags ) --- End of managed Java.Lang.LinkageError stack trace --- java.lang.NoClassDefFoundError: net.dot.jni.test.CallVirtualFromConstructorDerived at crc643df67da7b13bb6b1.TestInstrumentation_1.n_onStart(Native Method) at crc643df67da7b13bb6b1.TestInstrumentation_1.onStart(TestInstrumentation_1.java:35) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189) Caused by: android.runtime.JavaProxyThrowable: [System.NotSupportedException]: Could not find System.Type corresponding to Java type JniTypeSignature(TypeName=net/dot/jni/test/CallVirtualFromConstructorDerived ArrayRank=0 Keyword=False) . at Java.Interop.ManagedPeer.GetTypeFromSignature(Unknown Source:0) at Java.Interop.ManagedPeer.RegisterNativeMembers(Unknown Source:0) at net.dot.jni.ManagedPeer.registerNativeMembers(Native Method) at net.dot.jni.test.CallVirtualFromConstructorDerived.(CallVirtualFromConstructorDerived.java:12) ... 3 more --NotSupportedException at Java.Interop.ManagedPeer.GetTypeFromSignature(JniTypeManager , JniTypeSignature , String ) at Java.Interop.ManagedPeer.RegisterNativeMembers(IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, IntPtr n_methods) :shocked-pikachu-face: (But xamarin/xamarin-android#8543 was green!) The problem is twofold: 1. 005c9141 now requires the presence of typemap entries from e.g. `net.dot.jni.test.CallVirtualFromConstructorDerived` to `Java.InteropTests.CallVirtualFromConstructorDerived`. 2. `Java.Interop.Tools.JavaCallableWrappers` et al doesn't create typemap entries for `Java.Interop.JavaObject` subclasses which have `[JniTypeSignature]`. Consequently, our units tests fail (and apparently weren't *run* on xamarin/xamarin-android#8543?! Still not sure what happened.) Update typemap generation by adding a new `TypeDefinition.HasJavaPeer()` extension method to replace all the `.IsSubclassOf("Java.Lang.Object")` and similar checks, extending it to also check for `Java.Interop.JavaObject` and `Java.Interop.JavaException` base types. (Continuing to use base type checks is done instead of just relying on implementation of `Java.Interop.IJavaPeerable` as a performance optimization, as there could be *lots* of interface types to check.) Additionally, @jonathanpeppers -- while trying to investigate all this -- ran across a build failure: obj\Debug\net9.0-android\android\src\java\lang\Object.java(7,15): javac.exe error JAVAC0000: error: cyclic inheritance involving Object This suggests that `Java.Interop.Tools.JavaCallableWrappers` was encountering `Java.Interop.JavaObject` -- or some other type which has `[JniTypeSignature("java/lang/Object")]` -- which is why `java/lang/Object.java` was being generated. Audit all `[JniTypeSignature]` attributes, and add `GenerateJavaPeer=false` to all types which should *not* hava a Java Callable Wrapper generated for them. This includes nearly everything within `Java.Interop-Tests.dll`. (We want the typemaps! We *don't* want generated Java source, as we have hand-written Java peer types for those tests.) Add `[JniTypeSignature]` to `GenericHolder`. This type mapping isn't *actually* required, but it *is* used in `JavaVMFixture`, and it confuses people (me!) if things are inconsistent. Additionally, remove `tests/` from the Java-side name, for consistency. ~~ Avoid multiple java/lang/Object bindings ~~ A funny thing happened when in xamarin/xamarin-android#8681 -- which tested this commit -- when using an intermediate version of this commit: unit tests started crashing! E monodroid-assembly: typemap: unable to load assembly 'Java.Interop-Tests' when looking up managed type corresponding to Java type 'java/lang/Object' What appears to be happening is an Unfortunate Interaction™: 1. `Java.Interop-Tests.dll` contained *multiple bindings* for `java/lang/Object`. e.g. [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] partial class JavaDisposedObject : JavaObject { } 2. The typemap generator has no functionality to "prioritize" one binding vs. another; it's random. As such, there is nothing to cause `Java.Lang.Object, Mono.Android` to be used as the preferred binding for `java/lang/Object`. This meant that when we hit the typemap codepath in .NET Android, we looked for the C# type that corresponded to `java/lang/Object`, found *some random type* from `Java.Interop-Tests`, and… …and then we hit another oddity: that codepath only supported looking for C# types in assemblies which had already been loaded. This was occurring during startup, so `Java.Interop-Tests` had not yet been loaded yet, so it errored out, returned `nullptr`, and later Android just aborts things: F droid.NET_Test: runtime.cc:638] JNI DETECTED ERROR IN APPLICATION: use of deleted local reference 0x79 Just…eep! This didn't happen before because `Java.Interop.JavaObject` subclasses *didn't* participate in typemap generation. This commit *adds* that support, introducing this unforeseen interaction. Fix this by *removing* most "alternate bindings" for `java/lang/Object`: - [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] + [JniTypeSignature (JniTypeName)] partial class JavaDisposedObject : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/JavaDisposedObject"; } This implicitly requires that we now have a Java Callable Wrapper for this type, so update `Java.Interop-Tests.csproj` to run `jcw-gen` as part of the build process. This ensures that we create the JCW for e.g. `JavaDisposedObject`. Update `JavaVMFixture` to add the required typemap entries. --- Aside: this project includes [T4 Text Templates][0]. To regenerate the output files *without involving Visual Studio*, you can install the [`dotnet-t4`][1] tool: $ dotnet tool install --global dotnet-t4 then run it separately for each `.tt` file: $HOME/.dotnet/tools/t4 -o src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs \ src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt [0]: https://learn.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2022 [1]: https://www.nuget.org/packages/dotnet-t4/ --- .../Java.Interop.Dynamic/JavaInstanceProxy.cs | 2 +- .../TypeDefinitionRocks.cs | 19 +++++++++ .../JavaCallableWrapperGenerator.cs | 2 +- .../JavaTypeScanner.cs | 5 +-- .../JavaNativeTypeManager.cs | 2 +- .../Java.Interop/JavaException.cs | 2 +- src/Java.Interop/Java.Interop/JavaObject.cs | 2 +- .../Java.Interop/JavaPrimitiveArrays.cs | 16 ++++---- .../Java.Interop/JavaPrimitiveArrays.tt | 2 +- .../Java.Interop/JavaProxyObject.cs | 2 +- .../Java.Interop/JavaProxyThrowable.cs | 2 +- src/Java.Interop/Java.Interop/ManagedPeer.cs | 2 +- .../Java.Interop/JavaTiming.cs | 2 +- .../Java.Interop-Tests.csproj | 1 + .../Java.Interop-Tests.targets | 39 ++++++++++++++++++- .../Java.Interop/CallNonvirtualBase.cs | 2 +- .../Java.Interop/CallNonvirtualDerived.cs | 2 +- .../Java.Interop/CallNonvirtualDerived2.cs | 2 +- .../CallVirtualFromConstructorBase.cs | 2 +- .../CallVirtualFromConstructorDerived.cs | 2 +- .../Java.Interop/GetThis.cs | 2 +- .../Java.Interop/JavaManagedGCBridgeTests.cs | 2 +- .../Java.Interop/JavaObjectTest.cs | 18 ++++++--- .../Java.Interop/JavaVMFixture.cs | 4 ++ .../Java.Interop/JniPeerMembersTests.cs | 12 +++--- .../Java.Interop/JniTypeManagerTests.cs | 3 +- .../Java.Interop/TestType.cs | 2 +- .../Java.Interop/ExportTest.cs | 2 +- 28 files changed, 110 insertions(+), 45 deletions(-) diff --git a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaInstanceProxy.cs b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaInstanceProxy.cs index 6af9b57c4..d4079746f 100644 --- a/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaInstanceProxy.cs +++ b/src/Java.Interop.Dynamic/Java.Interop.Dynamic/JavaInstanceProxy.cs @@ -13,7 +13,7 @@ namespace Java.Interop.Dynamic { - [JniTypeSignature ("java/lang/Object")] + [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] class JavaInstanceProxy : JavaObject { public JavaInstanceProxy (ref JniObjectReference reference, JniObjectReferenceOptions transfer) diff --git a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/TypeDefinitionRocks.cs b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/TypeDefinitionRocks.cs index f4a2d82bc..2873b4b7d 100644 --- a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/TypeDefinitionRocks.cs +++ b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/TypeDefinitionRocks.cs @@ -92,6 +92,25 @@ public static bool IsSubclassOf (this TypeDefinition type, string typeName, IMet return false; } + public static bool HasJavaPeer (this TypeDefinition type, IMetadataResolver resolver) + { + if (type.IsInterface && type.ImplementsInterface ("Java.Interop.IJavaPeerable", resolver)) + return true; + + foreach (var t in type.GetTypeAndBaseTypes (resolver)) { + switch (t.FullName) { + case "Java.Lang.Object": + case "Java.Lang.Throwable": + case "Java.Interop.JavaObject": + case "Java.Interop.JavaException": + return true; + default: + break; + } + } + return false; + } + [Obsolete ("Use the TypeDefinitionCache overload for better performance.", error: true)] public static bool ImplementsInterface (this TypeDefinition type, string interfaceName) => throw new NotSupportedException (); 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 081481c47..699b3597e 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 @@ -121,7 +121,7 @@ void AddNestedTypes (TypeDefinition type) } children = children ?? new List (); foreach (TypeDefinition nt in type.NestedTypes) { - if (!nt.IsSubclassOf ("Java.Lang.Object", cache)) + if (!nt.HasJavaPeer (cache)) continue; if (!JavaNativeTypeManager.IsNonStaticInnerClass (nt, cache)) continue; diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs index 24c48296e..9d74487a9 100644 --- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs +++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs @@ -66,10 +66,7 @@ public List GetJavaTypes (AssemblyDefinition assembly) void AddJavaTypes (List javaTypes, TypeDefinition type) { - if (type.IsSubclassOf ("Java.Lang.Object", cache) || - type.IsSubclassOf ("Java.Lang.Throwable", cache) || - (type.IsInterface && type.ImplementsInterface ("Java.Interop.IJavaPeerable", cache))) { - // For subclasses of e.g. Android.App.Activity. + if (type.HasJavaPeer (cache)) { javaTypes.Add (type); } else if (type.IsClass && !type.IsSubclassOf ("System.Exception", cache) && type.ImplementsInterface ("Android.Runtime.IJavaObject", cache)) { var level = ErrorOnCustomJavaObject ? TraceLevel.Error : TraceLevel.Warning; 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 5531acc50..4cc4f10d6 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 @@ -717,7 +717,7 @@ internal static bool IsNonStaticInnerClass (TypeDefinition? type, IMetadataResol if (!type.IsNested) return false; - if (!type.DeclaringType.IsSubclassOf ("Java.Lang.Object", cache)) + if (!type.DeclaringType.HasJavaPeer (cache)) return false; foreach (var baseType in type.GetBaseTypes (cache)) { diff --git a/src/Java.Interop/Java.Interop/JavaException.cs b/src/Java.Interop/Java.Interop/JavaException.cs index 208f42af0..f62135f1f 100644 --- a/src/Java.Interop/Java.Interop/JavaException.cs +++ b/src/Java.Interop/Java.Interop/JavaException.cs @@ -4,7 +4,7 @@ namespace Java.Interop { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] unsafe public class JavaException : Exception, IJavaPeerable { internal const string JniTypeName = "java/lang/Throwable"; diff --git a/src/Java.Interop/Java.Interop/JavaObject.cs b/src/Java.Interop/Java.Interop/JavaObject.cs index 980629b55..8ce5212db 100644 --- a/src/Java.Interop/Java.Interop/JavaObject.cs +++ b/src/Java.Interop/Java.Interop/JavaObject.cs @@ -4,7 +4,7 @@ namespace Java.Interop { - [JniTypeSignature ("java/lang/Object")] + [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] unsafe public class JavaObject : IJavaPeerable { readonly static JniPeerMembers _members = new JniPeerMembers ("java/lang/Object", typeof (JavaObject)); diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs index 10a41623a..1aa030839 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs @@ -142,7 +142,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("Z", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("Z", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaBooleanArray : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -337,7 +337,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("B", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("B", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaSByteArray : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -532,7 +532,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("C", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("C", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaCharArray : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -727,7 +727,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("S", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("S", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaInt16Array : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -922,7 +922,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("I", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("I", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaInt32Array : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -1117,7 +1117,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("J", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("J", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaInt64Array : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -1312,7 +1312,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("F", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("F", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaSingleArray : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); @@ -1507,7 +1507,7 @@ protected override unsafe void Synchronize (JniReleaseArrayElementsMode releaseM } } - [JniTypeSignature ("D", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("D", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class JavaDoubleArray : JavaPrimitiveArray { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); diff --git a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt index 83a3aa160..c644d61f6 100644 --- a/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt +++ b/src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt @@ -139,7 +139,7 @@ namespace Java.Interop { } } - [JniTypeSignature ("<#= info.JniType #>", ArrayRank=1, IsKeyword=true)] + [JniTypeSignature ("<#= info.JniType #>", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed class Java<#= info.TypeModifier #>Array : JavaPrimitiveArray<<#= info.ManagedType #>> { internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler (); diff --git a/src/Java.Interop/Java.Interop/JavaProxyObject.cs b/src/Java.Interop/Java.Interop/JavaProxyObject.cs index 28f48ef7a..909629b18 100644 --- a/src/Java.Interop/Java.Interop/JavaProxyObject.cs +++ b/src/Java.Interop/Java.Interop/JavaProxyObject.cs @@ -7,7 +7,7 @@ namespace Java.Interop { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] sealed class JavaProxyObject : JavaObject, IEquatable { internal const string JniTypeName = "net/dot/jni/internal/JavaProxyObject"; diff --git a/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs b/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs index 14ebb0834..3fb2cc720 100644 --- a/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs +++ b/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs @@ -4,7 +4,7 @@ namespace Java.Interop { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] sealed class JavaProxyThrowable : JavaException { new internal const string JniTypeName = "net/dot/jni/internal/JavaProxyThrowable"; diff --git a/src/Java.Interop/Java.Interop/ManagedPeer.cs b/src/Java.Interop/Java.Interop/ManagedPeer.cs index 2757d2cdc..b23277fde 100644 --- a/src/Java.Interop/Java.Interop/ManagedPeer.cs +++ b/src/Java.Interop/Java.Interop/ManagedPeer.cs @@ -13,7 +13,7 @@ namespace Java.Interop { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] /* static */ sealed class ManagedPeer : JavaObject { internal const string JniTypeName = "net/dot/jni/ManagedPeer"; diff --git a/tests/Java.Interop-PerformanceTests/Java.Interop/JavaTiming.cs b/tests/Java.Interop-PerformanceTests/Java.Interop/JavaTiming.cs index dbf586885..a4104ac70 100644 --- a/tests/Java.Interop-PerformanceTests/Java.Interop/JavaTiming.cs +++ b/tests/Java.Interop-PerformanceTests/Java.Interop/JavaTiming.cs @@ -7,7 +7,7 @@ namespace Java.Interop.PerformanceTests { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] public class JavaTiming : JavaObject { protected const string JniTypeName = "com/xamarin/interop/performance/JavaTiming"; diff --git a/tests/Java.Interop-Tests/Java.Interop-Tests.csproj b/tests/Java.Interop-Tests/Java.Interop-Tests.csproj index 2529c2143..51fa6e6df 100644 --- a/tests/Java.Interop-Tests/Java.Interop-Tests.csproj +++ b/tests/Java.Interop-Tests/Java.Interop-Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/tests/Java.Interop-Tests/Java.Interop-Tests.targets b/tests/Java.Interop-Tests/Java.Interop-Tests.targets index b62ad593d..3f6e36bc6 100644 --- a/tests/Java.Interop-Tests/Java.Interop-Tests.targets +++ b/tests/Java.Interop-Tests/Java.Interop-Tests.targets @@ -1,12 +1,47 @@ + + <_BuildJavaInteropTestsJarInputs Include="$(TargetPath)" /> + <_BuildJavaInteropTestsJarInputs Include="$(MSBuildThisFileFullPath)" /> + <_BuildJavaInteropTestsJarInputs Include="java\**\*.java" /> + + + + + + + + <_RefAsmDirs Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" /> + + + <_JcwGen>"$(UtilityOutputFullPath)/jcw-gen.dll" + <_Target>--codegen-target JavaInterop1 + <_Output>-o "$(IntermediateOutputPath)/java" + <_Libpath>@(_RefAsmDirs->'-L "%(Identity)"', ' ') + + + + + + + + <_GeneratedJcwSource Include="$(IntermediateOutputPath)java\**\*.java" /> + + + <_Source Include="@(JavaInteropTestJar->Replace('%5c', '/'))" /> + <_Source Include="@(_GeneratedJcwSource->Replace('%5c', '/'))" /> array, out WeakRef } } - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] public class CrossReferenceBridge : JavaObject { internal const string JniTypeName = "net/dot/jni/test/CrossReferenceBridge"; diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs b/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs index a11736061..d90d5846f 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs @@ -164,11 +164,13 @@ public void Ctor_Exceptions () var r = new JniObjectReference (); Assert.Throws (() => new JavaObject (ref r, JniObjectReferenceOptions.CopyAndDispose)); - // Note: This may break if/when JavaVM provides "default" - Assert.Throws (() => new JavaObjectWithNoJavaPeer ()); #if __ANDROID__ Assert.Throws (() => new JavaObjectWithMissingJavaPeer ()).Dispose (); #else // !__ANDROID__ + // Note: `JavaObjectWithNoJavaPeer` creation works on Android because tooling provides all + // typemap entries. On desktop, we use the hardcoded dictionary in JavaVMFixture, which + // deliberately *lacks* an entry for `JavaObjectWithNoJavaPeer`. + Assert.Throws (() => new JavaObjectWithNoJavaPeer ()); Assert.Throws (() => new JavaObjectWithMissingJavaPeer ()).Dispose (); #endif // !__ANDROID__ } @@ -201,17 +203,21 @@ public void DisposeAccessesThis () } } +#if !__ANDROID__ class JavaObjectWithNoJavaPeer : JavaObject { } +#endif // !__ANDROID__ - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] class JavaObjectWithMissingJavaPeer : JavaObject { internal const string JniTypeName = "__this__/__type__/__had__/__better__/__not__/__Exist__"; } - [JniTypeSignature ("java/lang/Object")] + [JniTypeSignature (JniTypeName)] class JavaDisposedObject : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/JavaDisposedObject"; + public Action OnDisposed; public Action OnFinalized; @@ -230,8 +236,10 @@ protected override void Dispose (bool disposing) } } - [JniTypeSignature ("java/lang/Object")] + [JniTypeSignature (JniTypeName)] class MyDisposableObject : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/MyDisposableObject"; + bool _isDisposed; public MyDisposableObject () diff --git a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs index ba74d62c8..bbb8c0c5e 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs @@ -39,7 +39,11 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager { [RenameClassDerived.JniTypeName] = typeof (RenameClassDerived), [CallVirtualFromConstructorBase.JniTypeName] = typeof (CallVirtualFromConstructorBase), [CallVirtualFromConstructorDerived.JniTypeName] = typeof (CallVirtualFromConstructorDerived), + [CrossReferenceBridge.JniTypeName] = typeof (CrossReferenceBridge), [GetThis.JniTypeName] = typeof (GetThis), + [JavaDisposedObject.JniTypeName] = typeof (JavaDisposedObject), + [JavaObjectWithMissingJavaPeer.JniTypeName] = typeof (JavaObjectWithMissingJavaPeer), + [MyDisposableObject.JniTypeName] = typeof (JavaDisposedObject), }; public JavaVMFixtureTypeManager () diff --git a/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs index a21e0da9b..f225f86bc 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs @@ -161,7 +161,7 @@ public void DesugarInterfaceStaticMethod () #endif // NET } - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] class MyString : JavaObject { internal const string JniTypeName = "java/lang/String"; @@ -182,7 +182,7 @@ public unsafe MyString (string value) } - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] class JavaLangRemappingTestObject : JavaObject { internal const string JniTypeName = "java/lang/Object"; static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaLangRemappingTestObject)); @@ -210,7 +210,7 @@ public unsafe int remappedToStaticHashCode () } } - [JniTypeSignature (JavaLangRemappingTestRuntime.JniTypeName)] + [JniTypeSignature (JavaLangRemappingTestRuntime.JniTypeName, GenerateJavaPeer=false)] internal class JavaLangRemappingTestRuntime : JavaObject { internal const string JniTypeName = "java/lang/Runtime"; static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaLangRemappingTestRuntime)); @@ -228,7 +228,7 @@ public static unsafe void doesNotExist () } } - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] class RenameClassBase : JavaObject { internal const string JniTypeName = "net/dot/jni/test/RenameClassBase1"; static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (RenameClassBase)); @@ -246,7 +246,7 @@ public virtual unsafe int hashCode () } } - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] class RenameClassDerived : RenameClassBase { internal new const string JniTypeName = "net/dot/jni/test/RenameClassDerived"; public RenameClassDerived () @@ -260,7 +260,7 @@ public override unsafe int hashCode () } #if NET - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] interface IAndroidInterface : IJavaPeerable { internal const string JniTypeName = "net/dot/jni/test/AndroidInterface"; diff --git a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index c6be06530..e3f464c98 100644 --- a/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -197,8 +197,9 @@ public void GetTypeSignature () } } + [JniTypeSignature (JniTypeName)] class GenericHolder : JavaObject { - public const string JniTypeName = "net/dot/jni/test/tests/GenericHolder"; + public const string JniTypeName = "net/dot/jni/test/GenericHolder"; public T Value {get; set;} } diff --git a/tests/Java.Interop-Tests/Java.Interop/TestType.cs b/tests/Java.Interop-Tests/Java.Interop/TestType.cs index 737aebaa3..bd8804dd5 100644 --- a/tests/Java.Interop-Tests/Java.Interop/TestType.cs +++ b/tests/Java.Interop-Tests/Java.Interop/TestType.cs @@ -9,7 +9,7 @@ namespace Java.InteropTests { #if !NO_MARSHAL_MEMBER_BUILDER_SUPPORT - [JniTypeSignature (TestType.JniTypeName)] + [JniTypeSignature (TestType.JniTypeName, GenerateJavaPeer=false)] public partial class TestType : JavaObject { internal const string JniTypeName = "net/dot/jni/test/TestType"; diff --git a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs index 9c709a38b..43a15ed89 100644 --- a/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs +++ b/tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs @@ -30,7 +30,7 @@ long p namespace Java.InteropTests { - [JniTypeSignature (JniTypeName)] + [JniTypeSignature (JniTypeName, GenerateJavaPeer=false)] public class ExportTest : JavaObject { internal const string JniTypeName = "net/dot/jni/test/ExportType";