diff --git a/.github/workflows/micronaut-compatibility-preserve-crema.yml b/.github/workflows/micronaut-compatibility-preserve-crema.yml new file mode 100644 index 000000000000..101b6bae1408 --- /dev/null +++ b/.github/workflows/micronaut-compatibility-preserve-crema.yml @@ -0,0 +1,56 @@ +# +# Copyright (c) 2026, 2026, 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 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +name: Weekly Micronaut Tests (-H:+CompatibilityMode -H:Preserve=all -H:+RuntimeClassLoading) + +on: + pull_request: + paths: + - '.github/workflows/micronaut-template.yml' + - '.github/workflows/micronaut-compatibility-preserve.yml' + schedule: + - cron: '0 2 * * 1' + workflow_dispatch: + +jobs: + call-template: + uses: './.github/workflows/micronaut-template.yml' + with: + NATIVE_IMAGE_OPTIONS: '-H:+UnlockExperimentalVMOptions -H:+ReportExceptionStackTraces -H:+CompatibilityMode -H:Preserve=all -H:+RuntimeClassLoading -H:-UnlockExperimentalVMOptions' diff --git a/.github/workflows/micronaut-template.yml b/.github/workflows/micronaut-template.yml index 3dd6116c827a..dad5b25e2d7d 100644 --- a/.github/workflows/micronaut-template.yml +++ b/.github/workflows/micronaut-template.yml @@ -52,7 +52,6 @@ env: MICRONAUT_JAVA_VERSION: 25 # Enforce experimental option checking in CI (GR-47922) NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL: 'true' - NATIVE_IMAGE_OPTIONS: ${{ inputs.NATIVE_IMAGE_OPTIONS }} permissions: contents: read # to fetch code (actions/checkout) @@ -76,7 +75,9 @@ jobs: run: | tar -xzf ${{ needs.build-graalvm.outputs.artifact-file-name }} -C ${{ github.workspace }} echo "JAVA_HOME=${{ github.workspace }}/${{ needs.build-graalvm.outputs.artifact-name }}" >> ${GITHUB_ENV} - - name: Run nativeTest in Micronaut launch project + - name: Run nativeTest in Micronaut launch project${{ inputs.NATIVE_IMAGE_OPTIONS && format(' with ''{0}''', inputs.NATIVE_IMAGE_OPTIONS) || '' }} + env: + NATIVE_IMAGE_OPTIONS: ${{ inputs.NATIVE_IMAGE_OPTIONS }} run: | curl --fail --silent --location --retry 3 --max-time 10 --output demo.zip --request GET 'https://launch.micronaut.io/create/default/com.example.demo?lang=JAVA&build=GRADLE&test=JUNIT&javaVersion=JDK_${{ env.MICRONAUT_JAVA_VERSION }}' unzip demo.zip @@ -88,6 +89,8 @@ jobs: repository: micronaut-projects/micronaut-core path: ${{ env.MICRONAUT_CORE_PATH }} - name: Run nativeTest in micronaut-core${{ inputs.NATIVE_IMAGE_OPTIONS && format(' with ''{0}''', inputs.NATIVE_IMAGE_OPTIONS) || '' }} + env: + NATIVE_IMAGE_OPTIONS: ${{ inputs.NATIVE_IMAGE_OPTIONS }} run: | cd ${{ env.MICRONAUT_CORE_PATH }} ./gradlew nativeTest diff --git a/.github/workflows/reachability-metadata-compatibility-preserve-crema.yml b/.github/workflows/reachability-metadata-compatibility-preserve-crema.yml new file mode 100644 index 000000000000..33578b79b0b1 --- /dev/null +++ b/.github/workflows/reachability-metadata-compatibility-preserve-crema.yml @@ -0,0 +1,56 @@ +# +# Copyright (c) 2026, 2026, 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 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +name: Weekly Reachability Metadata Tests (-H:+CompatibilityMode -H:Preserve=all -H:+RuntimeClassLoading) + +on: + pull_request: + paths: + - '.github/workflows/reachability-metadata-template.yml' + - '.github/workflows/reachability-metadata-compatibility-preserve.yml' + schedule: + - cron: '0 1 * * 1' + workflow_dispatch: + +jobs: + call-template: + uses: './.github/workflows/reachability-metadata-template.yml' + with: + NATIVE_IMAGE_OPTIONS: '-H:+UnlockExperimentalVMOptions -H:+ReportExceptionStackTraces -H:+CompatibilityMode -H:Preserve=all -H:+RuntimeClassLoading -H:-UnlockExperimentalVMOptions' diff --git a/docs/reference-manual/native-image/guides/troubleshoot-run-time-errors.md b/docs/reference-manual/native-image/guides/troubleshoot-run-time-errors.md index 23f21ae2c4f0..9340d65a1a60 100644 --- a/docs/reference-manual/native-image/guides/troubleshoot-run-time-errors.md +++ b/docs/reference-manual/native-image/guides/troubleshoot-run-time-errors.md @@ -41,10 +41,20 @@ For diagnosing shared libraries built with Native Image, you can either: * specify `-R:MissingRegistrationReportingMode=Exit` when building a native shared library; * or specify `-XX:MissingRegistrationReportingMode=Exit` when the isolate is created. `graal_create_isolate_params_t` has `argc` and `argv` fields that can be used to pass C-style command-line options at run time. -### 2. Set java.home Explicitly +### 2. Set java.home and Classpath Explicitly If your application code uses the `java.home` property, set it explicitly with `-Djava.home=` when running a native executable. -Otherwise, the `System.getProperty("java.home")` call will return a `null` value. +Otherwise, the `System.getProperty("java.home")` call will return a `null` value. For instance, you can run your native executable like this: + +```console +./my-native-app -Djava.home=/path/to/jdk +``` + +Some applications also need access to the classpath and module path at run time. Those can be set with `-Djava.class.path=` and `-Djdk.module.path=` when running the executable. Here's how you would specify both properties: + +```console +./my-native-app -Djava.class.path=/path/to/classes:/path/to/lib.jar -Djdk.module.path=/path/to/modules +``` ### 3. Enable URL Protocols diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java index cd14335226bf..da259c205dfd 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, 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 @@ -161,7 +161,7 @@ public static boolean inImageBuildtimeCode() { */ public static boolean isExecutable() { ensureKindAvailable(); - return PROPERTY_IMAGE_KIND_VALUE_EXECUTABLE.equals(System.getProperty(PROPERTY_IMAGE_KIND_KEY)); + return PROPERTY_IMAGE_KIND_VALUE_EXECUTABLE.equals(ImageKindHolder.IMAGE_KIND); } /** @@ -171,13 +171,21 @@ public static boolean isExecutable() { */ public static boolean isSharedLibrary() { ensureKindAvailable(); - return PROPERTY_IMAGE_KIND_VALUE_SHARED_LIBRARY.equals(System.getProperty(PROPERTY_IMAGE_KIND_KEY)); + return PROPERTY_IMAGE_KIND_VALUE_SHARED_LIBRARY.equals(ImageKindHolder.IMAGE_KIND); } private static void ensureKindAvailable() { - if (inImageCode() && System.getProperty(PROPERTY_IMAGE_KIND_KEY) == null) { + if (inImageCode() && ImageKindHolder.IMAGE_KIND == null) { throw new UnsupportedOperationException( "The kind of image that is built (executable or shared library) is not available yet because the relevant command line option has not been parsed yet."); } } + + /// This class is needed so [#IMAGE_KIND] does not get initialized before the + /// [#PROPERTY_IMAGE_KIND_KEY] property is set. + /// [ImageInfo] is initialized very early in the build process before the + /// [#PROPERTY_IMAGE_KIND_KEY] property is set by the image builder. + private static final class ImageKindHolder { + static final String IMAGE_KIND = System.getProperty(PROPERTY_IMAGE_KIND_KEY); + } } diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 9aada4e203c6..6953493b59ac 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -24,6 +24,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-63737) Deprecated API function `Threading.registerRecurringCallback(...)` without replacement. This method should not be used as it is inherently unsafe. * (GR-63737) Removed deprecated API function `ProcessPropertiesSupport.setLocale(...)`. * (GR-52538) (GR-69523) (GR-73129) Introduce new SerialGC policy `Adaptive2` and default to mark-compact collection in the old generation. On average, this reduces memory usage and often improves throughput and latency. Restore the old behavior with: `-H:-CompactingOldGen -H:InitialCollectionPolicy=Adaptive`. +* (GR-71974) Introduced `-H:+CompatibilityMode` that disables all Native Image features that allow users to diverge from original program semantics: build-time initialization for classpath classes, native-image-specific system properties, substitutions on the classpath, and user features, while enabling all future defaults. This mode does not modify key Native Image restrictions related to dynamic access (reachability metadata) and run-time class loading as those are accepted limitations of native image. ## GraalVM 25 * (GR-52276) (GR-61959) Add support for Arena.ofShared(). diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java index d9b7a835cf71..4bd63b04d0a8 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java @@ -26,15 +26,19 @@ import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.word.impl.Word; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; import com.oracle.svm.core.jdk.SystemPropertiesSupport; import com.oracle.svm.core.posix.headers.Limits; import com.oracle.svm.core.posix.headers.Unistd; -import org.graalvm.word.impl.Word; public abstract class PosixSystemPropertiesSupport extends SystemPropertiesSupport { + public PosixSystemPropertiesSupport(boolean compatibilityMode) { + super(compatibilityMode); + } + @Override protected String jvmLibName() { return "libjvm" + jvmLibSuffix(); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java index ba52bf05f3a6..75753fefd56e 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.posix.darwin; +import com.oracle.svm.hosted.NativeImageOptions; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.c.function.CLibrary; @@ -32,6 +33,7 @@ import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; import org.graalvm.nativeimage.impl.RuntimeSystemPropertiesSupport; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.impl.Word; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; @@ -43,17 +45,20 @@ import com.oracle.svm.core.posix.headers.Stdlib; import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.posix.headers.darwin.Foundation; +import com.oracle.svm.shared.singletons.traits.SingletonTraits; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.AllAccess; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.BuildtimeAccessOnly; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.Disallowed; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.NoLayeredCallbacks; -import com.oracle.svm.shared.singletons.traits.SingletonTraits; -import org.graalvm.word.impl.Word; @SingletonTraits(access = AllAccess.class, layeredCallbacks = NoLayeredCallbacks.class, other = Disallowed.class) @CLibrary(value = "darwin", requireStatic = true) public class DarwinSystemPropertiesSupport extends PosixSystemPropertiesSupport { + public DarwinSystemPropertiesSupport(boolean compatibilityMode) { + super(compatibilityMode); + } + @Override protected String javaIoTmpdirValue() { /* Darwin has a per-user temp dir */ @@ -143,7 +148,8 @@ protected String jvmLibSuffix() { class DarwinSystemPropertiesFeature implements InternalFeature { @Override public void duringSetup(DuringSetupAccess access) { - ImageSingletons.add(RuntimeSystemPropertiesSupport.class, new DarwinSystemPropertiesSupport()); + ImageSingletons.add(RuntimeSystemPropertiesSupport.class, + new DarwinSystemPropertiesSupport(NativeImageOptions.compatibilityMode())); ImageSingletons.add(SystemPropertiesSupport.class, (SystemPropertiesSupport) ImageSingletons.lookup(RuntimeSystemPropertiesSupport.class)); } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java index 2775b6a4b882..ff87f51b0264 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java @@ -38,15 +38,20 @@ import com.oracle.svm.core.posix.PosixSystemPropertiesSupport; import com.oracle.svm.core.posix.headers.Stdlib; import com.oracle.svm.core.posix.headers.Utsname; +import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.shared.singletons.traits.SingletonTraits; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.AllAccess; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.BuildtimeAccessOnly; import com.oracle.svm.shared.singletons.traits.BuiltinTraits.SingleLayer; import com.oracle.svm.shared.singletons.traits.SingletonLayeredInstallationKind.InitialLayerOnly; -import com.oracle.svm.shared.singletons.traits.SingletonTraits; @SingletonTraits(access = AllAccess.class, layeredCallbacks = SingleLayer.class, layeredInstallationKind = InitialLayerOnly.class) public class LinuxSystemPropertiesSupport extends PosixSystemPropertiesSupport { + public LinuxSystemPropertiesSupport(boolean compatibilityMode) { + super(compatibilityMode); + } + @Override protected String javaIoTmpdirValue() { /* @@ -110,7 +115,7 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void duringSetup(DuringSetupAccess access) { - LinuxSystemPropertiesSupport singleton = new LinuxSystemPropertiesSupport(); + LinuxSystemPropertiesSupport singleton = new LinuxSystemPropertiesSupport(NativeImageOptions.compatibilityMode()); ImageSingletons.add(RuntimeSystemPropertiesSupport.class, singleton); ImageSingletons.add(SystemPropertiesSupport.class, singleton); } diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java index 5ee1adde9d50..5981348be8a1 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsSystemPropertiesSupport.java @@ -37,6 +37,7 @@ import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.nativeimage.impl.RuntimeSystemPropertiesSupport; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.impl.Word; import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; @@ -60,7 +61,7 @@ import com.oracle.svm.core.windows.headers.WinVer; import com.oracle.svm.core.windows.headers.WindowsLibC; import com.oracle.svm.core.windows.headers.WindowsLibC.WCharPointer; -import org.graalvm.word.impl.Word; +import com.oracle.svm.hosted.NativeImageOptions; @SingletonTraits(access = AllAccess.class, layeredCallbacks = NoLayeredCallbacks.class, other = Disallowed.class) public class WindowsSystemPropertiesSupport extends SystemPropertiesSupport { @@ -74,6 +75,10 @@ public class WindowsSystemPropertiesSupport extends SystemPropertiesSupport { private static final int VER_PLATFORM_WIN32_WINDOWS = 1; private static final int VER_PLATFORM_WIN32_NT = 2; + public WindowsSystemPropertiesSupport(boolean compatibilityMode) { + super(compatibilityMode); + } + @Override protected String userNameValue() { WCharPointer userName = WindowsLibC._wgetenv(NonmovableArrays.addressOf(NonmovableArrays.fromImageHeap(USERNAME), 0)); @@ -406,7 +411,8 @@ private void computeOsNameAndVersion() { class WindowsSystemPropertiesFeature implements InternalFeature { @Override public void duringSetup(DuringSetupAccess access) { - ImageSingletons.add(RuntimeSystemPropertiesSupport.class, new WindowsSystemPropertiesSupport()); + ImageSingletons.add(RuntimeSystemPropertiesSupport.class, + new WindowsSystemPropertiesSupport(NativeImageOptions.compatibilityMode())); ImageSingletons.add(SystemPropertiesSupport.class, (SystemPropertiesSupport) ImageSingletons.lookup(RuntimeSystemPropertiesSupport.class)); } } 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 065b6bc3b0f1..45ae1030b870 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 @@ -113,7 +113,7 @@ 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<>( + public static final HostedOptionKey FutureDefaults = new HostedOptionKey<>( AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()) { @Override protected void onValueUpdate(EconomicMap, Object> values, AccumulatingLocatableMultiOptionValue.Strings oldValue, AccumulatingLocatableMultiOptionValue.Strings newValue) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java index e66b069eeb97..15095226b4b9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java @@ -121,7 +121,7 @@ public static SystemPropertiesSupport singleton() { @Platforms(Platform.HOSTED_ONLY.class) @SuppressWarnings("this-escape") - protected SystemPropertiesSupport() { + protected SystemPropertiesSupport(boolean compatibilityMode) { for (String key : HOSTED_PROPERTIES) { String value = System.getProperty(key); if (value != null) { @@ -142,18 +142,9 @@ protected SystemPropertiesSupport() { initializeProperty("java.vm.version", vm.version); initializeProperty("java.class.path", ""); - initializeProperty("java.endorsed.dirs", ""); - initializeProperty("java.ext.dirs", ""); - initializeProperty("sun.arch.data.model", Integer.toString(ConfigurationValues.getTarget().wordJavaKind.getBitCount())); - - initializeProperty(ImageInfo.PROPERTY_IMAGE_CODE_KEY, ImageInfo.PROPERTY_IMAGE_CODE_VALUE_RUNTIME); + initializeProperty("jdk.module.path", ""); - for (String futureDefault : FutureDefaultsOptions.getFutureDefaults()) { - initializeProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.TRUE.toString()); - } - for (String futureDefault : FutureDefaultsOptions.getRetiredFutureDefaults()) { - initializeProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.TRUE.toString()); - } + initializeProperty("sun.arch.data.model", Integer.toString(ConfigurationValues.getTarget().wordJavaKind.getBitCount())); ArrayList lazyProperties = new ArrayList<>(); lazyProperties.add(new LazySystemProperty(UserSystemProperty.NAME, this::userNameValue)); @@ -176,10 +167,8 @@ protected SystemPropertiesSupport() { lazyProperties.add(new LazySystemProperty(UserSystemProperty.VARIANT_DISPLAY, () -> LocaleSupport.singleton().getLocale().displayVariant())); lazyProperties.add(new LazySystemProperty(UserSystemProperty.VARIANT_FORMAT, () -> LocaleSupport.singleton().getLocale().formatVariant())); - if (ImageLayerBuildingSupport.buildingImageLayer()) { - lazyProperties.add(new LazySystemProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY, () -> ImageKindInfoSingleton.singleton().getImageKindInfoProperty())); - } else { - initializeProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY, System.getProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY)); + if (!compatibilityMode) { + initializeNonStandardProperties(lazyProperties); } String targetName = System.getProperty("svm.targetName"); @@ -208,6 +197,23 @@ protected SystemPropertiesSupport() { } } + private void initializeNonStandardProperties(ArrayList lazyProperties) { + initializeProperty(ImageInfo.PROPERTY_IMAGE_CODE_KEY, ImageInfo.PROPERTY_IMAGE_CODE_VALUE_RUNTIME); + + for (String futureDefault : FutureDefaultsOptions.getFutureDefaults()) { + initializeProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.TRUE.toString()); + } + for (String futureDefault : FutureDefaultsOptions.getRetiredFutureDefaults()) { + initializeProperty(FutureDefaultsOptions.SYSTEM_PROPERTY_PREFIX + futureDefault, Boolean.TRUE.toString()); + } + + if (ImageLayerBuildingSupport.buildingImageLayer()) { + lazyProperties.add(new LazySystemProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY, () -> ImageKindInfoSingleton.singleton().getImageKindInfoProperty())); + } else { + initializeProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY, System.getProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY)); + } + } + /** * Initializes a system property at build-time or during VM startup from external input (e.g., * command line arguments). diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java index 9115a5a70fd0..056ef52dd720 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/HostedOptionKey.java @@ -51,6 +51,11 @@ public HostedOptionKey(T defaultValue, HostedOptionKeyFlag... flags) { this(defaultValue, null, flags); } + /** + * Hosted option with build-time validation. + *

