Skip to content
Merged
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
9 changes: 7 additions & 2 deletions docs/reference-manual/native-image/BuildOutput.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ This can also improve latency in some cases.
Use the `-R:MaxHeapSize` option when building with Native Image to preconfigure the maximum heap size.

#### <a name="glossary-image-assertions"></a>Assertions in the Generated Image
This shows whether Java assertions and system assertions are enabled in the generated image.
Enabling them can help identifying and debugging problems in the Java code built into the image.
This shows the hosted Java assertion defaults configured in the generated image.
Build-time-initialized classes always use these defaults.
When you build with `-H:-StrictRuntimeJavaOptions`, runtime-initialized image classes also use the build-time options
and runtime-loaded classes have assertions disabled unconditionally.
When you build with `-H:+StrictRuntimeJavaOptions`, runtime-initialized image classes and
runtime-loaded classes have their assertion status set by runtime `-ea`, `-da`, `-esa`, and `-dsa` options.
Enabling assertions can help identify and debug problems in the Java code built into the image.

#### <a name="glossary-experimental-options"></a>Experimental Options
A list of all active experimental options, including their origin and possible API option alternatives if available.
Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This changelog summarizes major changes to GraalVM Native Image.

## 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 ...`.
* (GR-71854) On Linux AMD64, Native Image now records the selected x86-64 ISA level in `.note.gnu.property` for `-march` values requiring x86-64-v2 or newer, so tools such as `readelf` report the requirement correctly.
* (GR-78784) Default to optional identity hash code fields with SerialGC. Few objects need one, and this optimization adds them during garbage collection. It can be disabled with `-H:-OptionalIdentityHashCodes`.
* (GR-78804) Added outlining for StringBuilder/StringBuffer append sequences and invokedynamic string concatenations. This reduces the binary size of native executables.
Expand Down
3 changes: 1 addition & 2 deletions substratevm/docs/crema-onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,7 @@ invocation (`CremaSupportImpl.java`: `invokeBasic`, `linkToVirtual`, `linkToStat
## Current boundaries

The current implementation still has several important boundaries: no parallel class loading, no JNI support for
runtime-loaded classes, no `condy`, fixed assertion status, and limited reflection for runtime-loaded
classes.
runtime-loaded classes, no `condy`, and limited reflection for runtime-loaded classes.

The code also shows a few areas that are still under construction:

Expand Down
12 changes: 11 additions & 1 deletion substratevm/docs/runtime-class-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,15 @@ This matches HotSpot-style resource lookup behavior where each directory resourc

## Current Limitations
* Parallel class loading is explicitly disabled and not supported.
* The assertion status of classes is fixed at image build time.
* Assertion status depends on whether you build with `-H:-StrictRuntimeJavaOptions` or
`-H:+StrictRuntimeJavaOptions`:
* Build-time-initialized classes use the assertion status configured when the image is built.
Runtime `-ea`, `-da`, `-esa`, and `-dsa` options never change their status.
* Runtime-initialized image classes use the build-time `-ea`, `-da`, `-esa`, and `-dsa`
options when you build with `-H:-StrictRuntimeJavaOptions`.
When you build with `-H:+StrictRuntimeJavaOptions`, runtime `-ea`, `-da`, `-esa`, and `-dsa`
options control their status instead.
* Runtime-loaded classes use runtime assertion options.
The runtime assertion options are supported only when the image was built with
`-H:+StrictRuntimeJavaOptions`.
* Methods or static fields removed by analysis from a class included in the image have no fallback and cause an error if run-time-loaded code uses them.
110 changes: 102 additions & 8 deletions substratevm/mx.substratevm/mx_substratevm.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ def __getattr__(self, name):
'standalone_pointsto_unittests',
'native_unittests',
'generic_field_type',
'runtime_assertions',
'all_native_unittests',
'java_desktop_integration',
'build',
Expand Down Expand Up @@ -565,6 +566,11 @@ def svm_gate_body(args, tasks):
if t:
generic_field_type_test_task(args.extra_image_builder_arguments)

runtime_assertions_tags = [GraalTags.runtime_assertions, GraalTags.native_unittests, GraalTags.all_native_unittests]
with Task('runtime assertions', tasks, tags=runtime_assertions_tags) as t:
if t:
runtime_assertions_test_task(args.extra_image_builder_arguments)

with Task('runtime classpath resource lookup', tasks, tags=[GraalTags.native_unittests]) as t:
if t:
with native_image_context(IMAGE_ASSERTION_FLAGS):
Expand Down Expand Up @@ -923,6 +929,93 @@ def generic_field_type_test_task(extra_build_args=None):
mx.abort('Unexpected generic field types: ' + str(field_output) + ' != ' + str(expected_field_output))


def runtime_assertions_test_task(extra_image_args=None):
test_dir = join(suite.dir, 'src', 'native-image-tests', 'runtime-assertions')
output_dir = join(svmbuild_dir(), 'runtime-assertions-test')
if exists(output_dir):
mx.rmtree(output_dir)
mx_util.ensure_dir_exists(output_dir)

sources = [
join(test_dir, 'RuntimeAssertions.java'),
join(test_dir, 'RuntimeLoadedAssertions.java'),
]
mx.run([mx.get_jdk().javac, '-d', output_dir] + sources)

test_class = 'runtimeassertions.RuntimeAssertions'
with native_image_context(IMAGE_ASSERTION_FLAGS) as native_image:
def build_assertion_image(name, assertion_args, image_options, main_class=test_class, classpath=output_dir, output_root=output_dir):
build_args = []
if classpath is not None:
build_args += ['-cp', classpath]
build_args += assertion_args + [
'-o', join(output_root, name),
] + svm_experimental_options(image_options)
if main_class is not None:
build_args.append(main_class)
if extra_image_args is not None:
build_args += extra_image_args
return native_image(build_args)

assertion_image = build_assertion_image('runtime-assertions', [
'-ea',
'-esa',
], [
'-H:+StrictRuntimeJavaOptions',
])

# Each execution starts with fresh runtime assertion directives.
test_cases = [
[],
['-ea'],
['-ea', '-da'],
['-ea:runtimeassertions.ClassEnabled'],
['-ea:runtimeassertions...', '-da:runtimeassertions.ClassDisabled'],
['-ea', '-da:runtimeassertions...', '-ea:runtimeassertions.ClassEnabled'],
['-esa'],
['-esa', '-dsa'],
['-enableassertions:runtimeassertions.ClassEnabled', '-enablesystemassertions'],
['-enableassertions', '-disableassertions', '-enablesystemassertions', '-disablesystemassertions'],
]
for runtime_args in test_cases:
scenario = " ".join(runtime_args)
mx.run([assertion_image] + runtime_args + ['--', scenario])

legacy_assertion_image = build_assertion_image('runtime-assertions-legacy', [
'-ea',
], [
'-H:-StrictRuntimeJavaOptions',
])
mx.run([legacy_assertion_image, 'legacy-build-time-status'])

runtime_loaded_excluded_image = build_assertion_image('runtime-assertions-runtime-loaded-excluded', [
'-ea',
], [
'-H:IncludeResources=runtimeassertions/RuntimeLoadedAssertions.class',
'-H:+StrictRuntimeJavaOptions',
'-H:+RuntimeClassLoading',
])
# Runtime-loaded classes must remain controlled by runtime directives even when image assertion code is excluded.
runtime_loaded_test_cases = [
([], False),
(['-ea'], True),
(['-ea:runtimeassertions...'], True),
(['-ea', '-da:runtimeassertions...'], False),
(['-ea:runtimeassertions...', '-da:runtimeassertions.RuntimeLoadedAssertions'], False),
]
for runtime_args, expected in runtime_loaded_test_cases:
scenario = 'runtime-loaded:' + str(expected).lower()
mx.run([runtime_loaded_excluded_image] + runtime_args + ['--', scenario])

default_assertion_image = build_assertion_image('runtime-assertions-default', [], ['-H:-StrictRuntimeJavaOptions'])
mx.run([default_assertion_image, 'code-excluded'])


@mx.command(suite.name, 'runtime-assertionstest', 'Builds and tests runtime assertion options in a native image.')
def runtime_assertionstest(args):
runtime_assertions_test_task(args)


def runtime_classpath_resource_test_task(extra_build_args=None):
svm_tests_jar = mx.distribution('substratevm:SVM_TESTS').path
build_args = svm_experimental_options(['-H:+ClassForNameRespectsClassLoader']) + [
Expand Down Expand Up @@ -2861,17 +2954,16 @@ def _hosted_boolean_option_defaults(native_image):
boolean_option_defaults = _hosted_boolean_option_defaults(native_image)
module_path_sep = ';' if mx.is_windows() else ':'
runtime_class_loading = _bool_option_value('RuntimeClassLoading', boolean_option_defaults)
strict_runtime_java_options = _bool_option_value('StrictRuntimeJavaOptions', boolean_option_defaults)

def moduletest_args(modules, extra_args=None):
return [
'-ea',
] + (extra_args or []) + [
def moduletest_args(modules, *, on_jvm, extra_args=None):
return (['-ea'] if on_jvm or not strict_runtime_java_options else []) + (extra_args or []) + [
'--add-exports=moduletests.hello.lib/hello.privateLib=moduletests.hello.app',
'--add-opens=moduletests.hello.lib/hello.privateLib2=moduletests.hello.app',
'-p', module_path_sep.join(modules), '-m', 'moduletests.hello.app'
]

moduletest_run_args = moduletest_args(module_path)
moduletest_run_args = moduletest_args(module_path, on_jvm=True)
mx.log('Running module-tests on JVM:')
build_dir = join(svmbuild_dir(), 'hellomodule')
mx.run([
Expand All @@ -2886,9 +2978,10 @@ def moduletest_args(modules, extra_args=None):
mx.run([
# On Windows, java is always an .exe, never a .cmd symlink
join(_vm_home(None), 'bin', mx.exe_suffix('java')),
] + moduletest_args(runtime_module_path, runtime_module_path_jvm_args))
] + moduletest_args(runtime_module_path, on_jvm=True, extra_args=runtime_module_path_jvm_args))

# Build module into native image
moduletest_run_args = moduletest_args(module_path, on_jvm=False)
mx.log('Building image from java modules: ' + str(module_path))
moduletest_build_args = list(moduletest_run_args)
if runtime_class_loading:
Expand All @@ -2899,10 +2992,11 @@ def moduletest_args(modules, extra_args=None):
['--verbose'] + svm_experimental_options(['-H:Path=' + build_dir]) + args + moduletest_build_args
)
mx.log('Running image ' + built_image + ' built from module without runtime module path:')
mx.run([built_image])
runtime_ea = ["-ea"] if "-ea" not in moduletest_build_args else []
mx.run([built_image] + runtime_ea)
if runtime_class_loading:
mx.log('Running image ' + built_image + ' built from module with runtime module path:')
runtime_module_path_args = [built_image, '-Dsvm.test.expectRuntimeModulePathFallback=true']
runtime_module_path_args = [built_image] + runtime_ea + ['-Dsvm.test.expectRuntimeModulePathFallback=true']
runtime_module_path_args.append('-Dsvm.test.expectRuntimeDefinedModuleLayer=true')
runtime_module_path_args.append('-Djava.home=' + _vm_home(None))
runtime_module_path_args.append('--module-path=' + module_path_sep.join(runtime_module_path))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import org.graalvm.word.impl.Word;

import com.oracle.svm.core.Isolates;
import com.oracle.svm.core.RuntimeAssertionsSupport;
import com.oracle.svm.core.AssertionsSupport;
import com.oracle.svm.guest.staging.SubstrateGCOptions;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.c.NonmovableArray;
Expand Down Expand Up @@ -530,7 +530,7 @@ private static void checkSanityAfterCollection() {

@Fold
static boolean runtimeAssertions() {
return RuntimeAssertionsSupport.singleton().desiredAssertionStatus(GCImpl.class);
return AssertionsSupport.singleton().desiredAssertionStatus(GCImpl.class);
}

@Fold
Expand Down
Loading