Skip to content

Commit

Permalink
Bump to xamarin/Java.Interop/main@bbaeda6f (dotnet#7799)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jonpryor authored Feb 23, 2023
1 parent 5137dc7 commit 77678eb
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 4 deletions.
9 changes: 8 additions & 1 deletion build-tools/scripts/Jar.targets
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@
<_DestDir>$(IntermediateOutputPath)__CreateTestJarFile-bin</_DestDir>
<_AndroidJar>-bootclasspath "$(AndroidSdkDirectory)\platforms\android-$(_AndroidApiLevelName)\android.jar"</_AndroidJar>
<_CP>-cp "$(_JavaInteropJarPath)"</_CP>
<_JavacFilesResponse>$(IntermediateOutputPath)__javac_response.txt</_JavacFilesResponse>
</PropertyGroup>
<WriteLinesToFile
File="$(_JavacFilesResponse)"
Lines="@(_JavacSource)"
Overwrite="True"
/>
<MakeDir Directories="$(_DestDir)" />
<Exec Command="$(_Javac) $(_Targets) -d &quot;$(_DestDir)&quot; $(_AndroidJar) $(_CP) @(_JavacSource->'&quot;%(Identity)&quot;', ' ')" />
<Exec Command="$(_Javac) $(_Targets) -d &quot;$(_DestDir)&quot; $(_AndroidJar) $(_CP) &quot;@$(_JavacFilesResponse)&quot;" />
<Delete Files="$(_JavacFilesResponse)" />
<Exec
Command="$(_Jar) cf &quot;classes.jar&quot; ."
WorkingDirectory="$(_DestDir)"
Expand Down
2 changes: 1 addition & 1 deletion external/Java.Interop
7 changes: 5 additions & 2 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,12 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
desugarType.Append ("Desugar").Append (name);
}

var typeWithPrefix = desugarType.ToString ();
var typeWithSuffix = $"{jniSimpleReference}$-CC";

return new[]{
desugarType.ToString (),
$"{jniSimpleReference}$-CC"
GetReplacementTypeCore (typeWithPrefix) ?? typeWithPrefix,
GetReplacementTypeCore (typeWithSuffix) ?? typeWithSuffix,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static class ResourceData
static Lazy<byte []> javaSourceTestInterface = new Lazy<byte []> (() => GetResourceData ("JavaSourceTestInterface.java"));
static Lazy<byte []> remapActivityJava = new Lazy<byte []> (() => GetResourceData ("RemapActivity.java"));
static Lazy<byte []> remapActivityXml = new Lazy<byte []> (() => GetResourceData ("RemapActivity.xml"));
static Lazy<byte []> idmStaticMethodsInterface = new Lazy<byte []> (() => GetResourceData ("StaticMethodsInterface.java"));

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

static byte[] GetResourceData (string name)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
<EmbeddedResource Include="Resources\RemapActivity*">
<LogicalName>%(FileName)%(Extension)</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\StaticMethodsInterface*">
<LogicalName>%(FileName)%(Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package example;

public interface StaticMethodsInterface {
static int getValue() {
return 3;
}
}
53 changes: 53 additions & 0 deletions tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,59 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease)
);
}

[Test]
public void SupportDesugaringStaticInterfaceMethods ()
{
AssertHasDevices ();
if (!Builder.UseDotNet) {
Assert.Ignore ("Skipping. Test not relevant under Classic.");
}

var proj = new XASdkProject () {
IsRelease = true,
OtherBuildItems = {
new AndroidItem.AndroidJavaSource ("StaticMethodsInterface.java") {
Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false),
TextContent = () => ResourceData.IdmStaticMethodsInterface,
Metadata = {
{ "Bind", "True" },
},
},
},
};

// Note: To properly test, Desugaring must be *enabled*, which requires that
// `$(SupportedOSPlatformVersion)` be *less than* 23. 21 is currently the default,
// but set this explicitly anyway just so that this implicit requirement is explicit.
proj.SetProperty (proj.ReleaseProperties, "SupportedOSPlatformVersion", "21");

proj.MainActivity = proj.DefaultMainActivity.Replace ("//${AFTER_ONCREATE}", @"
Console.WriteLine ($""# jonp static interface default method invocation; IStaticMethodsInterface.Value={Example.IStaticMethodsInterface.Value}"");
");
proj.SetRuntimeIdentifier (DeviceAbi);
var relativeProjDir = Path.Combine ("temp", TestName);
var fullProjDir = Path.Combine (Root, relativeProjDir);
TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir;
var files = proj.Save ();
proj.Populate (relativeProjDir, files);
proj.CopyNuGetConfig (relativeProjDir);
var dotnet = new DotNetCLI (proj, Path.Combine (fullProjDir, proj.ProjectFilePath));

Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed");
Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed");

bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity",
Path.Combine (fullProjDir, "logcat.log"));
Assert.IsTrue (didLaunch, "MainActivity should have launched!");
var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log"));

StringAssert.Contains (
"IStaticMethodsInterface.Value=3",
logcatOutput,
"Was IStaticMethodsInterface.Value executed?"
);
}

[Test]
[Category ("Debugger"), Category ("Node-4")]
public void DotNetDebug ([Values("net6.0-android", "net7.0-android")] string targetFramework)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<replace-type
from="com/xamarin/interop/RenameClassBase1"
to="com/xamarin/interop/RenameClassBase2" />
<replace-type
from="com/xamarin/interop/AndroidInterface"
to="com/xamarin/interop/DesugarAndroidInterface$_CC" />
<replace-method
source-type="java/lang/Object"
source-method-name="remappedToToString"
Expand Down

0 comments on commit 77678eb

Please sign in to comment.