+ * Note: buildTimeValidation is called even when the option is not passed in. + */ public HostedOptionKey(T defaultValue, Consumer> buildTimeValidation, HostedOptionKeyFlag... flags) { super(defaultValue); this.buildTimeValidation = buildTimeValidation; diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index ece735641cb3..873d0ee3fb43 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -103,6 +103,7 @@ import com.oracle.svm.driver.metainf.NativeImageMetaInfWalker; import com.oracle.svm.hosted.CommonPoolUncaughtExceptionHandler; import com.oracle.svm.hosted.NativeImageGeneratorRunner; +import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.NativeImageSystemClassLoader; import com.oracle.svm.hosted.util.JDKArgsUtils; import com.oracle.svm.shared.util.StringUtil; @@ -125,6 +126,9 @@ public class NativeImage { static final String platform = getPlatform(); + // to avoid pulling in hosted classes + public static final String COMPATIBILITY_MODE_FLAG_NAME = NativeImageOptions.CompatibilityMode.getName(); + private static String getPlatform() { return (OS.getCurrent().className + "-" + SubstrateUtil.getArchitectureName()).toLowerCase(Locale.ROOT); } @@ -671,6 +675,12 @@ public List getBuildArgs() { } class DriverMetaInfProcessor implements NativeImageMetaInfResourceProcessor { + private final Path graalvmRootDir; + + DriverMetaInfProcessor(Path graalvmRootDir) { + this.graalvmRootDir = graalvmRootDir; + } + @Override public boolean processMetaInfResource(Path classpathEntry, Path resourceRoot, Path resourcePath, MetaInfFileType type) throws IOException { boolean isNativeImagePropertiesFile = type.equals(MetaInfFileType.Properties); @@ -720,9 +730,11 @@ public boolean processMetaInfResource(Path classpathEntry, Path resourceRoot, Pa if (imageNameValue != null) { addPlainImageBuilderArg(oHName + resolver.apply(imageNameValue), resourcePath.toUri().toString()); } - forEachPropertyValue(properties.get("JavaArgs"), NativeImage.this::addImageBuilderJavaArgs, resolver); - forEachPropertyValue(properties.get("Args"), args, resolver); - forEachPropertyValue(properties.get("ProvidedHostedOptions"), apiOptionHandler::injectKnownHostedOption, resolver); + if (classpathEntry.startsWith(graalvmRootDir) || !isCompatibilityModeEnabled()) { + forEachPropertyValue(properties.get("JavaArgs"), NativeImage.this::addImageBuilderJavaArgs, resolver); + forEachPropertyValue(properties.get("Args"), args, resolver); + forEachPropertyValue(properties.get("ProvidedHostedOptions"), apiOptionHandler::injectKnownHostedOption, resolver); + } } else { args.accept(oH(type.optionKey) + resourceRoot.relativize(resourcePath)); } @@ -760,7 +772,7 @@ public boolean isExcluded(Path resourcePath, Path entry) { @SuppressWarnings("this-escape") protected NativeImage(BuildConfiguration config) { this.config = config; - this.metaInfProcessor = new DriverMetaInfProcessor(); + this.metaInfProcessor = new DriverMetaInfProcessor(config.rootDir); this.archiveSupport = new ArchiveSupport(isVerbose()); String configFile = System.getenv(CONFIG_FILE_ENV_VAR_KEY); @@ -1419,7 +1431,12 @@ private record ArgumentEntry(int index, String value) { } private static Boolean getHostedOptionBooleanArgumentValue(List args, OptionKey option) { - String locationAgnosticBooleanPattern = "^" + oH + "[+-]" + option.getName() + "(@[^=]*)?$"; + String name = option.getName(); + return getHostedOptionBooleanArgumentValue(args, name); + } + + private static Boolean getHostedOptionBooleanArgumentValue(List args, String optionName) { + String locationAgnosticBooleanPattern = "^" + oH + "[+-]" + optionName + "(@[^=]*)?$"; Pattern pattern = Pattern.compile(locationAgnosticBooleanPattern); Boolean result = null; for (String arg : args) { @@ -1431,6 +1448,10 @@ private static Boolean getHostedOptionBooleanArgumentValue(List args, Op return result; } + private boolean isCompatibilityModeEnabled() { + return Boolean.TRUE.equals(getHostedOptionBooleanArgumentValue(imageBuilderArgs, COMPATIBILITY_MODE_FLAG_NAME)); + } + private boolean shouldAddCWDToCP() { if (printFlagsOptionQuery != null || printFlagsWithExtraHelpOptionQuery != null) { return false; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java index 4bfd79fc1cf6..d85229662b05 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageOptions.java @@ -33,9 +33,11 @@ import java.util.Date; import java.util.concurrent.ForkJoinPool; +import com.oracle.svm.core.option.OptionOrigin; import org.graalvm.collections.EconomicMap; import com.oracle.graal.pointsto.reports.ReportUtils; +import com.oracle.svm.core.FutureDefaultsOptions; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; @@ -45,10 +47,12 @@ import com.oracle.svm.core.util.InterruptImageBuilding; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions; +import com.oracle.svm.hosted.image.PreserveOptionsSupport; import com.oracle.svm.hosted.util.CPUType; import com.oracle.svm.shared.util.StringUtil; import com.oracle.svm.util.LogUtils; +import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.options.Option; import jdk.graal.compiler.options.OptionKey; import jdk.graal.compiler.options.OptionStability; @@ -301,4 +305,45 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o } } }; + + @Option(help = """ + This mode disables all Native Image features that allow users to diverge from original program semantics. + It disables build-time initialization for classes on the classpath, native-image system properties, user-defined substitutions, and user-defined features, while enabling all future defaults. + + This mode does not modify key Native Image restrictions related to dynamic access (reachability metadata) and runtime class loading as those are accepted limitations of native image. + + To overcome restrictions related to dynamic access (reachability metadata) and runtime class loading, and achieve the same semantics behavior as the original program, it is recommended to use this flag with: + + native-image -H:+CompatibilityMode -H:Preserve=all -H:+RuntimeClassLoading App + + And run the executable with: + + ./app -Djava.home= -Djava.class.path= -Djdk.module.path= + """, stability = OptionStability.EXPERIMENTAL)// + public static final HostedOptionKey CompatibilityMode = new HostedOptionKey<>(false, NativeImageOptions::validateCompatibilityMode) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { + super.onValueUpdate(values, oldValue, newValue); + if (!newValue) { + throw UserError.abort("CompatibilityMode can not be unset. Please remove " + SubstrateOptionsParser.commandArgument(NativeImageOptions.CompatibilityMode, "-", true, false)); + } + + FutureDefaultsOptions.FutureDefaults.update(values, "all"); + PreserveOptionsSupport.enableAllJDKFeatures(values); + } + }; + + private static void validateCompatibilityMode(HostedOptionKey compatibilityMode) { + OptionOrigin lastOrigin = compatibilityMode.getLastOrigin(); + if (lastOrigin != null && !lastOrigin.commandLineLike()) { + String optionArgument = SubstrateOptionsParser.commandArgument(NativeImageOptions.CompatibilityMode, "+", true, false); + throw UserError.abort("Using %s is only allowed on command line. The option was used from %s", optionArgument, NativeImageOptions.CompatibilityMode.getLastOrigin()); + } + } + + @Fold + public static boolean compatibilityMode() { + return CompatibilityMode.getValue(); + } + } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java index ae5a4c09788a..fe067d0c5be0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java @@ -159,24 +159,27 @@ public static void parsePreserveOption(EconomicMap, Object> hostedV } if (classLoaderSupport.isPreserveAll()) { - /* Include all parts of native image that are stripped */ - AddAllCharsets.update(hostedValues, true); - IncludeAllLocales.update(hostedValues, true); - AllowJRTFileSystem.update(hostedValues, true); - - /* Should be removed with GR-61365 */ - var missingJDKProtocols = List.of("http", "https", "ftp", "jar", "mailto", "jrt", "jmod"); - for (String missingProtocol : missingJDKProtocols) { - EnableURLProtocols.update(hostedValues, missingProtocol); - } - - AdditionalSecurityProviders.update(hostedValues, getSecurityProvidersCSV()); + enableAllJDKFeatures(hostedValues); /* Allow metadata tracing in preserve all images */ MetadataTracingSupport.update(hostedValues, true); } } + public static void enableAllJDKFeatures(EconomicMap, Object> hostedValues) { + /* Include all parts of native image that are stripped */ + AddAllCharsets.update(hostedValues, true); + IncludeAllLocales.update(hostedValues, true); + AllowJRTFileSystem.update(hostedValues, true); + + /* Should be removed with GR-61365 */ + var missingJDKProtocols = List.of("http", "https", "ftp", "jar", "mailto", "jrt", "jmod"); + for (String missingProtocol : missingJDKProtocols) { + EnableURLProtocols.update(hostedValues, missingProtocol); + } + AdditionalSecurityProviders.update(hostedValues, getSecurityProvidersCSV()); + } + private static String getSecurityProvidersCSV() { StringJoiner joiner = new StringJoiner(","); for (Provider provider : Security.getProviders()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index b25f320c4a65..310afc904132 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -395,6 +395,11 @@ protected void handleClass(Class annotatedClass, Set builderModules) return; } + boolean userSubstitution = !builderModules.contains(annotatedClass.getModule()); + if (NativeImageOptions.compatibilityMode() && userSubstitution) { + return; + } + TargetClass targetClassAnnotation = lookupAnnotation(annotatedClass, TargetClass.class); Class originalClass = findTargetClass(annotatedClass, targetClassAnnotation); if (originalClass == null) { @@ -413,7 +418,6 @@ protected void handleClass(Class annotatedClass, Set builderModules) int numAnnotations = (deleteAnnotation != null ? 1 : 0) + (substituteAnnotation != null ? 1 : 0); guarantee(numAnnotations <= 1, "Only one of @Delete or @Substitute can be used: %s", annotatedClass); - boolean userSubstitution = !builderModules.contains(annotatedClass.getModule()); if (deleteAnnotation != null) { handleDeletedClass(originalClass, deleteAnnotation); } else if (substituteAnnotation != null) { diff --git a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/WebImageSystemPropertiesSupport.java b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/WebImageSystemPropertiesSupport.java index e426ea31cd0f..47d01eab5a81 100644 --- a/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/WebImageSystemPropertiesSupport.java +++ b/web-image/src/com.oracle.svm.webimage/src/com/oracle/svm/webimage/WebImageSystemPropertiesSupport.java @@ -30,6 +30,10 @@ public class WebImageSystemPropertiesSupport extends SystemPropertiesSupport { + public WebImageSystemPropertiesSupport() { + super(false); + } + @Override protected String userNameValue() { return "No user name";