Skip to content

Commit

Permalink
[Java.Interop] Typemap support for JavaObject & [JniTypeSignature] (#…
Browse files Browse the repository at this point in the history
…1181)

Context: dotnet/android#8543
Context: dotnet/android#8625
Context: dotnet/android#8681
Context: #1168
Context: def5bc0
Context: 005c914

dotnet/android#8543 tested PR #1168, was Totally Green™ --
finding no issues -- and so we merged PR #1168 into 005c914.

Enter dotnet/android#8625, which bumps xamarin-android to
use def5bc0, which includes 005c914.  dotnet/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.<clinit>(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 dotnet/android#8543 was green!)

The problem is twofold:

 1. 005c914 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
dotnet/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<T>`.  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 dotnet/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/
  • Loading branch information
jonpryor authored Feb 2, 2024
1 parent d529f3b commit 07c7300
Show file tree
Hide file tree
Showing 28 changed files with 110 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ void AddNestedTypes (TypeDefinition type)
}
children = children ?? new List<JavaCallableWrapperGenerator> ();
foreach (TypeDefinition nt in type.NestedTypes) {
if (!nt.IsSubclassOf ("Java.Lang.Object", cache))
if (!nt.HasJavaPeer (cache))
continue;
if (!JavaNativeTypeManager.IsNonStaticInnerClass (nt, cache))
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,7 @@ public List<TypeDefinition> GetJavaTypes (AssemblyDefinition assembly)

void AddJavaTypes (List<TypeDefinition> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JavaException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JavaObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
16 changes: 8 additions & 8 deletions src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Boolean> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<SByte> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Char> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Int16> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Int32> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Int64> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Single> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down Expand Up @@ -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<Double> {

internal static readonly ValueMarshaler ArrayMarshaler = new ValueMarshaler ();
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JavaProxyObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Java.Interop {

[JniTypeSignature (JniTypeName)]
[JniTypeSignature (JniTypeName, GenerateJavaPeer=false)]
sealed class JavaProxyObject : JavaObject, IEquatable<JavaProxyObject>
{
internal const string JniTypeName = "net/dot/jni/internal/JavaProxyObject";
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JavaProxyThrowable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/ManagedPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions tests/Java.Interop-Tests/Java.Interop-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ProjectReference Include="..\..\src\Java.Interop.GenericMarshaler\Java.Interop.GenericMarshaler.csproj" />
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
<ProjectReference Include="..\TestJVM\TestJVM.csproj" />
<ProjectReference Include="..\..\tools\jcw-gen\jcw-gen.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
Expand Down
39 changes: 37 additions & 2 deletions tests/Java.Interop-Tests/Java.Interop-Tests.targets
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
<Project>

<ItemGroup>
<_BuildJavaInteropTestsJarInputs Include="$(TargetPath)" />
<_BuildJavaInteropTestsJarInputs Include="$(MSBuildThisFileFullPath)" />
<_BuildJavaInteropTestsJarInputs Include="java\**\*.java" />
</ItemGroup>

<Target Name="_CreateJavaCallableWrappers"
Condition=" '$(TargetPath)' != '' "
AfterTargets="Build"
Inputs="@(_BuildJavaInteropTestsJarInputs)"
Outputs="$(IntermediateOutputPath)java\.stamp">
<RemoveDir Directories="$(IntermediateOutputPath)java" />
<MakeDir Directories="$(IntermediateOutputPath)java" />
<ItemGroup>
<!-- I can't find a good way to trim the trailing `\`, so append with `.` so we can sanely quote for $(_Libpath) -->
<_RefAsmDirs Include="@(ReferencePathWithRefAssemblies->'%(RootDir)%(Directory).'->Distinct())" />
</ItemGroup>
<PropertyGroup>
<_JcwGen>"$(UtilityOutputFullPath)/jcw-gen.dll"</_JcwGen>
<_Target>--codegen-target JavaInterop1</_Target>
<_Output>-o "$(IntermediateOutputPath)/java"</_Output>
<_Libpath>@(_RefAsmDirs->'-L "%(Identity)"', ' ')</_Libpath>
</PropertyGroup>
<Exec Command="$(DotnetToolPath) $(_JcwGen) -v &quot;$(TargetPath)&quot; $(_Target) $(_Output) $(_Libpath)" />
<Touch Files="$(IntermediateOutputPath)java\.stamp" AlwaysCreate="True" />
</Target>

<Target Name="_CollectGeneratdJcwSource">
<ItemGroup>
<_GeneratedJcwSource Include="$(IntermediateOutputPath)java\**\*.java" />
</ItemGroup>
</Target>

<Target Name="BuildInteropTestJar"
BeforeTargets="Build"
Inputs="@(JavaInteropTestJar)"
AfterTargets="Build"
DependsOnTargets="_CreateJavaCallableWrappers;_CollectGeneratdJcwSource"
Inputs="@(JavaInteropTestJar);@(_GeneratedJcwSource)"
Outputs="$(OutputPath)interop-test.jar">
<MakeDir Directories="$(IntermediateOutputPath)it-classes" />
<ItemGroup>
<_Source Include="@(JavaInteropTestJar->Replace('%5c', '/'))" />
<_Source Include="@(_GeneratedJcwSource->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(IntermediateOutputPath)_java_sources.txt"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Java.InteropTests
{
[JniTypeSignature (CallNonvirtualBase.JniTypeName)]
[JniTypeSignature (CallNonvirtualBase.JniTypeName, GenerateJavaPeer=false)]
public class CallNonvirtualBase : JavaObject
{
internal const string JniTypeName = "net/dot/jni/test/CallNonvirtualBase";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Java.InteropTests
{
[JniTypeSignature (CallNonvirtualDerived.JniTypeName)]
[JniTypeSignature (CallNonvirtualDerived.JniTypeName, GenerateJavaPeer=false)]
public class CallNonvirtualDerived : CallNonvirtualBase
{
internal new const string JniTypeName = "net/dot/jni/test/CallNonvirtualDerived";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Java.InteropTests
{
[JniTypeSignature (CallNonvirtualDerived2.JniTypeName)]
[JniTypeSignature (CallNonvirtualDerived2.JniTypeName, GenerateJavaPeer=false)]
public class CallNonvirtualDerived2 : CallNonvirtualDerived
{
internal new const string JniTypeName = "net/dot/jni/test/CallNonvirtualDerived2";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Java.InteropTests {

[JniTypeSignature (CallVirtualFromConstructorBase.JniTypeName)]
[JniTypeSignature (CallVirtualFromConstructorBase.JniTypeName, GenerateJavaPeer=false)]
public class CallVirtualFromConstructorBase : JavaObject {

internal const string JniTypeName = "net/dot/jni/test/CallVirtualFromConstructorBase";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Java.InteropTests
{
[JniTypeSignature (CallVirtualFromConstructorDerived.JniTypeName)]
[JniTypeSignature (CallVirtualFromConstructorDerived.JniTypeName, GenerateJavaPeer=false)]
public class CallVirtualFromConstructorDerived : CallVirtualFromConstructorBase {
new internal const string JniTypeName = "net/dot/jni/test/CallVirtualFromConstructorDerived";
static readonly JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (CallVirtualFromConstructorDerived));
Expand Down
2 changes: 1 addition & 1 deletion tests/Java.Interop-Tests/Java.Interop/GetThis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Java.InteropTests
{
[JniTypeSignature (GetThis.JniTypeName)]
[JniTypeSignature (GetThis.JniTypeName, GenerateJavaPeer=false)]
public class GetThis : JavaObject
{
internal const string JniTypeName = "net/dot/jni/test/GetThis";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ static void SetupLinks (JavaObjectArray<CrossReferenceBridge> array, out WeakRef
}
}

[JniTypeSignature (JniTypeName)]
[JniTypeSignature (JniTypeName, GenerateJavaPeer=false)]
public class CrossReferenceBridge : JavaObject {
internal const string JniTypeName = "net/dot/jni/test/CrossReferenceBridge";

Expand Down
18 changes: 13 additions & 5 deletions tests/Java.Interop-Tests/Java.Interop/JavaObjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ public void Ctor_Exceptions ()
var r = new JniObjectReference ();
Assert.Throws<ArgumentException> (() => new JavaObject (ref r, JniObjectReferenceOptions.CopyAndDispose));

// Note: This may break if/when JavaVM provides "default"
Assert.Throws<NotSupportedException> (() => new JavaObjectWithNoJavaPeer ());
#if __ANDROID__
Assert.Throws<Java.Lang.ClassNotFoundException> (() => 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<NotSupportedException> (() => new JavaObjectWithNoJavaPeer ());
Assert.Throws<JavaException> (() => new JavaObjectWithMissingJavaPeer ()).Dispose ();
#endif // !__ANDROID__
}
Expand Down Expand Up @@ -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;

Expand All @@ -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 ()
Expand Down
4 changes: 4 additions & 0 deletions tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
Expand Down
Loading

0 comments on commit 07c7300

Please sign in to comment.