Skip to content

Commit 25c72f4

Browse files
committed
[GR-39165] [GR-39784] Enable more Native Image tests on JDK 19
PullRequest: graal/12234
2 parents 08ea173 + d603e08 commit 25c72f4

File tree

9 files changed

+127
-12
lines changed

9 files changed

+127
-12
lines changed

substratevm/ci.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
linux_amd64_jdk11 + gate("build-ce", "build,checkstubs,helloworld,test,nativeimagehelp,muslcbuild,debuginfotest") + maven + svm_unittest + t("35:00") + musl_toolchain + gdb("10.2"),
8181
linux_amd64_jdk11 + gate("modules-basic", "build,hellomodule,test") + maven + svm_unittest + t("30:00"),
8282
linux_amd64_jdk17 + gate("style-fullbuild", "style,fullbuild,helloworld,test,svmjunit,debuginfotest") + common.eclipse + common.jdt + maven + jsonschema + svm_unittest + t("50:00") + mx_build_exploded + gdb("10.2"),
83-
linux_amd64_jdk19 + gate("build-ce", "build,helloworld") + maven + svm_unittest + t("35:00"),
83+
linux_amd64_jdk19 + gate("basics", "build,helloworld,test,svmjunit") + svm_unittest + t("55:00"),
8484
windows_jdk17 + gate("basics", "build,helloworld,test,svmjunit") + svm_unittest + t("1:30:00"),
8585
windows_jdk17 + gate("basics-quickbuild", "build,helloworld_quickbuild,test_quickbuild,svmjunit_quickbuild") + svm_unittest + t("1:30:00"),
8686
],

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
8080
import com.oracle.svm.core.jdk.JDK11OrEarlier;
8181
import com.oracle.svm.core.jdk.JDK17OrLater;
82+
import com.oracle.svm.core.jdk.JDK19OrLater;
8283
import com.oracle.svm.core.jdk.Resources;
8384
import com.oracle.svm.core.meta.SharedType;
8485
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder;
@@ -1726,6 +1727,17 @@ final class Target_jdk_internal_reflect_ReflectionFactory {
17261727
public static ReflectionFactory getReflectionFactory() {
17271728
return soleInstance;
17281729
}
1730+
1731+
/**
1732+
* Do not use the field handle based field accessor but the one based on unsafe. It takes effect
1733+
* when {@code Target_java_lang_reflect_Field#fieldAccessorField#fieldAccessor} is recomputed at
1734+
* runtime. See also GR-39586.
1735+
*/
1736+
@TargetElement(onlyWith = JDK19OrLater.class)
1737+
@Substitute
1738+
static boolean useFieldHandleAccessor() {
1739+
return false;
1740+
}
17291741
}
17301742

17311743
@TargetClass(className = "java.lang.Class", innerClass = "EnclosingMethodInfo")

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,13 @@ private int hashCodeSubst() {
9999
}
100100

101101
@Substitute
102-
@TargetElement(name = "wait", onlyWith = NotLoomJDK.class)
102+
@TargetElement(name = "wait", onlyWith = JDK17OrEarlier.class)
103103
private void waitSubst(long timeoutMillis) throws InterruptedException {
104104
MonitorSupport.singleton().wait(this, timeoutMillis);
105105
}
106106

