Skip to content

Commit ee47652

Browse files
authored
[build] Add support for JDK 21 (#1287)
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)` to refer to the JDK-21 installation directory: 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 JDK-21's `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: // "Equivalent" Java for JNI purposes /* partial */ class Outer { /* partial */ class Inner { public Inner (Outer outer) { … } // note added constructor parameter } } 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, which 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
1 parent fe00cef commit ee47652

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

src/Java.Base/Java.Lang/StringBuffer.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,15 @@
44

55
namespace Java.Lang {
66
partial class StringBuffer : IEnumerable, IEnumerable<char> {
7+
8+
#if JAVA_API_21
9+
IAppendable? IAppendable.Append (char c) =>
10+
Append (c);
11+
IAppendable? IAppendable.Append (ICharSequence? s) =>
12+
Append (s);
13+
IAppendable? IAppendable.Append (ICharSequence? s, int a, int b) =>
14+
Append (s, a, b);
15+
#endif // JAVA_API_21
16+
717
}
818
}

src/Java.Base/Java.Lang/StringBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,15 @@
44

55
namespace Java.Lang {
66
partial class StringBuilder : IEnumerable, IEnumerable<char> {
7+
8+
#if JAVA_API_21
9+
IAppendable? IAppendable.Append (char c) =>
10+
Append (c);
11+
IAppendable? IAppendable.Append (ICharSequence? s) =>
12+
Append (s);
13+
IAppendable? IAppendable.Append (ICharSequence? s, int a, int b) =>
14+
Append (s, a, b);
15+
#endif // JAVA_API_21
16+
717
}
818
}

src/Java.Base/Java.Lang/Thread.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Java.Lang {
2+
partial class Thread {
3+
4+
#if JAVA_API_21
5+
partial interface IBuilder {
6+
partial class IOfPlatformInvoker {
7+
IBuilder? IBuilder.InheritInheritableThreadLocals (bool value) =>
8+
InheritInheritableThreadLocals (value);
9+
IBuilder? IBuilder.Name (string? name) =>
10+
Name (name);
11+
IBuilder? IBuilder.Name (string? name, long v) =>
12+
Name (name, v);
13+
IBuilder? IBuilder.UncaughtExceptionHandler (IUncaughtExceptionHandler? u) =>
14+
UncaughtExceptionHandler (u);
15+
}
16+
partial class IOfVirtualInvoker {
17+
IBuilder? IBuilder.InheritInheritableThreadLocals (bool value) =>
18+
InheritInheritableThreadLocals (value);
19+
IBuilder? IBuilder.Name (string? name) =>
20+
Name (name);
21+
IBuilder? IBuilder.Name (string? name, long v) =>
22+
Name (name, v);
23+
IBuilder? IBuilder.UncaughtExceptionHandler (IUncaughtExceptionHandler? u) =>
24+
UncaughtExceptionHandler (u);
25+
}
26+
}
27+
#endif // JAVA_API_21
28+
29+
}
30+
}

src/Xamarin.Android.Tools.Bytecode/Methods.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,16 @@ void UpdateParametersFromMethodParametersAttribute (ParameterInfo[] parameters)
300300
MethodParameterAccessFlags.Final | MethodParameterAccessFlags.Synthetic;
301301
var pinfo = methodParams.ParameterInfo;
302302
int startIndex = 0;
303-
while (startIndex < pinfo.Count &&
304-
((pinfo [startIndex].AccessFlags & OuterThis1) == OuterThis1 ||
305-
(pinfo [startIndex].AccessFlags & OuterThis2) == OuterThis2)) {
303+
while (IsConstructor &&
304+
startIndex < pinfo.Count &&
305+
((pinfo [startIndex].AccessFlags & OuterThis1) == OuterThis1 ||
306+
(pinfo [startIndex].AccessFlags & OuterThis2) == OuterThis2)) {
306307
startIndex++;
307308
}
308309
Debug.Assert (
309310
parameters.Length == pinfo.Count - startIndex,
310-
$"Unexpected number of method parameters in `{DeclaringType.FullJniName}.{Name}{Descriptor}`: expected {parameters.Length}, got {pinfo.Count - startIndex}");
311+
$"Unexpected number of method parameters in `{DeclaringType.FullJniName}.{Name}{Descriptor}`: expected {parameters.Length}, got {pinfo.Count - startIndex}! " +
312+
$"pinfo.Count={pinfo.Count}, startIndex={startIndex}, pinfo={string.Join (", ", pinfo.Select (p => p.ToString ()))}");
311313
int end = Math.Min (parameters.Length, pinfo.Count - startIndex);
312314
for (int i = 0; i < end; ++i) {
313315
var p = pinfo [i + startIndex];

0 commit comments

Comments
 (0)