Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java.Interop] Typemap support for JavaObject & [JniTypeSignature] #1181

Merged
merged 7 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea we'd call this same method from xamarin-android as well?

So we don't have duplicate of this logic running around:

https://github.com/xamarin/xamarin-android/blob/2cbb5c98ebed30283d89e8434210415d4d5e9ded/src/Xamarin.Android.Build.Tasks/Utilities/XAJavaTypeScanner.cs#L80

{
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