Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion substratevm/ci.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
linux_amd64_jdk11 + gate("build-ce", "build,checkstubs,helloworld,test,nativeimagehelp,muslcbuild,debuginfotest") + maven + svm_unittest + t("35:00") + musl_toolchain + gdb("10.2"),
linux_amd64_jdk11 + gate("modules-basic", "build,hellomodule,test") + maven + svm_unittest + t("30:00"),
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"),
linux_amd64_jdk19 + gate("build-ce", "build,helloworld") + maven + svm_unittest + t("35:00"),
linux_amd64_jdk19 + gate("basics", "build,helloworld,test,svmjunit") + svm_unittest + t("55:00"),
windows_jdk17 + gate("basics", "build,helloworld,test,svmjunit") + svm_unittest + t("1:30:00"),
windows_jdk17 + gate("basics-quickbuild", "build,helloworld_quickbuild,test_quickbuild,svmjunit_quickbuild") + svm_unittest + t("1:30:00"),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
import com.oracle.svm.core.jdk.JDK11OrEarlier;
import com.oracle.svm.core.jdk.JDK17OrLater;
import com.oracle.svm.core.jdk.JDK19OrLater;
import com.oracle.svm.core.jdk.Resources;
import com.oracle.svm.core.meta.SharedType;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder;
Expand Down Expand Up @@ -1726,6 +1727,17 @@ final class Target_jdk_internal_reflect_ReflectionFactory {
public static ReflectionFactory getReflectionFactory() {
return soleInstance;
}

/**
* Do not use the field handle based field accessor but the one based on unsafe. It takes effect
* when {@code Target_java_lang_reflect_Field#fieldAccessorField#fieldAccessor} is recomputed at
* runtime. See also GR-39586.
*/
@TargetElement(onlyWith = JDK19OrLater.class)
@Substitute
static boolean useFieldHandleAccessor() {
return false;
}
}

@TargetClass(className = "java.lang.Class", innerClass = "EnclosingMethodInfo")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ private int hashCodeSubst() {
}

@Substitute
@TargetElement(name = "wait", onlyWith = NotLoomJDK.class)
@TargetElement(name = "wait", onlyWith = JDK17OrEarlier.class)
private void waitSubst(long timeoutMillis) throws InterruptedException {
MonitorSupport.singleton().wait(this, timeoutMillis);
}

@Substitute
@TargetElement(name = "wait0", onlyWith = LoomJDK.class)
@TargetElement(name = "wait0", onlyWith = JDK19OrLater.class)
private void waitSubstLoom(long timeoutMillis) throws InterruptedException {
MonitorSupport.singleton().wait(this, timeoutMillis);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
*/
package com.oracle.svm.core.thread;

import java.lang.ref.WeakReference;
import java.util.Arrays;

import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

Expand All @@ -40,7 +42,9 @@
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.jdk.JDK17OrEarlier;
import com.oracle.svm.core.jdk.JDK19OrLater;
import com.oracle.svm.core.jdk.UninterruptibleUtils;
import com.oracle.svm.util.ReflectionUtil;

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

/*
* All ThreadGroups in the image heap are strong and will be stored in ThreadGroup.groups.
*/
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)//
@TargetElement(onlyWith = JDK19OrLater.class)//
private int nweaks;
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)//
@TargetElement(onlyWith = JDK19OrLater.class)//
private WeakReference<ThreadGroup>[] weaks;

@Inject @InjectAccessors(ThreadGroupIdAccessor.class) //
public long id;

Expand Down Expand Up @@ -148,6 +162,32 @@ public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original,
return ThreadStatus.NEW;
}
}

}

@Platforms(Platform.HOSTED_ONLY.class)
class ThreadHolderRecomputation implements RecomputeFieldValue.CustomFieldValueTransformer {
@Override
public RecomputeFieldValue.ValueAvailability valueAvailability() {
return RecomputeFieldValue.ValueAvailability.BeforeAnalysis;
}

@Override
public Object transform(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver, Object originalValue) {
assert JavaVersionUtil.JAVA_SPEC >= 19 : "ThreadHolder only exist on JDK 19+";
int threadStatus = ReflectionUtil.readField(ReflectionUtil.lookupClass(false, "java.lang.Thread$FieldHolder"), "threadStatus", receiver);
if (threadStatus == ThreadStatus.TERMINATED) {
return ThreadStatus.TERMINATED;
}
assert threadStatus == ThreadStatus.NEW : "All threads are in NEW state during image generation";
if (receiver == ReflectionUtil.readField(Thread.class, "holder", PlatformThreads.singleton().mainThread)) {
/* The main thread is recomputed as running. */
return ThreadStatus.RUNNABLE;
} else {
/* All other threads remain unstarted. */
return ThreadStatus.NEW;
}
}
}

