Skip to content

Commit

Permalink
Remove the old class initialization strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Wimmer committed Feb 7, 2024
1 parent 5597913 commit 7b99a84
Show file tree
Hide file tree
Showing 25 changed files with 467 additions and 1,823 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -46,11 +46,25 @@ public interface RuntimeClassInitializationSupport {

void initializeAtBuildTime(String name, String reason);

void rerunInitialization(String name, String reason);
@Deprecated
default void rerunInitialization(String name, String reason) {
/*
* There is no more difference between initializing a class at run-time and re-running the
* class initializer at run time.
*/
initializeAtRunTime(name, reason);
}

void initializeAtRunTime(Class<?> aClass, String reason);

void rerunInitialization(Class<?> aClass, String reason);
@Deprecated
default void rerunInitialization(Class<?> aClass, String reason) {
/*
* There is no more difference between initializing a class at run-time and re-running the
* class initializer at run time.
*/
initializeAtRunTime(aClass, reason);
}

void initializeAtBuildTime(Class<?> aClass, String reason);
}
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This changelog summarizes major changes to GraalVM Native Image.

## GraalVM for JDK 23 (Internal Version 24.1.0)
* (GR-51520) The old class initialization strategy, which was deprecated in GraalVM for JDK 22, is removed. The option `StrictImageHeap` no longer has any effect.
* (GR-51106) Fields that are accessed via a `VarHandle` or `MethodHandle` are no longer marked as "unsafe accessed" when the `VarHandle`/`MethodHandle` can be fully intrinsified.
* (GR-49996) Ensure explicitly set image name (e.g., via `-o imagename`) is not accidentally overwritten by `-jar jarfile` option.
* (GR-48683) Together with Red Hat, we added partial support for the JFR event `OldObjectSample`.
Expand Down
28 changes: 6 additions & 22 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1547,11 +1547,7 @@ def cinterfacetutorial(args):

@mx.command(suite.name, 'clinittest', 'Runs the ')
def clinittest(args):
def build_and_test_clinittest_images(native_image, args=None):
build_and_test_clinittest_image(native_image, args, True)
build_and_test_clinittest_image(native_image, args, False)

def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
def build_and_test_clinittest_image(native_image, args):
args = [] if args is None else args
test_cp = classpath('com.oracle.svm.test')
build_dir = join(svmbuild_dir(), 'clinittest')
Expand All @@ -1561,11 +1557,6 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
mx.rmtree(build_dir)
mx.ensure_dir_exists(build_dir)

if new_class_init_policy:
policy_args = svm_experimental_options(['-H:+SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature']
else:
policy_args = svm_experimental_options(['-H:-StrictImageHeap', '-H:-SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature']

# Build and run the example
binary_path = join(build_dir, 'clinittest')
native_image([
Expand All @@ -1575,9 +1566,10 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
'-o', binary_path,
'-H:+ReportExceptionStackTraces',
'-H:Class=com.oracle.svm.test.clinit.TestClassInitialization',
'--features=com.oracle.svm.test.clinit.TestClassInitializationFeature',
] + svm_experimental_options([
'-H:+PrintClassInitialization',
]) + policy_args + args)
]) + args)
mx.run([binary_path])

# Check the reports for initialized classes
Expand All @@ -1591,16 +1583,8 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):
"Classes marked with " + marker + " must have init kind " + init_kind + " and message " + msg)]
with open(classes_file) as f:
for line in f:
if new_class_init_policy:
checkLine(line, "MustBeSafeEarly", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeSafeLate", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
else:
checkLine(line, "MustBeSafeEarly", "BUILD_TIME", "class proven as side-effect free before analysis", wrongly_initialized_lines)
checkLine(line, "MustBeSafeLate", "BUILD_TIME", "class proven as side-effect free after analysis", wrongly_initialized_lines)
checkLine(line, "MustBeSimulated", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)

if len(wrongly_initialized_lines) > 0:
msg = ""
Expand All @@ -1613,7 +1597,7 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):

check_class_initialization(all_classes_file)

native_image_context_run(build_and_test_clinittest_images, args)
native_image_context_run(build_and_test_clinittest_image, args)


