Skip to content

Commit 77678eb

Browse files
authored
Bump to xamarin/Java.Interop/main@bbaeda6f (#7799)
Changes: dotnet/java-interop@9e0a469...bbaeda6 * dotnet/java-interop@bbaeda6f: [Java.Interop] Support Desugar + interface static methods (dotnet/java-interop#1077) Context: dotnet/java-interop@bbaeda6 Context: dotnet/java-interop@1f27ab5 Context: f6f11a5 [Desugaring][0] is the process of rewriting Java bytecode so that Java 8+ constructs can be used on Android pre-7.0 (API-24), as API-24 is the Android version which added native support for Java 8 features such as [interface default methods][1]. One of the implications of desugaring is that methods can "move"; consider this Java interface: package example; public interface StaticMethodsInterface { static int getValue() { return 3; } } Java.Interop bindings will attempt to invoke the `getValue().I` method on the type `example/StaticMethodsInterface`: public partial interface IStaticMethodsInterface : IJavaObject, IJavaPeerable { private static readonly JniPeerMembers _members = new XAPeerMembers ("example/StaticMethodsInterface", typeof (IStaticMethodsInterface), isInterface: true); static unsafe int Value { get { const string __id = "getValue.()I"; var __rm = _members.StaticMethods.InvokeInt32Method (__id, null); return __rm; } } } The problem is that, after Desugaring, the Java side *actually* looks like this: package example; public interface StaticMethodsInterface { } public class StaticMethodsInterface$-CC { public static int getValue() {return 3;} } Commits dotnet/java-interop@1f27ab55 and f6f11a5 added partial runtime support for this scenario via `AndroidTypeManager.GetStaticMethodFallbackTypesCore()`, which would attempt to lookup types with a `$-CC` suffix. While this was a good start, it wasn't ever actually *tested* end-to-end. Consequently, instead of *working*, this would instead cause the process to *abort*: JNI DETECTED ERROR IN APPLICATION: can't call static int example.StaticMethodsInterface$-CC.getValue() with class java.lang.Class<example.StaticMethodsInterface> in call to CallStaticIntMethodA from void crc….MainActivity.n_onCreate(android.os.Bundle) Oops. dotnet/java-interop@bbaeda6f improves our runtime support for invoking *`static`* methods on interfaces. Add a new `XASdkDeployTests.SupportDesugaringStaticInterfaceMethods()` test which performs an on-device, end-to-end invocation of a static method on a Java interface, to ensure that things *actually* work. *Note*: if `$(SupportedOSPlatformVersion)` is 24 or higher, this test will work even without dotnet/java-interop#1077, as Desugaring is *disabled* in that case. The test `JniPeerMembersTests.DesugarInterfaceStaticMethod()` added in dotnet/java-interop@bbaeda6f attempts to "fake" a Desugar environment for testing on Desktop Java, and this "fakery" doesn't work in the Android environment. Fix execution on Android by updating `AndroidRuntime.GetStaticMethodFallbackTypesCore()` to support type remapping (f6f11a5) -- which was overlooked/not considered -- such that the types returned are *after* calling `AndroidRuntime.GetReplacementTypeCore()`, which looks up `<replace-type/>` values. This allows us to remap `AndroidInterface` to `DesugarAndroidInterface$_CC`, allowing the `DesugarInterfaceStaticMethod()` test to pass. Update the `BuildTestJarFile` target so that it properly builds files that contain `$` on Unixy platforms. [0]: https://developer.android.com/studio/write/java8-support#library-desugaring [1]: https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
1 parent 5137dc7 commit 77678eb

File tree

8 files changed

+82
-4
lines changed

8 files changed

+82
-4
lines changed

build-tools/scripts/Jar.targets

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,16 @@
3535
<_DestDir>$(IntermediateOutputPath)__CreateTestJarFile-bin</_DestDir>
3636
<_AndroidJar>-bootclasspath "$(AndroidSdkDirectory)\platforms\android-$(_AndroidApiLevelName)\android.jar"</_AndroidJar>
3737
<_CP>-cp "$(_JavaInteropJarPath)"</_CP>
38+
<_JavacFilesResponse>$(IntermediateOutputPath)__javac_response.txt</_JavacFilesResponse>
3839
</PropertyGroup>
40+
<WriteLinesToFile
41+
File="$(_JavacFilesResponse)"
42+
Lines="@(_JavacSource)"
43+
Overwrite="True"
44+
/>
3945
<MakeDir Directories="$(_DestDir)" />
40-
<Exec Command="$(_Javac) $(_Targets) -d &quot;$(_DestDir)&quot; $(_AndroidJar) $(_CP) @(_JavacSource->'&quot;%(Identity)&quot;', ' ')" />
46+
<Exec Command="$(_Javac) $(_Targets) -d &quot;$(_DestDir)&quot; $(_AndroidJar) $(_CP) &quot;@$(_JavacFilesResponse)&quot;" />
47+
<Delete Files="$(_JavacFilesResponse)" />
4148
<Exec
4249
Command="$(_Jar) cf &quot;classes.jar&quot; ."
4350
WorkingDirectory="$(_DestDir)"

external/Java.Interop

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,12 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
314314
desugarType.Append ("Desugar").Append (name);
315315
}
316316

317+
var typeWithPrefix = desugarType.ToString ();
318+
var typeWithSuffix = $"{jniSimpleReference}$-CC";
319+
317320
return new[]{
318-
desugarType.ToString (),
319-
$"{jniSimpleReference}$-CC"
321+
GetReplacementTypeCore (typeWithPrefix) ?? typeWithPrefix,
322+
GetReplacementTypeCore (typeWithSuffix) ?? typeWithSuffix,
320323
};
321324
}
322325

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ static class ResourceData
2121
static Lazy<byte []> javaSourceTestInterface = new Lazy<byte []> (() => GetResourceData ("JavaSourceTestInterface.java"));
2222
static Lazy<byte []> remapActivityJava = new Lazy<byte []> (() => GetResourceData ("RemapActivity.java"));
2323
static Lazy<byte []> remapActivityXml = new Lazy<byte []> (() => GetResourceData ("RemapActivity.xml"));
24+
static Lazy<byte []> idmStaticMethodsInterface = new Lazy<byte []> (() => GetResourceData ("StaticMethodsInterface.java"));
2425

