diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/FutureDefaultsOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/FutureDefaultsOptions.java index d1aa2ba86b42..c118f588444c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/FutureDefaultsOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/FutureDefaultsOptions.java @@ -31,10 +31,12 @@ import java.util.Set; import java.util.stream.Collectors; +import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; import com.oracle.svm.core.option.HostedOptionKey; @@ -45,6 +47,7 @@ import com.oracle.svm.util.StringUtil; import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionType; /** @@ -81,12 +84,14 @@ public class FutureDefaultsOptions { private static final String RUN_TIME_INITIALIZE_SECURITY_PROVIDERS = "run-time-initialize-security-providers"; private static final String RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS = "run-time-initialize-file-system-providers"; private static final String RUN_TIME_INITIALIZE_RESOURCE_BUNDLES = "run-time-initialize-resource-bundles"; - private static final List ALL_FUTURE_DEFAULTS = List.of(RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS, RUN_TIME_INITIALIZE_SECURITY_PROVIDERS, RUN_TIME_INITIALIZE_RESOURCE_BUNDLES); + private static final String CLASS_FOR_NAME_RESPECTS_CLASS_LOADER = "class-for-name-respects-class-loader"; + private static final List ALL_FUTURE_DEFAULTS = List.of(CLASS_FOR_NAME_RESPECTS_CLASS_LOADER, RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS, RUN_TIME_INITIALIZE_SECURITY_PROVIDERS, + RUN_TIME_INITIALIZE_RESOURCE_BUNDLES); private static final String COMPLETE_REFLECTION_TYPES = "complete-reflection-types"; private static final List RETIRED_FUTURE_DEFAULTS = List.of(COMPLETE_REFLECTION_TYPES); - public static final String RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS + + public static final String RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + CLASS_FOR_NAME_RESPECTS_CLASS_LOADER + ")"; public static final String RUN_TIME_INITIALIZE_SECURITY_PROVIDERS_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + RUN_TIME_INITIALIZE_SECURITY_PROVIDERS + ")"; public static final String RUN_TIME_INITIALIZE_RESOURCE_BUNDLES_REASON = "Initialize JDK classes at run time (--" + OPTION_NAME + " includes " + RUN_TIME_INITIALIZE_RESOURCE_BUNDLES + ")"; @@ -107,7 +112,16 @@ private static LinkedHashSet getAllValues() { @APIOption(name = OPTION_NAME, defaultValue = DEFAULT_NAME) // @Option(help = "file:doc-files/FutureDefaultsHelp.txt", type = OptionType.User) // static final HostedOptionKey FutureDefaults = new HostedOptionKey<>( - AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); + AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, AccumulatingLocatableMultiOptionValue.Strings oldValue, AccumulatingLocatableMultiOptionValue.Strings newValue) { + super.onValueUpdate(values, oldValue, newValue); + /* temporary simple pwdctest, will do full parsing */ + if (newValue.values().contains("all") || newValue.values().contains(CLASS_FOR_NAME_RESPECTS_CLASS_LOADER)) { + ClassForNameSupport.Options.ClassForNameRespectsClassLoader.update(values, true); + } + } + }; private static String getOptionHelpText() { Objects.requireNonNull(FutureDefaultsOptions.FutureDefaults.getDescriptor(), "This must be called after the options are processed."); @@ -124,7 +138,14 @@ private static void verifyOptionDescription() { } } if (!optionHelpText.contains(futureDefaultsAllValues())) { - throw VMError.shouldNotReachHere("Must mention all options in a comma-separated sequence: " + futureDefaultsAllValues()); + throw VMError.shouldNotReachHere("Must mention all options in a comma-separated in the exact order: " + futureDefaultsAllValues()); + } + + /* Ensure retired future-defaults are not mentioned in user-facing help text */ + for (String retired : RETIRED_FUTURE_DEFAULTS) { + if (optionHelpText.contains("'" + retired + "'")) { + throw VMError.shouldNotReachHere("Must not mention retired options in the help text. Retired option: " + retired); + } } } @@ -183,16 +204,16 @@ public static void parseAndVerifyOptions() { /* Set build-time properties for user features */ for (String futureDefault : getFutureDefaults()) { - setSystemProperty(futureDefault, true); + setSystemProperty(futureDefault); } for (String retiredFutureDefault : RETIRED_FUTURE_DEFAULTS) { - setSystemProperty(retiredFutureDefault, true); + setSystemProperty(retiredFutureDefault); } } - private static void setSystemProperty(String futureDefault, boolean value) { - System.setProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.toString(value)); + private static void setSystemProperty(String futureDefault) { + System.setProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.toString(true)); } public static Set getFutureDefaults() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/FutureDefaultsHelp.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/FutureDefaultsHelp.txt index e47a1018e1f8..a8e0763cbc49 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/FutureDefaultsHelp.txt +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/doc-files/FutureDefaultsHelp.txt @@ -1,4 +1,4 @@ -Enable options that are planned to become defaults in future releases. Comma-separated list can contain 'all', 'none', 'run-time-initialize-jdk', 'run-time-initialize-file-system-providers', 'run-time-initialize-security-providers', 'run-time-initialize-resource-bundles', 'complete-reflection-types'. The preferred usage is '--future-defaults=all'. +Enable options that are planned to become defaults in future releases. Comma-separated list can contain 'all', 'none', 'run-time-initialize-jdk', 'class-for-name-respects-class-loader', 'run-time-initialize-file-system-providers', 'run-time-initialize-security-providers', 'run-time-initialize-resource-bundles'. The preferred usage is '--future-defaults=all'. The meaning of each possible option is as follows: 'all' - is the preferred option, and it enables all other behaviors. @@ -7,10 +7,10 @@ The meaning of each possible option is as follows: 'run-time-initialize-jdk' - enables all behaviors related to run-time initialization of the JDK: ['run-time-initialize-security-providers', 'run-time-initialize-file-system-providers', 'run-time-initialize-resource-bundles']. - 'complete-reflection-types' - reflective registration of a type, via metadata files or the Feature API, always includes all type metadata. Now, all registered types behave the same as types defined in 'reachability-metadata.json'. + 'class-for-name-respects-class-loader' - `Class.forName` and similar respect their class loader argument. 'run-time-initialize-security-providers' - shifts away from build-time initialization for 'java.security.Provider'. Unless you store 'java.security.Provider'-related classes in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages. 'run-time-initialize-file-system-providers' - shifts away from build-time initialization for 'java.nio.file.spi.FileSystemProvider'. Unless you store 'FileSystemProvider'-related classes in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages. - 'run-time-initialize-resource-bundles' - shifts away from build-time initialization for 'java.util.ResourceBundle'. Unless you store 'ResourceBundle'-related classes in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages. \ No newline at end of file + 'run-time-initialize-resource-bundles' - shifts away from build-time initialization for 'java.util.ResourceBundle'. Unless you store 'ResourceBundle'-related classes in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java index ab692a6721fc..edc0a1b7cf86 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java @@ -482,6 +482,9 @@ public static boolean isUnsafeAllocatedPreserved(Class jClass) { } public static boolean isRegisteredClass(String className) { + if (!ClassNameSupport.isValidReflectionName(className)) { + return true; + } if (respectClassLoader()) { RuntimeDynamicAccessMetadata dynamicAccessMetadata = getDynamicAccessMetadataForName(className); if (dynamicAccessMetadata == null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java index 60fc7dd9aa39..5e9c7f9499ca 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java @@ -197,6 +197,9 @@ private Class resolve(String name, ClassLoader loader) throws ClassNotFoundEx arrayDimensions++; } if (arrayDimensions == name.length()) { + if (loader == null) { + return null; + } throw new ClassNotFoundException(name); } Class elementalResult; @@ -271,7 +274,7 @@ private static Class getArrayClass(String name, Class elementalResult, int if (RuntimeClassLoading.isSupported()) { RuntimeClassLoading.getOrCreateArrayHub(hub); } else { - if (throwMissingRegistrationErrors()) { + if (throwMissingRegistrationErrors() && shouldFollowReflectionConfiguration() && !ClassForNameSupport.isRegisteredClass(name)) { MissingReflectionRegistrationUtils.reportClassAccess(name); } return null;