class SubstrateJvmFuncsFallbacksBuilder(mx.Project):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ protected static boolean isWindows() {
return Platform.includedIn(Platform.WINDOWS.class);
}

protected static void rerunClassInit(FeatureAccess access, String... classNames) {
protected static void initializeAtRunTime(FeatureAccess access, String... classNames) {
RuntimeClassInitializationSupport classInitSupport = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
for (String className : classNames) {
classInitSupport.rerunInitialization(clazz(access, className), "for JDK native code support via JNI");
classInitSupport.initializeAtRunTime(clazz(access, className), "for JDK native code support via JNI");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
public void duringSetup(DuringSetupAccess access) {
RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
rci.initializeAtRunTime("jdk.internal.net.http", "for reading properties at run time");
rci.rerunInitialization("jdk.internal.net.http.websocket.OpeningHandshake", "contains a SecureRandom reference");
rci.initializeAtRunTime("jdk.internal.net.http.websocket.OpeningHandshake", "contains a SecureRandom reference");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ private void processFieldUpdater(Object updater) {
class InnocuousForkJoinWorkerThreadFeature implements InternalFeature {
@Override
public void duringSetup(DuringSetupAccess access) {
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).rerunInitialization(access.findClassByName("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread"),
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtRunTime(access.findClassByName("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread"),
"innocuousThreadGroup must be initialized at run time");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(loader, classLoaderSupport));
ImageSingletons.add(ObservableImageHeapMapProvider.class, new ObservableImageHeapMapProviderImpl());

ClassInitializationSupport classInitializationSupport = ClassInitializationSupport.create(originalMetaAccess, loader);
ClassInitializationSupport classInitializationSupport = new ClassInitializationSupport(originalMetaAccess, loader);
ImageSingletons.add(RuntimeClassInitializationSupport.class, classInitializationSupport);
ClassInitializationFeature.processClassInitializationOptions(classInitializationSupport);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.Configuration;

import jdk.graal.compiler.options.Option;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.RuntimeJNIAccess;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
Expand All @@ -107,6 +106,7 @@
import com.oracle.svm.util.ModuleSupport;
import com.oracle.svm.util.ReflectionUtil;

import jdk.graal.compiler.options.Option;
import sun.security.jca.ProviderList;
import sun.security.provider.NativePRNG;
import sun.security.x509.OIDMap;
Expand Down Expand Up @@ -239,63 +239,50 @@ public void duringSetup(DuringSetupAccess a) {
RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
/*
* The SecureRandom implementations open the /dev/random and /dev/urandom files which are
* used as sources for entropy. These files are opened in the static initializers. That's
* why we rerun the static initializers at runtime. We cannot completely delay the static
* initializers execution to runtime because the SecureRandom classes are needed by the
* native image generator too, e.g., by Files.createTempDirectory().
* used as sources for entropy. These files are opened in the static initializers.
*/
rci.rerunInitialization(NativePRNG.class, "for substitutions");
rci.rerunInitialization(NativePRNG.Blocking.class, "for substitutions");
rci.rerunInitialization(NativePRNG.NonBlocking.class, "for substitutions");
rci.initializeAtRunTime(NativePRNG.class, "for substitutions");
rci.initializeAtRunTime(NativePRNG.Blocking.class, "for substitutions");
rci.initializeAtRunTime(NativePRNG.NonBlocking.class, "for substitutions");

rci.rerunInitialization(clazz(access, "sun.security.provider.SeedGenerator"), "for substitutions");
rci.rerunInitialization(clazz(access, "sun.security.provider.SecureRandom$SeederHolder"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "sun.security.provider.SeedGenerator"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "sun.security.provider.SecureRandom$SeederHolder"), "for substitutions");

/*
* sun.security.provider.AbstractDrbg$SeederHolder has a static final EntropySource seeder
* field that needs to be re-initialized at run time because it captures the result of
* field that needs to be initialized at run time because it captures the result of
* SeedGenerator.getSystemEntropy().
*/
rci.rerunInitialization(clazz(access, "sun.security.provider.AbstractDrbg$SeederHolder"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "sun.security.provider.AbstractDrbg$SeederHolder"), "for substitutions");
if (isMscapiModulePresent) {
/* PRNG.<clinit> creates a Cleaner (see JDK-8210476), which starts its thread. */
rci.rerunInitialization(clazz(access, "sun.security.mscapi.PRNG"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "sun.security.mscapi.PRNG"), "for substitutions");
}
rci.rerunInitialization(clazz(access, "sun.security.provider.FileInputStreamPool"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "sun.security.provider.FileInputStreamPool"), "for substitutions");
/* java.util.UUID$Holder has a static final SecureRandom field. */
rci.rerunInitialization(clazz(access, "java.util.UUID$Holder"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "java.util.UUID$Holder"), "for substitutions");

/*
* The classes below have a static final SecureRandom field. Note that if the classes are
* not found as reachable by the analysis registering them for class initialization rerun
* doesn't have any effect.
*/
rci.rerunInitialization(clazz(access, "sun.security.jca.JCAUtil$CachedSecureRandomHolder"), "for substitutions");
rci.rerunInitialization(clazz(access, "com.sun.crypto.provider.SunJCE$SecureRandomHolder"), "for substitutions");
optionalClazz(access, "sun.security.krb5.Confounder").ifPresent(clazz -> rci.rerunInitialization(clazz, "for substitutions"));
optionalClazz(access, "sun.security.krb5.Config").ifPresent(clazz -> rci.rerunInitialization(clazz, "Reset the value of lazily initialized field sun.security.krb5.Config#singleton"));
/* The classes below have a static final SecureRandom field. */
rci.initializeAtRunTime(clazz(access, "sun.security.jca.JCAUtil$CachedSecureRandomHolder"), "for substitutions");
rci.initializeAtRunTime(clazz(access, "com.sun.crypto.provider.SunJCE$SecureRandomHolder"), "for substitutions");
optionalClazz(access, "sun.security.krb5.Confounder").ifPresent(clazz -> rci.initializeAtRunTime(clazz, "for substitutions"));
optionalClazz(access, "sun.security.krb5.Config").ifPresent(clazz -> rci.initializeAtRunTime(clazz, "Reset the value of lazily initialized field sun.security.krb5.Config#singleton"));

rci.rerunInitialization(clazz(access, "sun.security.jca.JCAUtil"), "JCAUtil.def holds a SecureRandom.");
rci.initializeAtRunTime(clazz(access, "sun.security.jca.JCAUtil"), "JCAUtil.def holds a SecureRandom.");

/*
* When SSLContextImpl$DefaultManagersHolder sets-up the TrustManager in its initializer it
* gets the value of the -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStorePassword
* properties from the build machine. Re-runing its initialization at run time is required
* to use the run time provided values.
* properties from the build machine. Running its initialization at run time is required to
* use the run time provided values.
*/
rci.rerunInitialization(clazz(access, "sun.security.ssl.SSLContextImpl$DefaultManagersHolder"), "for reading properties at run time");
rci.initializeAtRunTime(clazz(access, "sun.security.ssl.SSLContextImpl$DefaultManagersHolder"), "for reading properties at run time");

/*
* SSL debug logging enabled by javax.net.debug system property is setup during the class
* initialization of either sun.security.ssl.Debug or sun.security.ssl.SSLLogger. (In JDK 8
* this was implemented in sun.security.ssl.Debug, the logic was moved to
* sun.security.ssl.SSLLogger in JDK11 but not yet backported to all JDKs. See JDK-8196584
* for details.) We cannot prevent these classes from being initialized at image build time,
* so we have to reinitialize them at run time to honour the run time passed value for the
* javax.net.debug system property.
* initialization.
*/
optionalClazz(access, "sun.security.ssl.Debug").ifPresent(c -> rci.rerunInitialization(c, "for reading properties at run time"));
optionalClazz(access, "sun.security.ssl.SSLLogger").ifPresent(c -> rci.rerunInitialization(c, "for reading properties at run time"));
rci.initializeAtRunTime(clazz(access, "sun.security.ssl.SSLLogger"), "for reading properties at run time");
}

@Override
Expand Down
Loading

0 comments on commit 7b99a84

Please sign in to comment.