From 88a3f67e9fef8c81b7797142c2529481b20b0a29 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Tue, 11 Jul 2023 23:28:44 -0700 Subject: [PATCH] Allow all classes to be used at image build time --- substratevm/CHANGELOG.md | 1 + substratevm/mx.substratevm/mx_substratevm.py | 4 ++-- .../classinitialization/ClassInitializationOptions.java | 5 +++-- .../classinitialization/ClassInitializationSupport.java | 8 ++++---- .../hosted/image/DisallowedImageHeapObjectFeature.java | 9 ++++++++- .../oracle/svm/test/clinit/TestClassInitialization.java | 8 ++++---- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index b193a081f039..55bac1a54fab 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-45841) BellSoft added support for the JFR event ThreadCPULoad. * (GR-45994) Removed the option `-H:EnableSignalAPI`. Please use the runtime option `EnableSignalHandling` if it is necessary to enable or disable signal handling explicitly. * (GR-39406) Simulation of class initializer: Class initializer of classes that are not marked for initialization at image build time are simulated at image build time to avoid executing them at image run time. +* (GR-39406) All classes can now be used at image build time, even when they are not configured as --initialize-at-build-time. But still, only classes configured as --initialize-at-build-time are allowed in the image heap. * (GR-46392) Add `--parallelism` option to control how many threads are used by the build process. * (GR-46392) Add build resources section to the build output that shows the memory and thread limits of the build process. diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 710ed2a0a74c..e1e71aa6dfec 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1413,9 +1413,9 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy): mkpath(build_dir) if new_class_init_policy: - policy_args = ['-H:+UseNewExperimentalClassInitialization', '-H:+SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] + policy_args = ['-H:-UseDeprecatedOldClassInitialization', '-H:+SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] else: - policy_args = ['-H:-UseNewExperimentalClassInitialization', '-H:-SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] + policy_args = ['-H:+UseDeprecatedOldClassInitialization', '-H:-SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] # Build and run the example native_image( diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java index b6128313be24..54fca49082cd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java @@ -100,8 +100,9 @@ private static class InitializationValueEager extends InitializationValueTransfo @Option(help = "Assert class initialization is specified for all classes.", type = OptionType.Debug)// public static final HostedOptionKey AssertInitializationSpecifiedForAllClasses = new HostedOptionKey<>(false); - @Option(help = "Use new class initialization strategy that allows all classes to be used at image build time.", type = OptionType.Expert)// - public static final HostedOptionKey UseNewExperimentalClassInitialization = new HostedOptionKey<>(false); + @Option(help = "Use the old class initialization strategy that does not allow all classes to be used at image build time.", type = OptionType.Expert, // + deprecated = true, deprecationMessage = "Temporary flag to restore the class initialization behavior of older GraalVM versions. The old class initialization strategy will be removed in a future version of GraalVM.") // + public static final HostedOptionKey UseDeprecatedOldClassInitialization = new HostedOptionKey<>(false); @Option(help = "Simulate the effects of class initializer at image build time, to avoid class initialization at run time.", type = OptionType.Expert)// public static final HostedOptionKey SimulateClassInitializer = new HostedOptionKey<>(true); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java index 3405e25009f6..d7c88e8e71e0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java @@ -87,11 +87,11 @@ public abstract class ClassInitializationSupport implements RuntimeClassInitiali final MetaAccessProvider metaAccess; public static ClassInitializationSupport create(MetaAccessProvider metaAccess, ImageClassLoader loader) { - if (ClassInitializationOptions.UseNewExperimentalClassInitialization.getValue()) { - LogUtils.warning("Using new experimental class initialization strategy. Image size and peak performance are not optimized yet!"); - return new AllowAllHostedUsagesClassInitializationSupport(metaAccess, loader); + if (ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + LogUtils.warning("Using old deprecated class initialization strategy. Only classes that are marked explicitly as '--initialize-at-build-time' can be used during image generation."); + return new ProvenSafeClassInitializationSupport(metaAccess, loader); } - return new ProvenSafeClassInitializationSupport(metaAccess, loader); + return new AllowAllHostedUsagesClassInitializationSupport(metaAccess, loader); } public static ClassInitializationSupport singleton() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java index d34bd7b0d50a..421f1b1f4168 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java @@ -168,11 +168,18 @@ private void checkDisallowedMBeanObjects(Object original) { } private RuntimeException error(String msg, Object obj, String initializerAction) { + String suffix = ""; + if (!ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + suffix = System.lineSeparator() + + "If you see this error while migrating to GraalVM for JDK 21, please note that the class initialization strategy has changed in GraalVM for JDK 21." + + " It is not allowed to use all classes at image build time. However, only classes explicitly marked as --initialize-at-built-time are allowed to be in the image heap." + + " This rule is now strictly enforced, i.e., the problem might be solvable by registering the reported type as --initialize-at-built-time."; + } throw new UnsupportedFeatureException(msg + " " + classInitialization.objectInstantiationTraceMessage(obj, initializerAction) + " " + "The object was probably created by a class initializer and is reachable from a static field. " + "You can request class initialization at image runtime by using the option " + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, "", "initialize-at-run-time") + ". " + - "Or you can write your own initialization methods and call them explicitly from your main entry point."); + "Or you can write your own initialization methods and call them explicitly from your main entry point." + suffix); } private static boolean search(byte[] haystack, byte[] needle) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java index 92149d7ce60e..14f481166aa7 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java @@ -714,8 +714,8 @@ public void afterImageWrite(AfterImageWriteAccess access) { } /** - * For testing with {@link ClassInitializationOptions#UseNewExperimentalClassInitialization} set to - * false and simulation of class initializer disabled. + * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to + * true and simulation of class initializer disabled. */ class TestClassInitializationFeatureOldPolicyFeature extends TestClassInitializationFeature { @@ -752,8 +752,8 @@ void checkClass(Class checkedClass, boolean checkSafeEarly, boolean checkSafe } /** - * For testing with {@link ClassInitializationOptions#UseNewExperimentalClassInitialization} set to - * true and simulation of class initializer enabled. + * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to + * false and simulation of class initializer enabled. */ class TestClassInitializationFeatureNewPolicyFeature extends TestClassInitializationFeature { @Override