Skip to content

Commit

Permalink
[build] Add support for JDK 21
Browse files Browse the repository at this point in the history
Context: 5bb0d24
Context: 4273e5c
Context: 0355acf
Context: dotnet/android#9651

Does dotnet/android build with JDK-21?  We don't know!  But in order
to answer that question, dotnet/java-interop needs to be able to
build under JDK-21; a'la 5bb0d24:

 1. [Install JDK-21][0]

 2. Run `dotnet build -t:Prepare`, overriding `$(JdksRoot)`:

        dotnet build -t:Prepare Java.Interop.sln -p:JdksRoot=/Library/Java/JavaVirtualMachines/microsoft-21.jdk/Contents/Home

 3. Build: `dotnet build`

Unfortunately, this *fails* for three reasons:

 1. `class-parse` crashes when processing `java.base.jmod`.
 2. `src/Java.Base` needs updates to bind JDK-21's `java.base.jmod`.
 3. On Linux and Windows, Gradle 8.1 and JDK-21 don't mix.

~~ class-parse ~~

This only impacts Debug builds of dotnet/java-interop, but:

	% dotnet "bin/Debug-net8.0/class-parse.dll" \
	  "$HOME/android-toolchain/jdk-21/jmods/java.base.jmod" \
	  "-o=obj/Debug-net8.0//mcw/api.xml"
	…
	Process terminated. Assertion failed.
	Unexpected number of method parameters in `Ljdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter$Instantiation;.get(Ljava/lang/Object;)Ljava/lang/Object;`: expected 1, got 0
	   at Xamarin.Android.Tools.Bytecode.MethodInfo.UpdateParametersFromMethodParametersAttribute(ParameterInfo[] parameters) in …/src/Xamarin.Android.Tools.Bytecode/Methods.cs:line 308
	   …

The assertion?

	var pinfo = methodParams.ParameterInfo;
	int startIndex = 0;
	while (startIndex < pinfo.Count &&
	    pinfo [startIndex].AccessFlags.HasFlag (.Synthetic))
	  startIndex++;
	Debug.Assert (parameters.Length == pinfo.Count - startIndex, …);

This is part of 4273e5c and 0355acf, attempting to "skip over" the
constructor parameters which non-static inner classes have which
contain the outer class instance:

	// Java
	class Outer {
	  /* non-static */ class Inner {
	    public Inner () { … }
	  }
	}

At the ABI boundary, the `Outer.Inner` constructor is actually:

	/* partial */ class Outer {
	  /* partial */ class Inner {
	    public Inner (Outer outer) { … }
	  }
	}

and we need to skip over the first parameter.

Which brings us to
`jdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter.Instantiation.get()`:

	% mkdir _x
	% unzip $HOME/android-toolchain/jdk-21/jmods/java.base.jmod -d _x
	% dotnet bin/Debug-net8.0/class-parse.dll --dump \
	  _x/classes/jdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter\$Instantiation.class
	…
		8: get (Ljava/lang/Object;)Ljava/lang/Object; Public, Bridge, Synthetic
			Code(6, Unknown[LineNumberTable](6), LocalVariableTableAttribute(LocalVariableTableEntry(Name='this', Descriptor='Ljdk/internal/org/objectweb/asm/commons/JSRInlinerAdapter$Instantiation;', StartPC=0, Index=0)))
			MethodParametersAttribute(MethodParameterInfo(Name='', AccessFlags=Final, Synthetic))

The parameter for `JSRInlinerAdapter.Instantiation.get(Object)` is
`Synthetic`, causing us to skip over it, which is why we have a
parameter mismatch.  The thing is, this parameter *shouldn't* be
skipped; the skipping is intended for *constructor* parameters!

Update the code so that the loop only occurs for constructors.
This allows `class-parse` to *not assert*, allowing `src/Java.Base`
to build.

~~ Gradle ~~

Via dotnet/android#9651, `gradle` fails when building
`tools/java-source-utils`, but only on Linux and Windows:

	"/mnt/vss/_work/1/s/xamarin-android/external/Java.Interop/build-tools/gradle/gradlew" -d --stacktrace --no-daemon -PjavaSourceVer=11 -PjavaTargetVer=11 jar
	…
	[org.gradle.internal.buildevents.BuildExceptionReporter]
	[org.gradle.internal.buildevents.BuildExceptionReporter] FAILURE: Build failed with an exception.
	[org.gradle.internal.buildevents.BuildExceptionReporter]
	[org.gradle.internal.buildevents.BuildExceptionReporter] * What went wrong:
	[org.gradle.internal.buildevents.BuildExceptionReporter] Could not open settings generic class cache for settings file '/mnt/vss/_work/1/s/xamarin-android/external/Java.Interop/tools/java-source-utils/settings.gradle' (/home/cloudtest/.gradle/caches/8.1.1/scripts/aiw0k2bokig45bv5yvkog3o3j).
	[org.gradle.internal.buildevents.BuildExceptionReporter] > BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 65
	[org.gradle.internal.buildevents.BuildExceptionReporter]
	[org.gradle.internal.buildevents.BuildExceptionReporter] * Try:
	[org.gradle.internal.buildevents.BuildExceptionReporter] > Run with --scan to get full insights.
	[org.gradle.internal.buildevents.BuildExceptionReporter]
	[org.gradle.internal.buildevents.BuildExceptionReporter] * Exception is:
	[org.gradle.internal.buildevents.BuildExceptionReporter] org.gradle.cache.CacheOpenException: Could not open settings generic class cache for settings file '/mnt/vss/_work/1/s/xamarin-android/external/Java.Interop/tools/java-source-utils/settings.gradle' (/home/cloudtest/.gradle/caches/8.1.1/scripts/aiw0k2bokig45bv5yvkog3o3j).
	[org.gradle.internal.buildevents.BuildExceptionReporter] 	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:91)
	[org.gradle.internal.buildevents.BuildExceptionReporter]  …
	[org.gradle.internal.buildevents.BuildExceptionReporter] Caused by: BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 65
	[org.gradle.internal.buildevents.BuildExceptionReporter] 	at org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler.compileScript(DefaultScriptCompilationHandler.java:147)
	[org.gradle.internal.buildevents.BuildExceptionReporter]  …
	[org.gradle.internal.buildevents.BuildExceptionReporter] Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 65
	[org.gradle.internal.buildevents.BuildExceptionReporter] 	at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:199)

