Skip to content
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
34 changes: 24 additions & 10 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ JniMethodInfo GetMethodInfo (string method, string signature)
return Members.JniPeerType.GetStaticMethod (method, signature);
}

#pragma warning disable CA1801
JniType GetMethodDeclaringType (JniMethodInfo method)
{
#if NET
if (method.StaticRedirect != null) {
return method.StaticRedirect;
}
#endif // NET
return Members.JniPeerType;
}
#pragma warning restore CA1801

#if NET
JniMethodInfo? FindInFallbackTypes (string method, string signature)
{
Expand All @@ -80,6 +92,8 @@ JniMethodInfo GetMethodInfo (string method, string signature)
continue;
}
if (t.TryGetStaticMethod (method, signature, out var m)) {
m.StaticRedirect = t;
t = null;
return m;
}
}
Expand All @@ -94,61 +108,61 @@ JniMethodInfo GetMethodInfo (string method, string signature)
public unsafe void InvokeVoidMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
JniEnvironment.StaticMethods.CallStaticVoidMethod (Members.JniPeerType.PeerReference, m, parameters);
JniEnvironment.StaticMethods.CallStaticVoidMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe bool InvokeBooleanMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticBooleanMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticBooleanMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe sbyte InvokeSByteMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticByteMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticByteMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe char InvokeCharMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticCharMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticCharMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe short InvokeInt16Method (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticShortMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticShortMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe int InvokeInt32Method (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticIntMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticIntMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe long InvokeInt64Method (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticLongMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticLongMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe float InvokeSingleMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticFloatMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticFloatMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe double InvokeDoubleMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticDoubleMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticDoubleMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}

public unsafe JniObjectReference InvokeObjectMethod (string encodedMember, JniArgumentValue* parameters)
{
var m = GetMethodInfo (encodedMember);
return JniEnvironment.StaticMethods.CallStaticObjectMethod (Members.JniPeerType.PeerReference, m, parameters);
return JniEnvironment.StaticMethods.CallStaticObjectMethod (GetMethodDeclaringType (m).PeerReference, m, parameters);
}
}}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Java.Interop/Java.Interop/JniPeerMembers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public partial class JniPeerMembers {
private bool isInterface;

public JniPeerMembers (string jniPeerTypeName, Type managedPeerType, bool isInterface)
: this (jniPeerTypeName, managedPeerType, checkManagedPeerType: true, isInterface: isInterface)
: this (jniPeerTypeName = GetReplacementType (jniPeerTypeName), managedPeerType, checkManagedPeerType: true, isInterface: isInterface)
{
}

Expand Down
8 changes: 3 additions & 5 deletions tests/Java.Interop-Tests/Java.Interop-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,12 @@
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\RenameClassBase1.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\RenameClassBase2.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\RenameClassDerived.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\AndroidInterface.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\DesugarAndroidInterface$_CC.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\SelfRegistration.java" />
<JavaInteropTestJar Include="$(MSBuildThisFileDirectory)java\com\xamarin\interop\TestType.java" />
</ItemGroup>

<Target Name="BuildInteropTestJar" BeforeTargets="Build" Inputs="@(JavaInteropTestJar)" Outputs="$(OutputPath)interop-test.jar">
<MakeDir Directories="$(IntermediateOutputPath)it-classes" />
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(IntermediateOutputPath)it-classes&quot; -classpath &quot;$(OutputPath)../$(Configuration)/java-interop.jar&quot; @(JavaInteropTestJar->'%(Identity)', ' ')" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)interop-test.jar&quot; -C &quot;$(IntermediateOutputPath)it-classes&quot; ." />
</Target>
<Import Project="Java.Interop-Tests.targets" />

</Project>
21 changes: 21 additions & 0 deletions tests/Java.Interop-Tests/Java.Interop-Tests.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project>

<Target Name="BuildInteropTestJar"
BeforeTargets="Build"
Inputs="@(JavaInteropTestJar)"
Outputs="$(OutputPath)interop-test.jar">
<MakeDir Directories="$(IntermediateOutputPath)it-classes" />
<ItemGroup>
<_Source Include="@(JavaInteropTestJar->Replace('%5c', '/'))" />
</ItemGroup>
<WriteLinesToFile
File="$(IntermediateOutputPath)_java_sources.txt"
Lines="@(_Source)"
Overwrite="True"
/>
<Exec Command="&quot;$(JavaCPath)&quot; $(_JavacSourceOptions) -d &quot;$(IntermediateOutputPath)it-classes&quot; -classpath &quot;$(OutputPath)../$(Configuration)/java-interop.jar&quot; &quot;@$(IntermediateOutputPath)_java_sources.txt&quot;" />
<Delete Files="$(IntermediateOutputPath)_java_sources.txt" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;$(OutputPath)interop-test.jar&quot; -C &quot;$(IntermediateOutputPath)it-classes&quot; ." />
</Target>

</Project>
4 changes: 2 additions & 2 deletions tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ IEnumerable<string> CreateSimpleReferencesEnumerator (Type type)
// "potentially non-existent" types ensures that we don't throw
// from places we don't want to internally throw.
return new[]{
desugarType,
$"{jniSimpleReference}$-CC"
$"{desugarType}$_CC", // For JniPeerMembersTests.DesugarInterfaceStaticMethod()
$"{jniSimpleReference}$-CC",
};
}

