Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -52,6 +52,7 @@
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
import org.graalvm.nativeimage.hosted.FieldValueTransformerRequiresUniverse;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.graalvm.nativeimage.libgraal.hosted.LibGraalLoader;
Expand Down Expand Up @@ -261,7 +262,7 @@ void afterAnalysis(AfterAnalysisAccess access) {
* Transformer for {@code Fields.offsets} and {@code Edges.iterationMask} which need to be
* recomputed to use SVM field offsets instead of HotSpot field offsets.
*/
class FieldOffsetsTransformer implements FieldValueTransformer {
class FieldOffsetsTransformer implements FieldValueTransformer, FieldValueTransformerRequiresUniverse {
/**
* Map from {@link Fields} objects to a (newOffsets, newIterationMask) tuple represented as
* a {@link java.util.Map.Entry} value.
Expand Down
12 changes: 12 additions & 0 deletions docs/reference-manual/native-image/ClassInitialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ To accommodate this requirement, a short-term "relevant supertype" is used, as w
* Relevant supertypes of types initialized at build time must also be initialized at build time.
* Relevant subtypes of types initialized at runtime must also be initialized at runtime.
* No instances of classes that are initialized at runtime must be present in the executable.
* With `--future-defaults=predictable-build-time-initialization`, a class initialized at build time must not reference instances of run-time initialized classes, directly or indirectly, through its static fields,
even if reachability analysis does not mark those instances as reachable.

To enjoy the complete out-of-the-box experience of Native Image and still get the benefits of build-time initialization, Native Image does two things:
* [Build-Time Initialization](#build-time-initialization)
Expand All @@ -41,6 +43,16 @@ Native Image initializes most JDK classes at build time, including the garbage c
For all of the classes that are initialized at build time, Native Image gives proper support so that the semantics remain consistent despite class initialization occurring at build time.
If you discover an issue with a JDK class behaving incorrectly because of class initialization at build time, please [report an issue](https://github.com/oracle/graal/issues/new).

### Verification of Objects Referenced by Static Fields

By default, Native Image verifies that objects included in the executable's image heap are instances of build-time initialized classes and are allowed in the image heap.

With `--future-defaults=predictable-build-time-initialization`, this verification extends to any object referenced directly
or indirectly by static fields of build-time initialized classes that could become part of the image heap.
This makes reporting of class initialization violations more reliable, as these errors are no longer masked by reachability.
If an object is found to be an instance of a run-time initialized class, the build fails with an error showing a path from a
build-time initialized class to the offending object and suggesting how to resolve the violation.

## Automatic Initialization of Safe Classes

For application classes, Native Image tries to find classes that can be safely initialized at build time.
Expand Down
10 changes: 9 additions & 1 deletion sdk/src/org.graalvm.nativeimage/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,7 @@ innr public abstract interface static AfterImageWriteAccess
innr public abstract interface static AfterRegistrationAccess
innr public abstract interface static BeforeAnalysisAccess
innr public abstract interface static BeforeCompilationAccess
innr public abstract interface static BeforeFieldValueAccess
innr public abstract interface static BeforeHeapLayoutAccess
innr public abstract interface static BeforeImageWriteAccess
innr public abstract interface static BeforeUniverseBuildingAccess
Expand Down Expand Up @@ -1054,22 +1055,27 @@ meth public abstract org.graalvm.nativeimage.dynamicaccess.ResourceAccess getRes

CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess
outer org.graalvm.nativeimage.hosted.Feature
intf org.graalvm.nativeimage.hosted.Feature$BeforeFieldValueAccess
intf org.graalvm.nativeimage.hosted.Feature$FeatureAccess
meth public abstract !varargs void registerReachabilityHandler(java.util.function.Consumer<org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess>,java.lang.Object[])
meth public abstract void registerAsAccessed(java.lang.reflect.Field)
meth public abstract void registerAsInHeap(java.lang.Class<?>)
meth public abstract void registerAsUnsafeAccessed(java.lang.reflect.Field)
meth public abstract void registerAsUnsafeAllocated(java.lang.Class<?>)
meth public abstract void registerAsUsed(java.lang.Class<?>)
meth public abstract void registerBeforeFieldValueAccessHandler(java.util.function.BiConsumer<org.graalvm.nativeimage.hosted.Feature$BeforeFieldValueAccess,java.lang.Class<?>>,java.lang.Class<?>)
meth public abstract void registerClassInitializerReachabilityHandler(java.util.function.Consumer<org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess>,java.lang.Class<?>)
meth public abstract void registerFieldValueTransformer(java.lang.reflect.Field,org.graalvm.nativeimage.hosted.FieldValueTransformer)
meth public abstract void registerMethodOverrideReachabilityHandler(java.util.function.BiConsumer<org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess,java.lang.reflect.Executable>,java.lang.reflect.Executable)
meth public abstract void registerSubtypeReachabilityHandler(java.util.function.BiConsumer<org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess,java.lang.Class<?>>,java.lang.Class<?>)

CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeCompilationAccess
outer org.graalvm.nativeimage.hosted.Feature
intf org.graalvm.nativeimage.hosted.Feature$CompilationAccess

CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeFieldValueAccess
outer org.graalvm.nativeimage.hosted.Feature
meth public abstract void registerFieldValueTransformer(java.lang.reflect.Field,org.graalvm.nativeimage.hosted.FieldValueTransformer)

CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeHeapLayoutAccess
outer org.graalvm.nativeimage.hosted.Feature
intf org.graalvm.nativeimage.hosted.Feature$CompilationAccess
Expand Down Expand Up @@ -1135,6 +1141,8 @@ CLSS public abstract interface org.graalvm.nativeimage.hosted.FieldValueTransfor
meth public abstract java.lang.Object transform(java.lang.Object,java.lang.Object)
meth public boolean isAvailable()

CLSS public abstract interface org.graalvm.nativeimage.hosted.FieldValueTransformerRequiresUniverse

CLSS public final org.graalvm.nativeimage.hosted.RuntimeClassInitialization
meth public !varargs static void initializeAtBuildTime(java.lang.Class<?>[])
meth public !varargs static void initializeAtBuildTime(java.lang.String[])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,36 @@ interface DuringSetupAccess extends FeatureAccess {
void registerBuildTimeBootstrapCondy(Executable method);
}

/**
* Interface that encapsulates registration of field value transformers. Under
* {@code --future-defaults=predictable-build-time-initialization}, registration of transformers
* will only be possible before analysis starts or in a
* {@link BeforeAnalysisAccess#registerBeforeFieldValueAccessHandler(BiConsumer, Class) before-field-value-access handler},
* which is triggered before the first field of the selected class is accessed.
*/
@Platforms(Platform.HOSTED_ONLY.class)
interface BeforeFieldValueAccess {
/**
* Registers a field value transformer for the provided field.
* Under {@code --future-defaults=predictable-build-time-initialization}, registration of
* transformers will only be possible before analysis starts or in a
* {@link BeforeAnalysisAccess#registerBeforeFieldValueAccessHandler(BiConsumer, Class) before-field-value-access handler}.
*
* <p>
* See the JavaDoc of
* {@link FieldValueTransformer} for details.
* </p>
*/
void registerFieldValueTransformer(Field field, FieldValueTransformer transformer);
}

/**
* Access methods available for {@link Feature#beforeAnalysis}.
*
* @since 19.0
*/
@Platforms(Platform.HOSTED_ONLY.class)
interface BeforeAnalysisAccess extends FeatureAccess {
interface BeforeAnalysisAccess extends FeatureAccess, BeforeFieldValueAccess {

/**
* Registers the provided type a used, i.e., metadata for the type is put into the native
Expand Down Expand Up @@ -380,12 +403,18 @@ interface BeforeAnalysisAccess extends FeatureAccess {
void registerClassInitializerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Class<?> clazz);

/**
* Registers a field value transformer for the provided field. See the JavaDoc of
* {@link FieldValueTransformer} for details.
*
* @since 22.3
* Registers a callback that allows registration of field value transformers on demand. The
* callback is triggered for every subclass of {@code baseClass}, including
* {@code baseClass} itself, before the first field value of the class is accessed. The class
* is passed as the callback's second parameter.
*
* <p>
* This is a replacement for registration done in type/subtype reachability handlers, where
* this behavior can no longer be supported. Handlers must be registered in a
* {@link Feature#beforeAnalysis beforeAnalysis} callback.
* </p>
*/
void registerFieldValueTransformer(Field field, FieldValueTransformer transformer);
void registerBeforeFieldValueAccessHandler(BiConsumer<BeforeFieldValueAccess, Class<?>> callback, Class<?> baseClass);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature.BeforeAnalysisAccess;

import java.util.function.BiConsumer;

/**
* A transformer for a field value that can be registered using
* {@link BeforeAnalysisAccess#registerFieldValueTransformer}.
* {@link BeforeAnalysisAccess#registerFieldValueTransformer} or in a
* {@link BeforeAnalysisAccess#registerBeforeFieldValueAccessHandler(BiConsumer, Class) before-field-value-access handler}.
* <p>
* At image build time, the field value transformer provides the value of the field for the image
* heap. Without a transformer, the value of the field in the image heap is the same as the hosted
Expand All @@ -70,6 +73,12 @@
* reachability handler} for the declaring class of the field, or a
* {@link BeforeAnalysisAccess#registerSubtypeReachabilityHandler subtype reachability handler} for
* a super-type of the declaring class of the field.
* <p>
* Under {@code --future-defaults=predictable-build-time-initialization},
* registration of transformers will only be possible before static analysis starts
* or in a {@link BeforeAnalysisAccess#registerBeforeFieldValueAccessHandler(BiConsumer, Class) before-field-value-access handler}.
* The handler is triggered for each subclass, including the base class, before its first field
* value is accessed.
*
* @since 22.3
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 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.
*/
package org.graalvm.nativeimage.hosted;

/**
* Marker interface to denote that a field value transformer
* requires the analysis/hosted universe or internal operations that expand the universe or image heap,
* such as creating new universe types, methods or fields, modifying existing ones,
* marking constants for heap scanning, or adding new entries to an {@code ImageHeapList}
* or {@code ImageHeapMap}.
*
* <p>
* For example, {@code com.oracle.svm.core.reflect.target.ExecutableAccessorComputer}
* may create an accessor for a method or a constructor, which requires the analysis.
* </p>
*
* <p>
* This marker interface is used by {@code PredictableBuildTimeInitializationFeature}
* to differentiate between fields that can be read outside the universe and
* those that cannot be read are processed only if the field is read and its receiver is reachable.
* </p>
*
* <p>
* This marker interface can be used on external code, but is mostly relevant for internal transformers.
* It is placed in this package in order to be available for {@code jdk.graal.compiler.libgraal.LibGraalFeature}.
* </p>
*/
public interface FieldValueTransformerRequiresUniverse {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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
Expand Down Expand Up @@ -47,6 +47,10 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

@Platforms(Platform.HOSTED_ONLY.class)
public class ClassInitializationTracking {

public static final Map<Class<?>, StackTraceElement[]> initializedClasses = new ConcurrentHashMap<>();
Expand Down
2 changes: 2 additions & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-77491) Bundle format version is now 2.0, with explicit path root kinds, build argument groups that retain their source platform, and persistent markers for unavailable inputs.
Older bundle formats remain readable.
* (GR-79366) Add auxiliary images: a mechanism for persisting runtime-allocated objects and runtime-compiled code, and loading them in later executions of the same native image.
* (GR-51046) Introduced `--future-defaults=predictable-build-time-initialization`, which verifies that the classes of all objects stored in or reachable from static fields of build-time-initialized classes are also initialized at build time, even if those fields are not reachable. It also forbids registration of `FieldValueTransformer`s during analysis or in a reachability handler.
* (GR-51046) Added `BeforeAnalysis#registerBeforeFieldValueAccessHandler`, which registers a handler that allows field value transformer registration on demand, before field values are accessed.

## GraalVM 25.4 (Internal Version 25.4.4)
* (GR-75824) When native executables are built with `-H:+StrictRuntimeJavaOptions`, runtime assertion options (for example, `-ea`, `-da`, `-esa`, and `-dsa`) are supported and configure the assertion status of runtime-loaded classes and runtime-initialized image classes. They do not affect build-time-initialized classes whose assertion status is *only* configured by `native-image -ea ...`.
Expand Down
16 changes: 13 additions & 3 deletions substratevm/mx.substratevm/mx_substratevm_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,20 +726,22 @@ def _empty_file():

_DACAPO_EXTRA_IMAGE_BUILD_ARGS = {
'h2' : [],
'pmd': [],
'pmd': ['--initialize-at-build-time=org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl'],
# org.apache.crimson.parser.Parser2 is force initialized at build-time due to non-determinism in class initialization
# order that can lead to runtime issues. See GR-26324.
'xalan': ['--initialize-at-build-time=org.apache.crimson.parser.Parser2,org.apache.crimson.parser.Parser2$Catalog,org.apache.crimson.parser.Parser2$NullHandler,org.apache.xml.utils.res.CharArrayWrapper'],
# There are two main issues with fop:
# There are three main issues with fop:
# 1. LoggingFeature is enabled by default, causing the LogManager configuration to be parsed at build-time. However
# DaCapo Harness sets the `java.util.logging.config.file` property at run-time. Therefore, we set
# `java.util.logging.config.file` to an empty file to avoid incorrectly parsing the default log configuration,
# leading to output on stderr. We cannot set it to scratch/fop.log as it would normally be, because the file does
# not exist and would fail the benchmark when assertions are enabled.
# 2. Native-image picks a different service provider than the JVM for javax.xml.transform.TransformerFactory.
# We can simply remove the jar containing that provider as it is not required for the benchmark to run.
# 3. The Xerces DatatypeFactory provider is reachable from build-time initialized JAXB state.
'fop': [f"-Djava.util.logging.config.file={_empty_file()}",
'--initialize-at-run-time=org.apache.fop.render.rtf.rtflib.rtfdoc.RtfList'],
'--initialize-at-run-time=org.apache.fop.render.rtf.rtflib.rtfdoc.RtfList',
'--initialize-at-build-time=org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl'],
'batik': []
}

Expand Down Expand Up @@ -952,6 +954,10 @@ def successPatterns(self):
_scala_daCapo_additional_lib = {
}

_scala_daCapo_extra_image_build_args = {
'scalac': ['--initialize-at-build-time=org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl'],
}


class ScalaDaCapoNativeImageBenchmarkSuite(mx_sdk_benchmark.ScalaDaCapoBenchmarkSuite, BaseDaCapoNativeImageBenchmarkSuite, mx_sdk_benchmark.NativeImageBenchmarkMixin): #pylint: disable=too-many-ancestors
def name(self):
Expand Down Expand Up @@ -996,6 +1002,10 @@ def skip_agent_assertions(self, benchmark, args):
else:
return []

def extra_image_build_argument(self, benchmark, args):
default_args = _scala_daCapo_extra_image_build_args.get(benchmark, [])
return default_args + super().extra_image_build_argument(benchmark, args)

def createCommandLineArgs(self, benchmarks, bmSuiteArgs):
if benchmarks is None:
mx.abort("Suite can only run a single benchmark per VM instance.")
Expand Down
Loading