Skip to content

Commit 2ce932d

Browse files
committed
Adding the experimental option -H:Preserve
The `-H:Preserve` flag preserves all metadata from module, class-path entry, or a package in the image. This will allow users to more easily support third-party libraries and get more user-friendly experience with Native Image. The flag can be used in following ways: 1. -H:Preserve=all preserves all elements from the JDK and from the classpath 2. -H:Preserve=module=<module> preserves all elements from a given module 3. -H:Preserve=module=ALL-UNNAMED preserves all elements from the classpath 4. -H:Preserve=package=<package> preserves all elements from a given package 5. -H:Preserve=package=<package-wildcard> preserves all elements from packages captured by the wildcard. For example, -H:Preserve=package=my.app.*. 6. -H:Preserve=path=<cp-entry> preserves all elements from a given class-path entry 7. A combination of any of the previous uses, for example, -H:Preserve=path=<cp-entry>,module<module>
1 parent 2e20bdc commit 2ce932d

File tree

25 files changed

+719
-243
lines changed

25 files changed

+719
-243
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -231,7 +231,7 @@ public static void registerAllFields(Class<?> declaringClass) {
231231
* @since 23.0
232232
*/
233233
public static void registerAllDeclaredFields(Class<?> declaringClass) {
234-
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFieldsQuery(ConfigurationCondition.alwaysTrue(), declaringClass);
234+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(ConfigurationCondition.alwaysTrue(), declaringClass);
235235
}
236236

237237
/**

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -48,7 +48,7 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
4848

4949
void registerAllFieldsQuery(ConfigurationCondition condition, Class<?> clazz);
5050

51-
void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class<?> clazz);
51+
void registerAllDeclaredFields(ConfigurationCondition condition, Class<?> clazz);
5252

5353
void registerAllConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz);
5454

substratevm/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ This changelog summarizes major changes to GraalVM Native Image.
44

55
## GraalVM for JDK 25
66
* (GR-58668) Enabled [Whole-Program Sparse Conditional Constant Propagation (WP-SCCP)](https://github.com/oracle/graal/pull/9821) by default, improving the precision of points-to analysis in Native Image. This optimization enhances static analysis accuracy and scalability, potentially reducing the size of the final native binary.
7-
* (GR-59313) Deprecated class-level metadata extraction using `native-image-inspect` and removed option `DumpMethodsData`. Use class-level SBOMs instead by passing `--enable-sbom=class-level,export` to the `native-image` builder. The default value of option `IncludeMethodData` was changed to `false`.
7+
* (GR-59313) Deprecated class-level metadata extraction using `native-image-inspect` and removed option `DumpMethodsData`. Use class-level SBOMs instead by passing `--enable-sbom=class-level,export` to the `native-image` builder. The default value of option `IncludeMethodData` was changed to `false`.
88
* (GR-52400) The build process now uses 85% of system memory in containers and CI environments. Otherwise, it tries to only use available memory. If less than 8GB of memory are available, it falls back to 85% of system memory. The reason for the selected memory limit is now also shown in the build resources section of the build output.
99
* (GR-59864) Added JVM version check to the Native Image agent. The agent will abort execution if the JVM major version does not match the version it was built with, and warn if the full JVM version is different.
1010
* (GR-59135) Verify if hosted options passed to `native-image` exist prior to starting the builder. Provide suggestions how to fix unknown options early on.
1111
* (GR-61492) The experimental JDWP option is now present in standard GraalVM builds.
12+
* (GR-54953) Add the experimental option `-H:Preserve` that makes the program work correctly without providing reachability metadata. Correctness is achieved by preserving all classes, resources, and reflection metadata in the image. Usage: `-H:Preserve=[all|none|module=<module>|package=<package>|package=<package-wildcard>|path=<cp-entry>][,...]`.
1213

1314
## GraalVM for JDK 24 (Internal Version 24.2.0)
1415
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ClassInclusionPolicy.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,28 @@ public void setBigBang(BigBang bb) {
5656
this.bb = bb;
5757
}
5858

59+
public static boolean isClassIncludedBase(Class<?> cls) {
60+
if (Feature.class.isAssignableFrom(cls)) {
61+
return false;
62+
}
63+
64+
if (AnnotationAccess.isAnnotationPresent(cls, TargetClass.class)) {
65+
return false;
66+
}
67+
try {
68+
Class<?> enclosingClass = cls.getEnclosingClass();
69+
return enclosingClass == null || isClassIncludedBase(enclosingClass);
70+
} catch (LinkageError e) {
71+
return true;
72+
}
73+
}
74+
5975
/**
6076
* Determine if the given class needs to be included in the image according to the policy.
6177
*/
6278
public boolean isClassIncluded(Class<?> cls) {
6379
Class<?> enclosingClass = cls.getEnclosingClass();
64-
return !Feature.class.isAssignableFrom(cls) && !AnnotationAccess.isAnnotationPresent(cls, TargetClass.class) && (enclosingClass == null || isClassIncluded(enclosingClass));
80+
return isClassIncludedBase(cls) && (enclosingClass == null || isClassIncluded(enclosingClass));
6581
}
6682

6783
/**

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ public void onTypeInstantiated(AnalysisType type) {
666666
/* Register the type as instantiated with all its super types. */
667667

668668
assert type.isInstantiated() : type;
669-
AnalysisError.guarantee(type.isArray() || (type.isInstanceClass() && !type.isAbstract()));
669+
AnalysisError.guarantee(type.isArray() || (type.isInstanceClass() && !type.isAbstract()), "Type %s must be either an array, or a non abstract instance class", type.getName());
670670

671671
TypeState typeState = TypeState.forExactType(this, type, true);
672672
TypeState typeStateNonNull = TypeState.forExactType(this, type, false);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,8 @@ public enum ReportingMode {
13131313
@Option(help = "Deprecated, option no longer has any effect.", deprecated = true, deprecationMessage = "It no longer has any effect, and no replacement is available")//
13141314
public static final HostedOptionKey<Boolean> UseOldMethodHandleIntrinsics = new HostedOptionKey<>(false);
13151315

1316-
@Option(help = "Include all classes, methods, fields, and resources from the class path", type = OptionType.Debug) //
1317-
public static final HostedOptionKey<Boolean> IncludeAllFromClassPath = new HostedOptionKey<>(false);
1316+
@Option(help = "file:doc-files/PreserveHelp.txt")//
1317+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> Preserve = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build());
13181318

13191319
@Option(help = "Force include include all public types and methods that can be reached using normal Java access rules.")//
13201320
public static final HostedOptionKey<Boolean> UseBaseLayerInclusionPolicy = new HostedOptionKey<>(false);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Makes the program work correctly without providing reachability metadata.
2+
This is achieved by preserving all class elements, resources, and reflection metadata in the image.
3+
Usage of this options can cause a large increase in image size, and a small decrease in overall performance.
4+
5+
Usage: -H:Preserve=[all|none|module=<module>|package=<package>|package=<package-wildcard>|path=<cp-entry>][,...]
6+
7+
The flag can be used in following ways:
8+
1. -H:Preserve=all preserves all elements from the JDK, the classpath, and the module path
9+
2. -H:Preserve=module=<module> preserves all elements from a given module
10+
3. -H:Preserve=module=ALL-UNNAMED preserves all elements from all class-path entries
11+
4. -H:Preserve=package=<package> preserves all elements from a given package
12+
5. -H:Preserve=package=<package-wildcard> preserves all elements from packages captured by the wildcard. For example, -H:Preserve=package=my.app.*
13+
6. -H:Preserve=path=<cp-entry> preserves all elements from a given class-path entry
14+
7. -H:Preserve=none disables all previous selections for preservation
15+
8. A comma-separated list of the previous cases. For example, -H:Preserve=path=<cp-entry>,module=<module>,package=<package>
16+
17+
-H:Preserve is only allowed on the native-image command line and cannot be embedded in the native-image.properties files.

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;
2828

29+
import java.lang.reflect.Modifier;
2930
import java.util.EnumSet;
3031
import java.util.Objects;
3132

@@ -159,7 +160,8 @@ public void registerNegativeQuery(ConfigurationCondition condition, String class
159160

160161
@Platforms(Platform.HOSTED_ONLY.class)
161162
public void registerUnsafeAllocated(ConfigurationCondition condition, Class<?> clazz) {
162-
if (!clazz.isArray()) {
163+
if (!clazz.isArray() && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
164+
/* Otherwise, UNSAFE.allocateInstance results in InstantiationException */
163165
var conditionSet = unsafeInstantiatedClasses.putIfAbsent(clazz, RuntimeConditionSet.createHosted(condition));
164166
if (conditionSet != null) {
165167
conditionSet.addCondition(condition);

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/DefaultOptionHandler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
import com.oracle.svm.core.option.OptionOrigin;
3535
import com.oracle.svm.core.option.OptionUtils;
3636
import com.oracle.svm.driver.NativeImage.ArgumentQueue;
37-
import com.oracle.svm.hosted.imagelayer.LayerOptionsSupport.ExtendedOption;
38-
import com.oracle.svm.hosted.imagelayer.LayerOptionsSupport.LayerOption;
39-
import com.oracle.svm.hosted.imagelayer.LayerOptionsSupport.PackageOptionValue;
37+
import com.oracle.svm.hosted.driver.IncludeOptionsSupport;
38+
import com.oracle.svm.hosted.driver.IncludeOptionsSupport.ExtendedOption;
39+
import com.oracle.svm.hosted.driver.LayerOptionsSupport.LayerOption;
4040
import com.oracle.svm.util.LogUtils;
4141

4242
class DefaultOptionHandler extends NativeImage.OptionHandler<NativeImage> {
@@ -151,7 +151,7 @@ public boolean consume(ArgumentQueue args) {
151151
List<String> layerCreateValue = OptionUtils.resolveOptionValuesRedirection(SubstrateOptions.LayerCreate, rawLayerCreateValue, OptionOrigin.from(args.argumentOrigin));
152152
LayerOption layerOption = LayerOption.parse(layerCreateValue);
153153
for (ExtendedOption option : layerOption.extendedOptions()) {
154-
var packageOptionValue = PackageOptionValue.from(option);
154+
var packageOptionValue = IncludeOptionsSupport.PackageOptionValue.from(option);
155155
if (packageOptionValue == null) {
156156
continue;
157157
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public void collectResources(ResourceCollector resourceCollector) {
116116

117117
/* Collect remaining resources from classpath */
118118
classLoaderSupport.classpath().stream().parallel().forEach(classpathFile -> {
119-
boolean includeCurrent = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile) || classLoaderSupport.includeAllFromClassPath();
119+
boolean includeCurrent = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile) ||
120+
classLoaderSupport.getClassPathEntriesToPreserve().contains(classpathFile) || classLoaderSupport.preserveAll();
120121
try {
121122
if (Files.isDirectory(classpathFile)) {
122123
scanDirectory(classpathFile, resourceCollector, includeCurrent);
@@ -132,7 +133,8 @@ public void collectResources(ResourceCollector resourceCollector) {
132133
private void collectResourceFromModule(ResourceCollector resourceCollector, ResourceLookupInfo info) {
133134
ModuleReference moduleReference = info.resolvedModule.reference();
134135
try (ModuleReader moduleReader = moduleReference.open()) {
135-
boolean includeCurrent = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name());
136+
boolean includeCurrent = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()) ||
137+
classLoaderSupport.getJavaModuleNamesToPreserve().contains(info.resolvedModule().name());
136138
List<ConditionalResource> resourcesFound = new ArrayList<>();
137139
moduleReader.list().forEach(resourceName -> {
138140
var conditionsWithOrigins = shouldIncludeEntry(info.module, resourceCollector, resourceName, moduleReference.location().orElse(null), includeCurrent);

0 commit comments

Comments
 (0)