Expand Down
47 changes: 47 additions & 0 deletions tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ public void ReplaceInstanceMethodWithStaticMethod ()
// Shouldn't throw; should instead invoke ObjectHelper.getHashCodeHelper(Object)
o.remappedToStaticHashCode ();
}

#if !__ANDROID__
// Note: this test looks up a static method from one class, then
// calls `JNIEnv::CallStaticObjectMethod()` passing in a jclass
// for a *different* class.
//
// This appears to work on Desktop JVM.
//
// On Android, this will ABORT the app:
// JNI DETECTED ERROR IN APPLICATION: can't call static int com.xamarin.interop.DesugarAndroidInterface$_CC.getClassName() with class java.lang.Class<com.xamarin.interop.AndroidInterface>
// in call to CallStaticObjectMethodA
//
// *Fascinating* the differences that can appear between JVM implementations
[Test]
public unsafe void DoesTheJmethodNeedToMatchDeclaringType ()
{
var iface = new JniType ("com/xamarin/interop/AndroidInterface");
var desugar = new JniType ("com/xamarin/interop/DesugarAndroidInterface$_CC");
var m = desugar.GetStaticMethod ("getClassName", "()Ljava/lang/String;");

var r = JniEnvironment.StaticMethods.CallStaticObjectMethod (iface.PeerReference, m, null);
var s = JniEnvironment.Strings.ToString (ref r, JniObjectReferenceOptions.CopyAndDispose);
Assert.AreEqual ("DesugarAndroidInterface$-CC", s);
}
#endif // !__ANDROID__

[Test]
public void DesugarInterfaceStaticMethod ()
{
var s = IAndroidInterface.getClassName ();
Assert.AreEqual ("DesugarAndroidInterface$-CC", s);
}
#endif // NET
}

Expand Down Expand Up @@ -208,4 +240,19 @@ public override unsafe int hashCode ()
return base.hashCode ();
}
}

#if NET
[JniTypeSignature (JniTypeName)]
interface IAndroidInterface : IJavaPeerable {
internal const string JniTypeName = "com/xamarin/interop/AndroidInterface";

private static JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (IAndroidInterface), isInterface: true);

public static unsafe string getClassName ()
{
var s = _members.StaticMethods.InvokeObjectMethod ("getClassName.()Ljava/lang/String;", null);
return JniEnvironment.Strings.ToString (ref s, JniObjectReferenceOptions.CopyAndDispose);
}
}
#endif // NET
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.xamarin.interop;

// When Android Desugaring is enabled -- the default when targeting API-25 and earlier --
// certain Java constructs result in Java bytecode rewriting.
// Interface static methods are *moved* into $-CC types.
public interface AndroidInterface {

// When Desugaring is enabled, this is moved to `AndroidInterface$-CC.getClassName()`,
// and the original `AndroidInterface.getClassName()` *no longer exists*.

// public static String getClassName() {
// return "AndroidInterface";
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.xamarin.interop;

public class DesugarAndroidInterface$_CC {

public static String getClassName() {
return "DesugarAndroidInterface$-CC";
}
}