@Platforms(Platform.HOSTED_ONLY.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.IsolateThread;

import com.oracle.svm.core.annotate.Alias;
Expand All @@ -45,6 +46,7 @@
import com.oracle.svm.core.monitor.MonitorSupport;
import com.oracle.svm.core.stack.JavaFrameAnchor;
import com.oracle.svm.core.stack.JavaFrameAnchors;
import com.oracle.svm.core.util.VMError;

import jdk.internal.misc.Unsafe;

Expand Down Expand Up @@ -431,6 +433,9 @@ boolean joinNanos(long nanos) throws InterruptedException {
}

private Object interruptLock() {
if (JavaVersionUtil.JAVA_SPEC >= 19) {
throw VMError.unsupportedFeature("Loom is not yet supported on JDK 19");
}
return JavaThreads.toTarget(this).blockerLock;
}

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

@Override
public void interrupt() {
if (JavaVersionUtil.JAVA_SPEC >= 19) {
throw VMError.unsupportedFeature("Loom is not yet supported on JDK 19");
}
if (Thread.currentThread() != this) {
Object token = switchToCarrierAndAcquireInterruptLock();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,23 @@ public boolean isInterrupted() {
}

@Substitute
@TargetElement(onlyWith = JDK17OrEarlier.class)
public static boolean interrupted() {
return JavaThreads.getAndClearInterrupt(Thread.currentThread());
}

@Substitute
@TargetElement(onlyWith = JDK19OrLater.class)
void clearInterrupt() {
getAndClearInterrupt();
}

@Substitute
@TargetElement(onlyWith = JDK19OrLater.class)
boolean getAndClearInterrupt() {
return JavaThreads.getAndClearInterrupt(SubstrateUtil.cast(this, Thread.class));
}

@Delete
@TargetElement(onlyWith = JDK11OrEarlier.class)
private native boolean isInterrupted(boolean clearInterrupted);
Expand Down Expand Up @@ -618,6 +631,7 @@ final class Target_java_lang_Thread_FieldHolder {
@Alias //
boolean daemon;
@Alias //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ThreadHolderRecomputation.class) //
volatile int threadStatus;

Target_java_lang_Thread_FieldHolder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME;

import java.io.PrintWriter;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
Expand All @@ -41,7 +40,6 @@
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;

import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
Expand Down Expand Up @@ -188,7 +186,6 @@ public void afterAnalysis(AfterAnalysisAccess access) {
if (ClassInitializationOptions.AssertInitializationSpecifiedForAllClasses.getValue()) {
List<String> unspecifiedClasses = classInitializationSupport.classesWithKind(RUN_TIME).stream()
.filter(c -> classInitializationSupport.specifiedInitKindFor(c) == null)
.filter(c -> JavaVersionUtil.JAVA_SPEC < 19 || !Proxy.isProxyClass(c))
.map(Class::getTypeName)
.collect(Collectors.toList());
if (!unspecifiedClasses.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@
import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.graalvm.collections.EconomicSet;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.java.LambdaUtils;

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

if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) {
if (!Proxy.isProxyClass(clazz) || !implementsRunTimeInitializedInterface(clazz)) {
forceInitializeHosted(clazz, "proxy/lambda classes with interfaces initialized at build time are also initialized at build time", false);
return InitKind.BUILD_TIME;
}
}

if (memoize && superResult != InitKind.RUN_TIME && clazzResult == InitKind.RUN_TIME && canBeProvenSafe(clazz)) {
/*
* Check if the class initializer is side-effect free using a simple intraprocedural
Expand Down Expand Up @@ -364,6 +378,32 @@ InitKind computeInitKindAndMaybeInitializeClass(Class<?> clazz, boolean memoize,
return result;
}

/**
* Interfaces must be initialized at run time if any interface method references a type that
* {@linkplain #shouldInitializeAtRuntime should be initialized at run time}.
*/
private boolean implementsRunTimeInitializedInterface(Class<?> clazz) {
EconomicSet<Class<?>> visited = EconomicSet.create();
return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited));
}

private boolean shouldInitializeInterfaceAtRunTime(Class<?> clazz, EconomicSet<Class<?>> visited) {
if (visited.contains(clazz)) {
// already visited
return false;
}
GraalError.guarantee(clazz.isInterface(), "expected interface, got %s", clazz);
visited.add(clazz);
var methods = Arrays.stream(clazz.getDeclaredMethods());
var types = methods.flatMap(m -> Stream.concat(Stream.of(m.getReturnType()), Arrays.stream(m.getParameterTypes())));
// check for initialize at run time
if (types.anyMatch(this::shouldInitializeAtRuntime)) {
return true;
}
// iterate super interfaces recursively
return Arrays.stream(clazz.getInterfaces()).anyMatch(c -> shouldInitializeInterfaceAtRunTime(c, visited));
}

private InitKind processInterfaces(Class<?> clazz, boolean memoizeEager, Set<Class<?>> earlyClassInitializerAnalyzedClasses) {
/*
* Note that we do not call computeInitKindForClass(clazz) on purpose: if clazz is the root
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.security.Provider;
import java.security.Security;

import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
Expand Down Expand Up @@ -80,12 +81,15 @@ public void testUnknownSecurityServices() throws Exception {

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

Expand Down