mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8368174: Proactive initialization of @AOTSafeClassInitializer classes
Reviewed-by: liach, adinn, asmehra
This commit is contained in:
parent
8f87fdce0b
commit
17accf4a06
@ -48,7 +48,14 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
|
||||
ik = RegeneratedClasses::get_original_object(ik);
|
||||
}
|
||||
|
||||
check_aot_annotations(ik);
|
||||
|
||||
if (!ik->is_initialized() && !ik->is_being_initialized()) {
|
||||
if (ik->has_aot_safe_initializer()) {
|
||||
ResourceMark rm;
|
||||
log_info(aot, init)("Class %s is annotated with @AOTSafeClassInitializer but has not been initialized",
|
||||
ik->external_name());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -245,6 +252,56 @@ void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass*
|
||||
}
|
||||
}
|
||||
|
||||
template <typename FUNCTION>
|
||||
void require_annotation_for_super_types(InstanceKlass* ik, const char* annotation, FUNCTION func) {
|
||||
if (log_is_enabled(Info, aot, init)) {
|
||||
ResourceMark rm;
|
||||
log_info(aot, init)("Found %s class %s", annotation, ik->external_name());
|
||||
}
|
||||
|
||||
// Since ik has this annotation, we require that
|
||||
// - all super classes must have this annotation
|
||||
// - all super interfaces that are interface_needs_clinit_execution_as_super()
|
||||
// must have this annotation
|
||||
// This avoid the situation where in the production run, we run the <clinit>
|
||||
// of a supertype but not the <clinit> of ik
|
||||
|
||||
InstanceKlass* super = ik->java_super();
|
||||
if (super != nullptr && !func(super)) {
|
||||
ResourceMark rm;
|
||||
log_error(aot, init)("Missing %s in superclass %s for class %s",
|
||||
annotation, super->external_name(), ik->external_name());
|
||||
AOTMetaspace::unrecoverable_writing_error();
|
||||
}
|
||||
|
||||
int len = ik->local_interfaces()->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InstanceKlass* intf = ik->local_interfaces()->at(i);
|
||||
if (intf->interface_needs_clinit_execution_as_super() && !func(intf)) {
|
||||
ResourceMark rm;
|
||||
log_error(aot, init)("Missing %s in superinterface %s for class %s",
|
||||
annotation, intf->external_name(), ik->external_name());
|
||||
AOTMetaspace::unrecoverable_writing_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOTClassInitializer::check_aot_annotations(InstanceKlass* ik) {
|
||||
if (ik->has_aot_safe_initializer()) {
|
||||
require_annotation_for_super_types(ik, "@AOTSafeClassInitializer", [&] (const InstanceKlass* supertype) {
|
||||
return supertype->has_aot_safe_initializer();
|
||||
});
|
||||
} else {
|
||||
// @AOTRuntimeSetup only meaningful in @AOTSafeClassInitializer
|
||||
if (ik->is_runtime_setup_required()) {
|
||||
ResourceMark rm;
|
||||
log_error(aot, init)("@AOTRuntimeSetup meaningless in non-@AOTSafeClassInitializer class %s",
|
||||
ik->external_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ASSERT
|
||||
void AOTClassInitializer::init_test_class(TRAPS) {
|
||||
// -XX:AOTInitTestClass is used in regression tests for adding additional AOT-initialized classes
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
class InstanceKlass;
|
||||
|
||||
class AOTClassInitializer : AllStatic {
|
||||
|
||||
static void check_aot_annotations(InstanceKlass* ik);
|
||||
|
||||
public:
|
||||
// Called by heapShared.cpp to see if src_ik->java_mirror() can be archived in
|
||||
// the initialized state.
|
||||
|
||||
@ -804,6 +804,23 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
AOTClassLinker::initialize();
|
||||
AOTClassInitializer::init_test_class(CHECK);
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
// - Load and link all classes used in the training run.
|
||||
// - Initialize @AOTSafeClassInitializer classes that were
|
||||
// initialized in the training run.
|
||||
// - Perform per-class optimization such as AOT-resolution of
|
||||
// constant pool entries that were resolved during the training run.
|
||||
FinalImageRecipes::apply_recipes(CHECK);
|
||||
|
||||
// Because the AOT assembly phase does not run the same exact code as in the
|
||||
// training run (e.g., we use different lambda form invoker classes;
|
||||
// generated lambda form classes are not recorded in FinalImageRecipes),
|
||||
// the recipes do not cover all classes that have been loaded so far. As
|
||||
// a result, we might have some unlinked classes at this point. Since we
|
||||
// require cached classes to be linked, all such classes will be linked
|
||||
// by the following step.
|
||||
}
|
||||
|
||||
link_all_loaded_classes(THREAD);
|
||||
|
||||
// Eargerly resolve all string constants in constant pools
|
||||
@ -817,10 +834,6 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
AOTConstantPoolResolver::preresolve_string_cp_entries(ik, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
FinalImageRecipes::apply_recipes(CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMetaspace::dump_static_archive(TRAPS) {
|
||||
|
||||
@ -57,7 +57,7 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
// ignored during the final image assembly.
|
||||
|
||||
GrowableArray<Array<int>*> tmp_cp_recipes;
|
||||
GrowableArray<int> tmp_cp_flags;
|
||||
GrowableArray<int> tmp_flags;
|
||||
|
||||
GrowableArray<Klass*>* klasses = ArchiveBuilder::current()->klasses();
|
||||
for (int i = 0; i < klasses->length(); i++) {
|
||||
@ -70,12 +70,16 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
ConstantPool* cp = ik->constants();
|
||||
ConstantPoolCache* cp_cache = cp->cache();
|
||||
|
||||
if (ik->is_initialized()) {
|
||||
flags |= WAS_INITED;
|
||||
}
|
||||
|
||||
for (int cp_index = 1; cp_index < cp->length(); cp_index++) { // Index 0 is unused
|
||||
if (cp->tag_at(cp_index).value() == JVM_CONSTANT_Class) {
|
||||
Klass* k = cp->resolved_klass_at(cp_index);
|
||||
if (k->is_instance_klass()) {
|
||||
cp_indices.append(cp_index);
|
||||
flags |= HAS_CLASS;
|
||||
flags |= CP_RESOLVE_CLASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,7 +92,7 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
if (rfe->is_resolved(Bytecodes::_getfield) ||
|
||||
rfe->is_resolved(Bytecodes::_putfield)) {
|
||||
cp_indices.append(rfe->constant_pool_index());
|
||||
flags |= HAS_FIELD_AND_METHOD;
|
||||
flags |= CP_RESOLVE_FIELD_AND_METHOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,7 +107,7 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
rme->is_resolved(Bytecodes::_invokestatic) ||
|
||||
rme->is_resolved(Bytecodes::_invokehandle)) {
|
||||
cp_indices.append(rme->constant_pool_index());
|
||||
flags |= HAS_FIELD_AND_METHOD;
|
||||
flags |= CP_RESOLVE_FIELD_AND_METHOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +119,7 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
int cp_index = rie->constant_pool_index();
|
||||
if (rie->is_resolved()) {
|
||||
cp_indices.append(cp_index);
|
||||
flags |= HAS_INDY;
|
||||
flags |= CP_RESOLVE_INDY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,14 +131,14 @@ void FinalImageRecipes::record_recipes_for_constantpool() {
|
||||
} else {
|
||||
tmp_cp_recipes.append(nullptr);
|
||||
}
|
||||
tmp_cp_flags.append(flags);
|
||||
tmp_flags.append(flags);
|
||||
}
|
||||
|
||||
_cp_recipes = ArchiveUtils::archive_array(&tmp_cp_recipes);
|
||||
ArchivePtrMarker::mark_pointer(&_cp_recipes);
|
||||
|
||||
_cp_flags = ArchiveUtils::archive_array(&tmp_cp_flags);
|
||||
ArchivePtrMarker::mark_pointer(&_cp_flags);
|
||||
_flags = ArchiveUtils::archive_array(&tmp_flags);
|
||||
ArchivePtrMarker::mark_pointer(&_flags);
|
||||
}
|
||||
|
||||
void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) {
|
||||
@ -142,7 +146,7 @@ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) {
|
||||
|
||||
for (int i = 0; i < _all_klasses->length(); i++) {
|
||||
Array<int>* cp_indices = _cp_recipes->at(i);
|
||||
int flags = _cp_flags->at(i);
|
||||
int flags = _flags->at(i);
|
||||
if (cp_indices != nullptr) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(_all_klasses->at(i));
|
||||
if (ik->is_loaded()) {
|
||||
@ -152,13 +156,13 @@ void FinalImageRecipes::apply_recipes_for_constantpool(JavaThread* current) {
|
||||
for (int j = 0; j < cp_indices->length(); j++) {
|
||||
preresolve_list.at_put(cp_indices->at(j), true);
|
||||
}
|
||||
if ((flags & HAS_CLASS) != 0) {
|
||||
if ((flags & CP_RESOLVE_CLASS) != 0) {
|
||||
AOTConstantPoolResolver::preresolve_class_cp_entries(current, ik, &preresolve_list);
|
||||
}
|
||||
if ((flags & HAS_FIELD_AND_METHOD) != 0) {
|
||||
if ((flags & CP_RESOLVE_FIELD_AND_METHOD) != 0) {
|
||||
AOTConstantPoolResolver::preresolve_field_and_method_cp_entries(current, ik, &preresolve_list);
|
||||
}
|
||||
if ((flags & HAS_INDY) != 0) {
|
||||
if ((flags & CP_RESOLVE_INDY) != 0) {
|
||||
AOTConstantPoolResolver::preresolve_indy_cp_entries(current, ik, &preresolve_list);
|
||||
}
|
||||
}
|
||||
@ -171,6 +175,7 @@ void FinalImageRecipes::load_all_classes(TRAPS) {
|
||||
Handle class_loader(THREAD, SystemDictionary::java_system_loader());
|
||||
for (int i = 0; i < _all_klasses->length(); i++) {
|
||||
Klass* k = _all_klasses->at(i);
|
||||
int flags = _flags->at(i);
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (ik->defined_by_other_loaders()) {
|
||||
@ -188,6 +193,11 @@ void FinalImageRecipes::load_all_classes(TRAPS) {
|
||||
}
|
||||
assert(ik->is_loaded(), "must be");
|
||||
ik->link_class(CHECK);
|
||||
|
||||
if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) {
|
||||
assert(ik->class_loader() == nullptr, "supported only for boot classes for now");
|
||||
ik->initialize(CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,20 +42,21 @@ template <typename T> class Array;
|
||||
// - The list of all classes that are stored in the AOTConfiguration file.
|
||||
// - The list of all classes that require AOT resolution of invokedynamic call sites.
|
||||
class FinalImageRecipes {
|
||||
static constexpr int HAS_CLASS = 0x1;
|
||||
static constexpr int HAS_FIELD_AND_METHOD = 0x2;
|
||||
static constexpr int HAS_INDY = 0x4;
|
||||
static constexpr int CP_RESOLVE_CLASS = 0x1 << 0; // CP has preresolved class entries
|
||||
static constexpr int CP_RESOLVE_FIELD_AND_METHOD = 0x1 << 1; // CP has preresolved field/method entries
|
||||
static constexpr int CP_RESOLVE_INDY = 0x1 << 2; // CP has preresolved indy entries
|
||||
static constexpr int WAS_INITED = 0x1 << 3; // Class was initialized during training run
|
||||
|
||||
// A list of all the archived classes from the preimage. We want to transfer all of these
|
||||
// into the final image.
|
||||
Array<Klass*>* _all_klasses;
|
||||
|
||||
// For each klass k _all_klasses->at(i), _cp_recipes->at(i) lists all the {klass,field,method,indy}
|
||||
// cp indices that were resolved for k during the training run.
|
||||
// For each klass k _all_klasses->at(i): _cp_recipes->at(i) lists all the {klass,field,method,indy}
|
||||
// cp indices that were resolved for k during the training run; _flags->at(i) has extra info about k.
|
||||
Array<Array<int>*>* _cp_recipes;
|
||||
Array<int>* _cp_flags;
|
||||
Array<int>* _flags;
|
||||
|
||||
FinalImageRecipes() : _all_klasses(nullptr), _cp_recipes(nullptr), _cp_flags(nullptr) {}
|
||||
FinalImageRecipes() : _all_klasses(nullptr), _cp_recipes(nullptr), _flags(nullptr) {}
|
||||
|
||||
void* operator new(size_t size) throw();
|
||||
|
||||
|
||||
@ -5163,46 +5163,6 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
|
||||
if (_parsed_annotations->has_any_annotations())
|
||||
_parsed_annotations->apply_to(ik);
|
||||
|
||||
// AOT-related checks.
|
||||
// Note we cannot check this in general due to instrumentation or module patching
|
||||
if (CDSConfig::is_initing_classes_at_dump_time()) {
|
||||
// Check the aot initialization safe status.
|
||||
// @AOTSafeClassInitializer is used only to support ahead-of-time initialization of classes
|
||||
// in the AOT assembly phase.
|
||||
if (ik->has_aot_safe_initializer()) {
|
||||
// If a type is included in the tables inside can_archive_initialized_mirror(), we require that
|
||||
// - all super classes must be included
|
||||
// - all super interfaces that have <clinit> must be included.
|
||||
// This ensures that in the production run, we don't run the <clinit> of a supertype but skips
|
||||
// ik's <clinit>.
|
||||
if (_super_klass != nullptr) {
|
||||
guarantee_property(_super_klass->has_aot_safe_initializer(),
|
||||
"Missing @AOTSafeClassInitializer in superclass %s for class %s",
|
||||
_super_klass->external_name(),
|
||||
CHECK);
|
||||
}
|
||||
|
||||
int len = _local_interfaces->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InstanceKlass* intf = _local_interfaces->at(i);
|
||||
guarantee_property(intf->class_initializer() == nullptr || intf->has_aot_safe_initializer(),
|
||||
"Missing @AOTSafeClassInitializer in superinterface %s for class %s",
|
||||
intf->external_name(),
|
||||
CHECK);
|
||||
}
|
||||
|
||||
if (log_is_enabled(Info, aot, init)) {
|
||||
ResourceMark rm;
|
||||
log_info(aot, init)("Found @AOTSafeClassInitializer class %s", ik->external_name());
|
||||
}
|
||||
} else {
|
||||
// @AOTRuntimeSetup only meaningful in @AOTClassInitializer
|
||||
guarantee_property(!ik->is_runtime_setup_required(),
|
||||
"@AOTRuntimeSetup meaningless in non-@AOTSafeClassInitializer class %s",
|
||||
CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
apply_parsed_class_attributes(ik);
|
||||
|
||||
// Miranda methods
|
||||
|
||||
@ -26,12 +26,14 @@
|
||||
package jdk.internal.math;
|
||||
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
|
||||
|
||||
/**
|
||||
* This class exposes package private utilities for other classes.
|
||||
* Thus, all methods are assumed to be invoked with correct arguments,
|
||||
* so these are not checked at all.
|
||||
*/
|
||||
@AOTSafeClassInitializer
|
||||
final class MathUtils {
|
||||
/*
|
||||
* For full details about this code see the following reference:
|
||||
|
||||
@ -30,25 +30,34 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/// Indicates that the static initializer of this class or interface
|
||||
/// (its `<clinit>` method) is allowed to be _AOT-initialized_,
|
||||
/// because its author considers it safe to execute during the AOT
|
||||
/// assembly phase.
|
||||
/// Indicates that the annotated class or interface is allowed to be _AOT-initialized_,
|
||||
/// because its author considers it safe to execute the static initializer of
|
||||
/// the class or interface during the AOT assembly phase.
|
||||
///
|
||||
/// This annotation directs the VM to expect that normal execution of Java code
|
||||
/// during the assembly phase could trigger initialization of this class,
|
||||
/// and if that happens, to store the resulting static field values in the
|
||||
/// AOT cache. (These fields happen to be allocated in the `Class` mirror.)
|
||||
/// For a class or interface _X_ annotated with `@AOTSafeClassInitializer`, it will
|
||||
/// be initialized in the AOT assembly phase under two circumstances:
|
||||
///
|
||||
/// During the production run, the static initializer (`<clinit>`) of
|
||||
/// this class or interface will not be executed, if it was already
|
||||
/// executed during the assembling of the AOT being used to start the
|
||||
/// production run. In that case the resulting static field states
|
||||
/// (within the `Class` mirror) were already stored in the AOT cache.
|
||||
/// 1. If _X_ was initialized during the AOT training run, the JVM will proactively
|
||||
/// initialize _X_ in the assembly phase.
|
||||
/// 2. If _X_ was not initialized during the AOT training run, the initialization of
|
||||
/// _X_ can still be triggered by normal execution of Java code in the assembly
|
||||
/// phase. At present this is usually the result of performing AOT optimizations for
|
||||
/// the `java.lang.invoke` package but it may include other cases as well.
|
||||
///
|
||||
/// Currently, this annotation is used mainly for supporting AOT
|
||||
/// linking of APIs, including bootstrap methods, in the
|
||||
/// `java.lang.invoke` package.
|
||||
/// If _X_ is initialized during the AOT assembly phase, the VM will store
|
||||
/// the values of the static fields of _X_ in the AOT cache. Consequently,
|
||||
/// during the production run that uses this AOT cache, the static initializer
|
||||
/// (`<clinit>`) of _X_ will not be executed. _X_ will appear to be in the
|
||||
/// "initialized" state and all the cached values of the static field of _X_
|
||||
/// will be available immediately upon the start of the prodcution run.
|
||||
///
|
||||
/// Currently, this annotation is used mainly for two purposes:
|
||||
///
|
||||
/// - To AOT-initialize complex static fields whose values are always the same
|
||||
/// across JVM lifetimes. One example is the tables of constant values
|
||||
/// in the `jdk.internal.math.MathUtils` class.
|
||||
/// - To support AOT linking of APIs, including bootstrap methods, in the
|
||||
/// `java.lang.invoke` package.
|
||||
///
|
||||
/// In more detail, the AOT assembly phase performs the following:
|
||||
///
|
||||
@ -62,6 +71,8 @@ import java.lang.annotation.Target;
|
||||
/// along with every relevant superclass and implemented interface, along
|
||||
/// with classes for every object created during the course of static
|
||||
/// initialization (running `<clinit>` for each such class or interface).
|
||||
/// 5. In addition, any class/interface annotated with `@AOTSafeClassInitializer`
|
||||
/// that was initialized during the training run is proactively initialized.
|
||||
///
|
||||
/// Thus, in order to determine that a class or interface _X_ is safe to
|
||||
/// AOT-initialize requires evaluating every other class or interface _Y_ that
|
||||
@ -112,21 +123,18 @@ import java.lang.annotation.Target;
|
||||
/// remotely) if the execution of such an API touches _X_ for initialization,
|
||||
/// or even if such an API request is in any way sensitive to values stored in
|
||||
/// the fields of _X_, even if the sensitivity is a simple reference identity
|
||||
/// test. As noted above, all supertypes of _X_ must also have the
|
||||
/// `@AOTSafeClassInitializer` annotation, and must also be safe for AOT
|
||||
/// initialization.
|
||||
/// test.
|
||||
///
|
||||
/// The author of an AOT-initialized class may elect to patch some states at
|
||||
/// production startup, using an [AOTRuntimeSetup] method, as long as the
|
||||
/// pre-patched field values (present during AOT assembly) are determined to be
|
||||
/// compatible with the post-patched values that apply to the production run.
|
||||
///
|
||||
/// In the assembly phase, `classFileParser.cpp` performs checks on the annotated
|
||||
/// classes, to ensure all supertypes of this class that must be initialized when
|
||||
/// this class is initialized have the `@AOTSafeClassInitializer` annotation.
|
||||
/// Otherwise, a [ClassFormatError] will be thrown. (This assembly phase restriction
|
||||
/// allows module patching and instrumentation to work on annotated classes when
|
||||
/// AOT is not enabled)
|
||||
/// Before adding this annotation to a class _X_, the author must determine
|
||||
/// that it's safe to execute the static initializer of _X_ during the AOT
|
||||
/// assembly phase. In addition, all supertypes of _X_ must also have this
|
||||
/// annotation. If a supertype of _X_ is found to be missing this annotation,
|
||||
/// the AOT assembly phase will fail.
|
||||
///
|
||||
/// This annotation is only recognized on privileged code and is ignored elsewhere.
|
||||
///
|
||||
|
||||
@ -2,7 +2,12 @@ runtime/modules/PatchModule/PatchModuleClassList.java 0000000 generic-all
|
||||
runtime/NMT/NMTWithCDS.java 0000000 generic-all
|
||||
runtime/symbols/TestSharedArchiveConfigFile.java 0000000 generic-all
|
||||
|
||||
# The following tests use very small -Xmx and will not be able to
|
||||
# use the AOT cache generated by "make test JTREG=AOT_JDK=onestep ..."
|
||||
gc/arguments/TestG1HeapSizeFlags.java 0000000 generic-all
|
||||
gc/arguments/TestParallelHeapSizeFlags.java 0000000 generic-all
|
||||
gc/arguments/TestSerialHeapSizeFlags.java 0000000 generic-all
|
||||
|
||||
gc/arguments/TestCompressedClassFlags.java 0000000 generic-all
|
||||
gc/TestAllocateHeapAtMultiple.java 0000000 generic-all
|
||||
gc/TestAllocateHeapAt.java 0000000 generic-all
|
||||
|
||||
@ -412,6 +412,7 @@ hotspot_cds_only = \
|
||||
|
||||
hotspot_appcds_dynamic = \
|
||||
runtime/cds/appcds/ \
|
||||
-runtime/cds/appcds/aotAnnotations \
|
||||
-runtime/cds/appcds/aotCache \
|
||||
-runtime/cds/appcds/aotClassLinking \
|
||||
-runtime/cds/appcds/aotCode \
|
||||
@ -512,6 +513,7 @@ hotspot_cds_epsilongc = \
|
||||
# test AOT class linking, so there's no need to run them again with -XX:+AOTClassLinking.
|
||||
hotspot_aot_classlinking = \
|
||||
runtime/cds \
|
||||
-runtime/cds/appcds/aotAnnotations \
|
||||
-runtime/cds/appcds/aotCache \
|
||||
-runtime/cds/appcds/aotClassLinking \
|
||||
-runtime/cds/appcds/aotCode \
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* @test
|
||||
* @summary Tests the effect of jdk.internal.vm.annotation.AOTXXX annotations
|
||||
* in the core Java library.
|
||||
* @bug 8317269
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @library /test/jdk/lib/testlibrary /test/lib
|
||||
* @build AOTAnnotationsTest
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTAnnotationsTestApp
|
||||
* @run driver AOTAnnotationsTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class AOTAnnotationsTest {
|
||||
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||
static final String mainClass = AOTAnnotationsTestApp.class.getName();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Tester tester = new Tester();
|
||||
tester.run(new String[] {"AOT", "--two-step-training"} );
|
||||
}
|
||||
|
||||
static class Tester extends CDSAppTester {
|
||||
public Tester() {
|
||||
super(mainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String classpath(RunMode runMode) {
|
||||
return appJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
return new String[] {
|
||||
"-Xlog:aot+class=debug",
|
||||
"-Xlog:aot+init",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] appCommandLine(RunMode runMode) {
|
||||
return new String[] { mainClass};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) {
|
||||
if (runMode == RunMode.ASSEMBLY) {
|
||||
out.shouldMatch("jdk.internal.math.MathUtils .*inited");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AOTAnnotationsTestApp {
|
||||
public static void main(String args[]) {
|
||||
double d = 12.34567;
|
||||
|
||||
// Double.toString() uses jdk.internal.math.MathUtils.
|
||||
// Because MathUtils has @AOTSafeClassInitializer and was initialized during
|
||||
// the training run, it will be cached in aot-inited state.
|
||||
System.out.println(Double.toString(d));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user