2526
public static byte[] JavaSourceJarTestJar => javaSourceJarTestJar.Value;
2627
public static byte[] JavaSourceJarTestSourcesJar => javaSourceJarTestSourcesJar.Value;
@@ -34,6 +35,7 @@ static class ResourceData
3435
public static string JavaSourceTestInterface => Encoding.ASCII.GetString (javaSourceTestInterface.Value);
3536
public static string RemapActivityJava => Encoding.UTF8.GetString (remapActivityJava.Value);
3637
public static string RemapActivityXml => Encoding.UTF8.GetString (remapActivityXml.Value);
38+
public static string IdmStaticMethodsInterface => Encoding.UTF8.GetString (idmStaticMethodsInterface.Value);
3739

3840
static byte[] GetResourceData (string name)
3941
{

tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
<EmbeddedResource Include="Resources\RemapActivity*">
3232
<LogicalName>%(FileName)%(Extension)</LogicalName>
3333
</EmbeddedResource>
34+
<EmbeddedResource Include="Resources\StaticMethodsInterface*">
35+
<LogicalName>%(FileName)%(Extension)</LogicalName>
36+
</EmbeddedResource>
3437
</ItemGroup>
3538

3639
<ItemGroup>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package example;
2+
3+
public interface StaticMethodsInterface {
4+
static int getValue() {
5+
return 3;
6+
}
7+
}

tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,59 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease)
155155
);
156156
}
157157

158+
[Test]
159+
public void SupportDesugaringStaticInterfaceMethods ()
160+
{
161+
AssertHasDevices ();
162+
if (!Builder.UseDotNet) {
163+
Assert.Ignore ("Skipping. Test not relevant under Classic.");
164+
}
165+
166+
var proj = new XASdkProject () {
167+
IsRelease = true,
168+
OtherBuildItems = {
169+
new AndroidItem.AndroidJavaSource ("StaticMethodsInterface.java") {
170+
Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false),
171+
TextContent = () => ResourceData.IdmStaticMethodsInterface,
172+
Metadata = {
173+
{ "Bind", "True" },
174+
},
175+
},
176+
},
177+
};
178+
179+
// Note: To properly test, Desugaring must be *enabled*, which requires that
180+
// `$(SupportedOSPlatformVersion)` be *less than* 23. 21 is currently the default,
181+
// but set this explicitly anyway just so that this implicit requirement is explicit.
182+
proj.SetProperty (proj.ReleaseProperties, "SupportedOSPlatformVersion", "21");
183+
184+
proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", @"
185+
Console.WriteLine ($""# jonp static interface default method invocation; IStaticMethodsInterface.Value={Example.IStaticMethodsInterface.Value}"");
186+
");
187+
proj.SetRuntimeIdentifier (DeviceAbi);
188+
var relativeProjDir = Path.Combine ("temp", TestName);
189+
var fullProjDir = Path.Combine (Root, relativeProjDir);
190+
TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir;
191+
var files = proj.Save ();
192+
proj.Populate (relativeProjDir, files);
193+
proj.CopyNuGetConfig (relativeProjDir);
194+
var dotnet = new DotNetCLI (proj, Path.Combine (fullProjDir, proj.ProjectFilePath));
195+
196+
Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
197+
Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed");
198+
199+
bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity",
200+
Path.Combine (fullProjDir, "logcat.log"));
201+
Assert.IsTrue (didLaunch, "MainActivity should have launched!");
202+
var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log"));
203+
204+
StringAssert.Contains (
205+
"IStaticMethodsInterface.Value=3",
206+
logcatOutput,
207+
"Was IStaticMethodsInterface.Value executed?"
208+
);
209+
}
210+
158211
[Test]
159212
[Category ("Debugger"), Category ("Node-4")]
160213
public void DotNetDebug ([Values("net6.0-android", "net7.0-android")] string targetFramework)

tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<replace-type
33
from="com/xamarin/interop/RenameClassBase1"
44
to="com/xamarin/interop/RenameClassBase2" />
5+
<replace-type
6+
from="com/xamarin/interop/AndroidInterface"
7+
to="com/xamarin/interop/DesugarAndroidInterface$_CC" />
58
<replace-method
69
source-type="java/lang/Object"
710
source-method-name="remappedToToString"

0 commit comments

Comments
 (0)