107107
@Substitute
108-
@TargetElement(name = "wait0", onlyWith = LoomJDK.class)
108+
@TargetElement(name = "wait0", onlyWith = JDK19OrLater.class)
109109
private void waitSubstLoom(long timeoutMillis) throws InterruptedException {
110110
MonitorSupport.singleton().wait(this, timeoutMillis);
111111
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/JavaLangThreadGroupSubstitutions.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
*/
2525
package com.oracle.svm.core.thread;
2626

27+
import java.lang.ref.WeakReference;
2728
import java.util.Arrays;
2829

30+
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
2931
import org.graalvm.nativeimage.Platform;
3032
import org.graalvm.nativeimage.Platforms;
3133

@@ -40,7 +42,9 @@
4042
import com.oracle.svm.core.annotate.Uninterruptible;
4143
import com.oracle.svm.core.heap.Heap;
4244
import com.oracle.svm.core.jdk.JDK17OrEarlier;
45+
import com.oracle.svm.core.jdk.JDK19OrLater;
4346
import com.oracle.svm.core.jdk.UninterruptibleUtils;
47+
import com.oracle.svm.util.ReflectionUtil;
4448

4549
import jdk.vm.ci.meta.MetaAccessProvider;
4650
import jdk.vm.ci.meta.ResolvedJavaField;
@@ -75,6 +79,16 @@ final class Target_java_lang_ThreadGroup {
7579
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ThreadGroupGroupsRecomputation.class, disableCaching = true)//
7680
private ThreadGroup[] groups;
7781

82+
/*
83+
* All ThreadGroups in the image heap are strong and will be stored in ThreadGroup.groups.
84+
*/
85+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)//
86+
@TargetElement(onlyWith = JDK19OrLater.class)//
87+
private int nweaks;
88+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)//
89+
@TargetElement(onlyWith = JDK19OrLater.class)//
90+
private WeakReference<ThreadGroup>[] weaks;
91+
7892
@Inject @InjectAccessors(ThreadGroupIdAccessor.class) //
7993
public long id;
8094

@@ -148,6 +162,32 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original,
148162
return ThreadStatus.NEW;
149163
}
150164
}
165+
166+
}
167+
168+
@Platforms(Platform.HOSTED_ONLY.class)
169+
class ThreadHolderRecomputation implements RecomputeFieldValue.CustomFieldValueTransformer {
170+
@Override
171+
public RecomputeFieldValue.ValueAvailability valueAvailability() {
172+
return RecomputeFieldValue.ValueAvailability.BeforeAnalysis;
173+
}
174+
175+
@Override
176+
public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) {
177+
assert JavaVersionUtil.JAVA_SPEC >= 19 : "ThreadHolder only exist on JDK 19+";
178+
int threadStatus = ReflectionUtil.readField(ReflectionUtil.lookupClass(false, "java.lang.Thread$FieldHolder"), "threadStatus", receiver);
179+
if (threadStatus == ThreadStatus.TERMINATED) {
180+
return ThreadStatus.TERMINATED;
181+
}
182+
assert threadStatus == ThreadStatus.NEW : "All threads are in NEW state during image generation";
183+
if (receiver == ReflectionUtil.readField(Thread.class, "holder", PlatformThreads.singleton().mainThread)) {
184+
/* The main thread is recomputed as running. */
185+
return ThreadStatus.RUNNABLE;
186+
} else {
187+
/* All other threads remain unstarted. */
188+
return ThreadStatus.NEW;
189+
}
190+
}
151191
}
152192

153193
@Platforms(Platform.HOSTED_ONLY.class)

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/SubstrateVirtualThread.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.concurrent.ScheduledThreadPoolExecutor;
3636
import java.util.concurrent.TimeUnit;
3737

38+
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
3839
import org.graalvm.nativeimage.IsolateThread;
3940

4041
import com.oracle.svm.core.annotate.Alias;
@@ -45,6 +46,7 @@
4546
import com.oracle.svm.core.monitor.MonitorSupport;
4647
import com.oracle.svm.core.stack.JavaFrameAnchor;
4748
import com.oracle.svm.core.stack.JavaFrameAnchors;
49+
import com.oracle.svm.core.util.VMError;
4850

4951
import jdk.internal.misc.Unsafe;
5052

@@ -431,6 +433,9 @@ boolean joinNanos(long nanos) throws InterruptedException {
431433
}
432434