🤔

As per the [Gradle Compatibility Matrix][1], Java 21 requires
Gradle 8.5 or later.  (No idea why this works on macOS…)

Bump to Gradle 8.12, whic is the current latest stable version.

~~ TODO ~~

While dotnet/java-interop now *builds* with JDK-21, unit tests don't
fully pass.  In particular,
`tests/Xamarin.Android.Tools.Bytecode-Tests` will need to be updated
because `javac` output has changed.  (Again.)

[0]: https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-21
[1]: https://docs.gradle.org/8.12/userguide/compatibility.html
  • Loading branch information
jonpryor committed Jan 7, 2025
1 parent 2c06b3c commit 0400cec
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
10 changes: 10 additions & 0 deletions src/Java.Base/Java.Lang/StringBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,15 @@

namespace Java.Lang {
partial class StringBuffer : IEnumerable, IEnumerable<char> {

#if JAVA_API_21
IAppendable? IAppendable.Append (char c) =>
Append (c);
IAppendable? IAppendable.Append (ICharSequence? s) =>
Append (s);
IAppendable? IAppendable.Append (ICharSequence? s, int a, int b) =>
Append (s, a, b);
#endif // JAVA_API_21

}
}
10 changes: 10 additions & 0 deletions src/Java.Base/Java.Lang/StringBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,15 @@

namespace Java.Lang {
partial class StringBuilder : IEnumerable, IEnumerable<char> {

#if JAVA_API_21
IAppendable? IAppendable.Append (char c) =>
Append (c);
IAppendable? IAppendable.Append (ICharSequence? s) =>
Append (s);
IAppendable? IAppendable.Append (ICharSequence? s, int a, int b) =>
Append (s, a, b);
#endif // JAVA_API_21

}
}
30 changes: 30 additions & 0 deletions src/Java.Base/Java.Lang/Thread.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Java.Lang {
partial class Thread {

#if JAVA_API_21
partial interface IBuilder {
partial class IOfPlatformInvoker {
IBuilder? IBuilder.InheritInheritableThreadLocals (bool value) =>
InheritInheritableThreadLocals (value);
IBuilder? IBuilder.Name (string? name) =>
Name (name);
IBuilder? IBuilder.Name (string? name, long v) =>
Name (name, v);
IBuilder? IBuilder.UncaughtExceptionHandler (IUncaughtExceptionHandler? u) =>
UncaughtExceptionHandler (u);
}
partial class IOfVirtualInvoker {
IBuilder? IBuilder.InheritInheritableThreadLocals (bool value) =>
InheritInheritableThreadLocals (value);
IBuilder? IBuilder.Name (string? name) =>
Name (name);
IBuilder? IBuilder.Name (string? name, long v) =>
Name (name, v);
IBuilder? IBuilder.UncaughtExceptionHandler (IUncaughtExceptionHandler? u) =>
UncaughtExceptionHandler (u);
}
}
#endif // JAVA_API_21

}
}
10 changes: 6 additions & 4 deletions src/Xamarin.Android.Tools.Bytecode/Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,16 @@ void UpdateParametersFromMethodParametersAttribute (ParameterInfo[] parameters)
MethodParameterAccessFlags.Final | MethodParameterAccessFlags.Synthetic;
var pinfo = methodParams.ParameterInfo;
int startIndex = 0;
while (startIndex < pinfo.Count &&
((pinfo [startIndex].AccessFlags & OuterThis1) == OuterThis1 ||
(pinfo [startIndex].AccessFlags & OuterThis2) == OuterThis2)) {
while (IsConstructor &&
startIndex < pinfo.Count &&
((pinfo [startIndex].AccessFlags & OuterThis1) == OuterThis1 ||
(pinfo [startIndex].AccessFlags & OuterThis2) == OuterThis2)) {
startIndex++;
}
Debug.Assert (
parameters.Length == pinfo.Count - startIndex,
$"Unexpected number of method parameters in `{DeclaringType.FullJniName}.{Name}{Descriptor}`: expected {parameters.Length}, got {pinfo.Count - startIndex}");
$"Unexpected number of method parameters in `{DeclaringType.FullJniName}.{Name}{Descriptor}`: expected {parameters.Length}, got {pinfo.Count - startIndex}! " +
$"pinfo.Count={pinfo.Count}, startIndex={startIndex}, pinfo={string.Join (", ", pinfo.Select (p => p.ToString ()))}");
int end = Math.Min (parameters.Length, pinfo.Count - startIndex);
for (int i = 0; i < end; ++i) {
var p = pinfo [i + startIndex];
Expand Down

0 comments on commit 0400cec

Please sign in to comment.