433435
private Object interruptLock() {
436+
if (JavaVersionUtil.JAVA_SPEC >= 19) {
437+
throw VMError.unsupportedFeature("Loom is not yet supported on JDK 19");
438+
}
434439
return JavaThreads.toTarget(this).blockerLock;
435440
}
436441

@@ -466,6 +471,9 @@ private void releaseInterruptLockAndSwitchBack(Object token) {
466471

467472
@Override
468473
public void interrupt() {
474+
if (JavaVersionUtil.JAVA_SPEC >= 19) {
475+
throw VMError.unsupportedFeature("Loom is not yet supported on JDK 19");
476+
}
469477
if (Thread.currentThread() != this) {
470478
Object token = switchToCarrierAndAcquireInterruptLock();
471479
try {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Target_java_lang_Thread.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,23 @@ public boolean isInterrupted() {
406406
}
407407

408408
@Substitute
409+
@TargetElement(onlyWith = JDK17OrEarlier.class)
409410
public static boolean interrupted() {
410411
return JavaThreads.getAndClearInterrupt(Thread.currentThread());
411412
}
412413

414+
@Substitute
415+
@TargetElement(onlyWith = JDK19OrLater.class)
416+
void clearInterrupt() {
417+
getAndClearInterrupt();
418+
}
419+
420+
@Substitute
421+
@TargetElement(onlyWith = JDK19OrLater.class)
422+
boolean getAndClearInterrupt() {
423+
return JavaThreads.getAndClearInterrupt(SubstrateUtil.cast(this, Thread.class));
424+
}
425+
413426
@Delete
414427
@TargetElement(onlyWith = JDK11OrEarlier.class)
415428
private native boolean isInterrupted(boolean clearInterrupted);
@@ -618,6 +631,7 @@ final class Target_java_lang_Thread_FieldHolder {
618631
@Alias //
619632
boolean daemon;
620633
@Alias //
634+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ThreadHolderRecomputation.class) //
621635
volatile int threadStatus;
622636

623637
Target_java_lang_Thread_FieldHolder(

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME;
3030

3131
import java.io.PrintWriter;
32-
import java.lang.reflect.Proxy;
3332
import java.util.ArrayList;
3433
import java.util.Arrays;
3534
import java.util.Comparator;
@@ -41,7 +40,6 @@
4140
import org.graalvm.compiler.graph.Node;
4241
import org.graalvm.compiler.options.OptionValues;
4342
import org.graalvm.compiler.phases.util.Providers;
44-
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
4543

4644
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
4745
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
@@ -188,7 +186,6 @@ public void afterAnalysis(AfterAnalysisAccess access) {
188186
if (ClassInitializationOptions.AssertInitializationSpecifiedForAllClasses.getValue()) {
189187
List<String> unspecifiedClasses = classInitializationSupport.classesWithKind(RUN_TIME).stream()
190188
.filter(c -> classInitializationSupport.specifiedInitKindFor(c) == null)
191-
.filter(c -> JavaVersionUtil.JAVA_SPEC < 19 || !Proxy.isProxyClass(c))
192189
.map(Class::getTypeName)
193190
.collect(Collectors.toList());
194191
if (!unspecifiedClasses.isEmpty()) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ProvenSafeClassInitializationSupport.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,18 @@
2828
import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME;
2929

3030
import java.lang.reflect.Field;
31+
import java.lang.reflect.Proxy;
32+
import java.util.Arrays;
3133
import java.util.Collections;
3234
import java.util.HashSet;
3335
import java.util.Map;
3436
import java.util.Set;
3537
import java.util.stream.Collectors;
38+
import java.util.stream.Stream;
39+
40+
import org.graalvm.collections.EconomicSet;
41+
import org.graalvm.compiler.debug.GraalError;
42+
import org.graalvm.compiler.java.LambdaUtils;
3643

3744
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
3845
import com.oracle.graal.pointsto.meta.AnalysisType;
@@ -323,6 +330,13 @@ InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize,
323330
}
324331
superResult = superResult.max(processInterfaces(clazz, memoize, earlyClassInitializerAnalyzedClasses));
325332

333+
if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) {
334+
if (!Proxy.isProxyClass(clazz) || !implementsRunTimeInitializedInterface(clazz)) {
335+
forceInitializeHosted(clazz, "proxy/lambda classes with interfaces initialized at build time are also initialized at build time", false);
336+
return InitKind.BUILD_TIME;
337+
}
338+
}
339+
326340
if (memoize && superResult != InitKind.RUN_TIME && clazzResult == InitKind.RUN_TIME && canBeProvenSafe(clazz)) {
327341
/*
328342
* Check if the class initializer is side-effect free using a simple intraprocedural
@@ -364,6 +378,32 @@ InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize,
364378
return result;
365379
}
366380

381+
/**
382+
* Interfaces must be initialized at run time if any interface method references a type that
383+
* {@linkplain #shouldInitializeAtRuntime should be initialized at run time}.
384+
*/
385+
private boolean implementsRunTimeInitializedInterface(Class<?> clazz) {
386+
EconomicSet<Class<?>> visited = EconomicSet.create();
387+
return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited));
388+
}
389+
390+
private boolean shouldInitializeInterfaceAtRunTime(Class<?> clazz, EconomicSet<Class<?>> visited) {
391+
if (visited.contains(clazz)) {
392+
// already visited
393+
return false;
394+
}
395+
GraalError.guarantee(clazz.isInterface(), "expected interface, got %s", clazz);
396+
visited.add(clazz);
397+
var methods = Arrays.stream(clazz.getDeclaredMethods());
398+
var types = methods.flatMap(m -> Stream.concat(Stream.of(m.getReturnType()), Arrays.stream(m.getParameterTypes())));
399+
// check for initialize at run time
400+
if (types.anyMatch(this::shouldInitializeAtRuntime)) {
401+
return true;
402+
}
403+
// iterate super interfaces recursively
404+
return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited));
405+
}
406+
367407
private InitKind processInterfaces(Class<?> clazz, boolean memoizeEager, Set<Class<?>> earlyClassInitializerAnalyzedClasses) {
368408
/*
369409
* Note that we do not call computeInitKindForClass(clazz) on purpose: if clazz is the root

substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/SecurityServiceTest.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.security.Provider;
2929
import java.security.Security;
3030

31+
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
3132
import org.graalvm.nativeimage.hosted.Feature;
3233
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
3334
import org.graalvm.nativeimage.hosted.RuntimeReflection;
@@ -80,12 +81,15 @@ public void testUnknownSecurityServices() throws Exception {
8081

8182
@Test
8283
public void testAutomaticSecurityServiceRegistration() {
83-
try {
84-
JCACompliantNoOpService service = JCACompliantNoOpService.getInstance("no-op-algo-two");
85-
Assert.assertNotNull("No service instance was created", service);
86-
Assert.assertThat("Unexpected service implementtation class", service, CoreMatchers.instanceOf(JcaCompliantNoOpServiceImpl.class));
87-
} catch (NoSuchAlgorithmException e) {
88-
Assert.fail("Failed to fetch noop service with exception: " + e);
84+
if (JavaVersionUtil.JAVA_SPEC < 19) {
85+
// Does not work on JDK 19 for yet unknown reasons (GR-39827)
86+
try {
87+
JCACompliantNoOpService service = JCACompliantNoOpService.getInstance("no-op-algo-two");
88+
Assert.assertNotNull("No service instance was created", service);
89+
Assert.assertThat("Unexpected service implementtation class", service, CoreMatchers.instanceOf(JcaCompliantNoOpServiceImpl.class));
90+
} catch (NoSuchAlgorithmException e) {
91+
Assert.fail("Failed to fetch noop service with exception: " + e);
92+
}
8993
}
9094
}
9195

0 commit comments

Comments
 (0)