8360163: Replace hard-coded checks with AOTRuntimeSetup and AOTSafeClassInitializer

Reviewed-by: jrose, iklam
This commit is contained in:
Chen Liang 2025-07-29 20:42:57 +00:00
parent d5d311f026
commit 330ee87131
39 changed files with 421 additions and 170 deletions

View File

@ -63,6 +63,7 @@ class TypeArrayKlass;
// be AOT-initialized:
// - If we discover at least one instance of class X, then class X is AOT-initialized (** Note1).
// - If AOTClassInitializer::can_archive_initialized_mirror(X) is true, then X is AOT-initialized.
// This function checks for the @jdk.internal.vm.annotation.AOTSafeClassInitializer annotation.
// - For each AOT-initialized class, we scan all the static fields in its java mirror. This will in
// turn discover more Klasses and java heap objects.
// - The scanning continues until we reach a steady state.

View File

@ -37,67 +37,6 @@
DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;)
// Detector for class names we wish to handle specially.
// It is either an exact string match or a string prefix match.
class AOTClassInitializer::AllowedSpec {
const char* _class_name;
bool _is_prefix;
int _len;
public:
AllowedSpec(const char* class_name, bool is_prefix = false)
: _class_name(class_name), _is_prefix(is_prefix)
{
_len = (class_name == nullptr) ? 0 : (int)strlen(class_name);
}
const char* class_name() { return _class_name; }
bool matches(Symbol* name, int len) {
assert(_class_name != nullptr, "caller resp.");
if (_is_prefix) {
return len >= _len && name->starts_with(_class_name);
} else {
return len == _len && name->equals(_class_name);
}
}
};
// Tell if ik has a name that matches one of the given specs.
bool AOTClassInitializer::is_allowed(AllowedSpec* specs, InstanceKlass* ik) {
Symbol* name = ik->name();
int len = name->utf8_length();
for (AllowedSpec* s = specs; s->class_name() != nullptr; s++) {
if (s->matches(name, len)) {
// 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 (ik->java_super() != nullptr) {
DEBUG_ONLY(ResourceMark rm);
assert(AOTClassInitializer::can_archive_initialized_mirror(ik->java_super()),
"super class %s of %s must be aot-initialized", ik->java_super()->external_name(),
ik->external_name());
}
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
int len = interfaces->length();
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
if (intf->class_initializer() != nullptr) {
assert(AOTClassInitializer::can_archive_initialized_mirror(intf),
"super interface %s (which has <clinit>) of %s must be aot-initialized", intf->external_name(),
ik->external_name());
}
}
return true;
}
}
return false;
}
bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass");
if (!CDSConfig::is_initing_classes_at_dump_time()) {
@ -260,74 +199,19 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
// because of invokedynamic. They are few enough for now to be
// manually tracked. There may be more in the future.
// IS_PREFIX means that we match all class names that start with a
// prefix. Otherwise, it is an exact match, of just one class name.
const bool IS_PREFIX = true;
{
static AllowedSpec specs[] = {
if (ik == vmClasses::Object_klass()) {
// everybody's favorite super
{"java/lang/Object"},
{nullptr}
};
if (is_allowed(specs, ik)) {
return true;
}
}
if (CDSConfig::is_dumping_method_handles()) {
// This table was created with the help of CDSHeapVerifier.
// The minimal list of @AOTSafeClassInitializer was created with the help of CDSHeapVerifier.
// Also, some $Holder classes are needed. E.g., Invokers.<clinit> explicitly
// initializes Invokers$Holder. Since Invokers.<clinit> won't be executed
// at runtime, we need to make sure Invokers$Holder is also aot-inited.
//
// We hope we can reduce the size of this list over time, and move
// the responsibility for identifying such classes into the JDK
// code itself. See tracking RFE JDK-8342481.
static AllowedSpec indy_specs[] = {
{"java/lang/constant/ConstantDescs"},
{"java/lang/constant/DynamicConstantDesc"},
{"java/lang/invoke/BoundMethodHandle"},
{"java/lang/invoke/BoundMethodHandle$Specializer"},
{"java/lang/invoke/BoundMethodHandle$Species_", IS_PREFIX},
{"java/lang/invoke/ClassSpecializer"},
{"java/lang/invoke/ClassSpecializer$", IS_PREFIX},
{"java/lang/invoke/DelegatingMethodHandle"},
{"java/lang/invoke/DelegatingMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized()
{"java/lang/invoke/DirectMethodHandle"},
{"java/lang/invoke/DirectMethodHandle$Constructor"},
{"java/lang/invoke/DirectMethodHandle$Holder"}, // UNSAFE.ensureClassInitialized()
{"java/lang/invoke/Invokers"},
{"java/lang/invoke/Invokers$Holder"}, // UNSAFE.ensureClassInitialized()
{"java/lang/invoke/LambdaForm"},
{"java/lang/invoke/LambdaForm$Holder"}, // UNSAFE.ensureClassInitialized()
{"java/lang/invoke/LambdaForm$NamedFunction"},
{"java/lang/invoke/LambdaMetafactory"},
{"java/lang/invoke/MethodHandle"},
{"java/lang/invoke/MethodHandles"},
{"java/lang/invoke/SimpleMethodHandle"},
{"java/lang/invoke/StringConcatFactory"},
{"java/lang/invoke/VarHandleGuards"},
{"java/util/Collections"},
{"java/util/stream/Collectors"},
{"jdk/internal/constant/ConstantUtils"},
{"jdk/internal/constant/PrimitiveClassDescImpl"},
{"jdk/internal/constant/ReferenceClassDescImpl"},
// Can't include this, as it will pull in MethodHandleStatics which has many environment
// dependencies (on system properties, etc).
// MethodHandleStatics is an example of a class that must NOT get the aot-init treatment,
// because of its strong reliance on (a) final fields which are (b) environmentally determined.
//{"java/lang/invoke/InvokerBytecodeGenerator"},
{nullptr}
};
if (is_allowed(indy_specs, ik)) {
return true;
}
if (ik->name()->starts_with("java/lang/invoke/MethodHandleImpl")) {
if (ik->has_aot_safe_initializer()) {
return true;
}
}
@ -341,17 +225,6 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
return false;
}
// TODO: currently we have a hard-coded list. We should turn this into
// an annotation: @jdk.internal.vm.annotation.RuntimeSetupRequired
// See JDK-8342481.
bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
return ik == vmClasses::Class_klass() ||
ik == vmClasses::internal_Unsafe_klass() ||
ik == vmClasses::ConcurrentHashMap_klass() ||
ik == vmClasses::MethodHandleImpl_klass() ||
ik == vmClasses::Reference_klass();
}
void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) {
assert(ik->has_aot_initialized_mirror(), "sanity");
if (ik->is_runtime_setup_required()) {

View File

@ -31,15 +31,11 @@
class InstanceKlass;
class AOTClassInitializer : AllStatic {
class AllowedSpec;
static bool is_allowed(AllowedSpec* specs, InstanceKlass* ik);
public:
// Called by heapShared.cpp to see if src_ik->java_mirror() can be archived in
// the initialized state.
static bool can_archive_initialized_mirror(InstanceKlass* src_ik);
static bool is_runtime_setup_required(InstanceKlass* ik);
static void call_runtime_setup(JavaThread* current, InstanceKlass* ik);
// Support for regression testing. Available in debug builds only.

View File

@ -505,9 +505,6 @@ bool HeapShared::is_archivable_hidden_klass(InstanceKlass* ik) {
void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) {
ik->set_has_aot_initialized_mirror();
if (AOTClassInitializer::is_runtime_setup_required(ik)) {
ik->set_is_runtime_setup_required();
}
oop orig_mirror;
if (RegeneratedClasses::is_regenerated_object(ik)) {

View File

@ -941,6 +941,8 @@ public:
_jdk_internal_ValueBased,
_java_lang_Deprecated,
_java_lang_Deprecated_for_removal,
_jdk_internal_vm_annotation_AOTSafeClassInitializer,
_method_AOTRuntimeSetup,
_annotation_LIMIT
};
const Location _location;
@ -976,6 +978,8 @@ public:
void set_stable(bool stable) { set_annotation(_field_Stable); }
bool is_stable() const { return has_annotation(_field_Stable); }
bool has_aot_runtime_setup() const { return has_annotation(_method_AOTRuntimeSetup); }
};
// This class also doubles as a holder for metadata cleanup.
@ -1896,6 +1900,16 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): {
return _java_lang_Deprecated;
}
case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature): {
if (_location != _in_class) break; // only allow for classes
if (!privileged) break; // only allow in privileged code
return _jdk_internal_vm_annotation_AOTSafeClassInitializer;
}
case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_AOTRuntimeSetup_signature): {
if (_location != _in_method) break; // only allow for methods
if (!privileged) break; // only allow in privileged code
return _method_AOTRuntimeSetup;
}
default: {
break;
}
@ -1975,6 +1989,9 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) {
m->set_deprecated_for_removal();
}
}
if (has_annotation(_jdk_internal_vm_annotation_AOTSafeClassInitializer)) {
ik->set_has_aot_safe_initializer();
}
}
#define MAX_ARGS_SIZE 255
@ -2661,6 +2678,13 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
if (is_hidden()) { // Mark methods in hidden classes as 'hidden'.
m->set_is_hidden();
}
if (parsed_annotations.has_aot_runtime_setup()) {
if (name != vmSymbols::runtimeSetup() || signature != vmSymbols::void_method_signature() ||
!access_flags.is_private() || !access_flags.is_static()) {
classfile_parse_error("@AOTRuntimeSetup method must be declared private static void runtimeSetup() for class %s", CHECK_NULL);
}
_has_aot_runtime_setup_method = true;
}
// Copy annotations
copy_method_annotations(m->constMethod(),
@ -3978,6 +4002,15 @@ void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) {
const jint lh = Klass::instance_layout_helper(ik->size_helper(), true);
ik->set_layout_helper(lh);
}
// Propagate the AOT runtimeSetup method discovery
if (_has_aot_runtime_setup_method) {
ik->set_is_runtime_setup_required();
if (log_is_enabled(Info, aot, init)) {
ResourceMark rm;
log_info(aot, init)("Found @AOTRuntimeSetup class %s", ik->external_name());
}
}
}
// utility methods for appending an array with check for duplicates
@ -5117,8 +5150,47 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
check_methods_for_intrinsics(ik, methods);
// Fill in field values obtained by parse_classfile_attributes
if (_parsed_annotations->has_any_annotations()) {
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);
@ -5326,6 +5398,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
_has_localvariable_table(false),
_has_final_method(false),
_has_contended_fields(false),
_has_aot_runtime_setup_method(false),
_has_finalizer(false),
_has_empty_finalizer(false),
_max_bootstrap_specifier_index(-1) {

View File

@ -192,6 +192,7 @@ class ClassFileParser {
bool _has_localvariable_table;
bool _has_final_method;
bool _has_contended_fields;
bool _has_aot_runtime_setup_method;
// precomputed flags
bool _has_finalizer;

View File

@ -732,8 +732,10 @@ class SerializeClosure;
template(java_lang_invoke_DelegatingMethodHandle_Holder, "java/lang/invoke/DelegatingMethodHandle$Holder") \
template(jdk_internal_loader_ClassLoaders, "jdk/internal/loader/ClassLoaders") \
template(jdk_internal_misc_CDS, "jdk/internal/misc/CDS") \
template(jdk_internal_vm_annotation_AOTSafeClassInitializer_signature, "Ljdk/internal/vm/annotation/AOTSafeClassInitializer;")\
template(java_util_concurrent_ConcurrentHashMap, "java/util/concurrent/ConcurrentHashMap") \
template(java_util_ArrayList, "java/util/ArrayList") \
template(jdk_internal_vm_annotation_AOTRuntimeSetup_signature, "Ljdk/internal/vm/annotation/AOTRuntimeSetup;") \
template(runtimeSetup, "runtimeSetup") \
template(toFileURL_name, "toFileURL") \
template(toFileURL_signature, "(Ljava/lang/String;)Ljava/net/URL;") \

View File

@ -764,6 +764,14 @@ public:
bool has_final_method() const { return _misc_flags.has_final_method(); }
void set_has_final_method() { _misc_flags.set_has_final_method(true); }
// Indicates presence of @AOTSafeClassInitializer. Also see AOTClassInitializer for more details.
bool has_aot_safe_initializer() const { return _misc_flags.has_aot_safe_initializer(); }
void set_has_aot_safe_initializer() { _misc_flags.set_has_aot_safe_initializer(true); }
// Indicates @AOTRuntimeSetup private static void runtimeSetup() presence.
bool is_runtime_setup_required() const { return _misc_flags.is_runtime_setup_required(); }
void set_is_runtime_setup_required() { _misc_flags.set_is_runtime_setup_required(true); }
// for adding methods, ConstMethod::UNSET_IDNUM means no more ids available
inline u2 next_method_idnum();
void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; }

View File

@ -54,6 +54,8 @@ class InstanceKlassFlags {
flag(has_localvariable_table , 1 << 11) /* has localvariable information */ \
flag(has_miranda_methods , 1 << 12) /* True if this class has miranda methods in it's vtable */ \
flag(has_final_method , 1 << 13) /* True if klass has final method */ \
flag(has_aot_safe_initializer , 1 << 14) /* has @AOTSafeClassInitializer annotation */ \
flag(is_runtime_setup_required , 1 << 15) /* has a runtimeSetup method to be called */ \
/* end of list */
#define IK_FLAGS_ENUM_NAME(name, value) _misc_##name = value,

View File

@ -186,9 +186,6 @@ private:
_is_generated_shared_class = 1 << 5,
// archived mirror already initialized by AOT-cache assembly: no further need to call <clinit>
_has_aot_initialized_mirror = 1 << 6,
// If this class has been aot-inititalized, do we need to call its runtimeSetup()
// method during the production run?
_is_runtime_setup_required = 1 << 7,
};
#endif
@ -380,15 +377,6 @@ protected:
NOT_CDS(return false;)
}
void set_is_runtime_setup_required() {
assert(has_aot_initialized_mirror(), "sanity");
CDS_ONLY(_shared_class_flags |= _is_runtime_setup_required;)
}
bool is_runtime_setup_required() const {
CDS_ONLY(return (_shared_class_flags & _is_runtime_setup_required) != 0;)
NOT_CDS(return false;)
}
bool is_shared() const { // shadows MetaspaceObj::is_shared)()
CDS_ONLY(return (_shared_class_flags & _is_shared_class) != 0;)
NOT_CDS(return false;)

View File

@ -47,7 +47,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@ -79,6 +78,8 @@ import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.ConstantPool;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -211,6 +212,7 @@ import sun.reflect.annotation.*;
* @see java.lang.ClassLoader#defineClass(byte[], int, int)
* @since 1.0
*/
@AOTSafeClassInitializer
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
@ -226,7 +228,9 @@ public final class Class<T> implements java.io.Serializable,
runtimeSetup();
}
// Called from JVM when loading an AOT cache
/// No significant static final fields; [#resetArchivedStates()] handles
/// prevents storing [#reflectionFactory] into AOT image.
@AOTRuntimeSetup
private static void runtimeSetup() {
registerNatives();
}

View File

@ -25,6 +25,7 @@
package java.lang;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
/**
@ -35,6 +36,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* @see java.lang.Class
* @since 1.0
*/
@AOTSafeClassInitializer // for hierarchy checks
public class Object {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -28,6 +28,7 @@ import jdk.internal.constant.ClassOrInterfaceDescImpl;
import jdk.internal.constant.ConstantUtils;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.constant.PrimitiveClassDescImpl;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import java.lang.Enum.EnumDesc;
import java.lang.invoke.CallSite;
@ -56,6 +57,7 @@ import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;
*
* @since 12
*/
@AOTSafeClassInitializer
public final class ConstantDescs {
// No instances
private ConstantDescs() { }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -36,6 +36,8 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import static java.lang.constant.ConstantDescs.CD_Class;
import static java.lang.constant.ConstantDescs.CD_VarHandle;
import static java.lang.constant.ConstantDescs.DEFAULT_NAME;
@ -56,6 +58,7 @@ import static jdk.internal.constant.ConstantUtils.validateMemberName;
*
* @since 12
*/
@AOTSafeClassInitializer // for PrimitiveClassDescImpl
public abstract non-sealed class DynamicConstantDesc<T>
implements ConstantDesc {

View File

@ -25,6 +25,7 @@
package java.lang.invoke;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import java.util.ArrayList;
@ -44,6 +45,7 @@ import static java.lang.invoke.MethodHandleStatics.uncaughtException;
*
* All bound arguments are encapsulated in dedicated species.
*/
@AOTSafeClassInitializer
/*non-public*/
abstract non-sealed class BoundMethodHandle extends MethodHandle {
@ -233,6 +235,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle {
// concrete BMH classes required to close bootstrap loops
//
@AOTSafeClassInitializer
private // make it private to force users to access the enclosing class first
static final class Species_L extends BoundMethodHandle {
@ -312,6 +315,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle {
// BMH species meta-data
//
@AOTSafeClassInitializer
/*non-public*/
static final class SpeciesData
extends ClassSpecializer<BoundMethodHandle, String, SpeciesData>.SpeciesData {
@ -402,6 +406,7 @@ abstract non-sealed class BoundMethodHandle extends MethodHandle {
Species_L.BMH_SPECIES = BoundMethodHandle.SPECIALIZER.findSpecies("L");
}
@AOTSafeClassInitializer
/*non-public*/
static final class Specializer
extends ClassSpecializer<BoundMethodHandle, String, SpeciesData> {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 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
@ -47,6 +47,7 @@ import jdk.internal.constant.ClassOrInterfaceDescImpl;
import jdk.internal.constant.ConstantUtils;
import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.loader.BootLoader;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.BytecodeName;
import sun.invoke.util.Wrapper;
@ -64,11 +65,15 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* @param <K> key which identifies individual specializations.
* @param <S> species data type.
*/
@AOTSafeClassInitializer
/*non-public*/
abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesData> {
private static final ClassDesc CD_LambdaForm = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;");
private static final ClassDesc CD_BoundMethodHandle = ClassOrInterfaceDescImpl.ofValidated("Ljava/lang/invoke/BoundMethodHandle;");
private static final RuntimeVisibleAnnotationsAttribute AOT_SAFE_ANNOTATION = RuntimeVisibleAnnotationsAttribute.of(
Annotation.of(ConstantUtils.referenceClassDesc(AOTSafeClassInitializer.class))
);
private static final RuntimeVisibleAnnotationsAttribute STABLE_ANNOTATION = RuntimeVisibleAnnotationsAttribute.of(
Annotation.of(ConstantUtils.referenceClassDesc(Stable.class))
);
@ -233,6 +238,7 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
* it would appear that a shorter species could serve as a supertype of a
* longer one which extends it.
*/
@AOTSafeClassInitializer
abstract class SpeciesData {
// Bootstrapping requires circular relations Class -> SpeciesData -> Class
// Therefore, we need non-final links in the chain. Use @Stable fields.
@ -469,6 +475,7 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
* Code generation support for instances.
* Subclasses can modify the behavior.
*/
@AOTSafeClassInitializer
class Factory {
/**
* Constructs a factory.
@ -624,6 +631,7 @@ abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesDat
clb.withFlags(ACC_FINAL | ACC_SUPER)
.withSuperclass(superClassDesc)
.with(SourceFileAttribute.of(classDesc.displayName()))
.with(AOT_SAFE_ANNOTATION)
// emit static types and BMH_SPECIES fields
.withField(sdFieldName, CD_SPECIES_DATA, new Consumer<>() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 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
@ -26,6 +26,9 @@
package java.lang.invoke;
import java.util.Arrays;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.LambdaForm.Kind.*;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
@ -36,6 +39,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
* The delegating MH itself can hold extra "intentions" beyond the simple behavior.
* @author jrose
*/
@AOTSafeClassInitializer
/*non-public*/
abstract sealed class DelegatingMethodHandle extends MethodHandle
permits MethodHandleImpl.AsVarargsCollector,
@ -193,5 +197,6 @@ abstract sealed class DelegatingMethodHandle extends MethodHandle
}
/* Placeholder class for DelegatingMethodHandles generated ahead of time */
@AOTSafeClassInitializer
final class Holder {}
}

View File

@ -27,6 +27,7 @@ package java.lang.invoke;
import jdk.internal.misc.CDS;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.ValueConversions;
@ -49,6 +50,7 @@ import static java.lang.invoke.MethodTypeForm.*;
* to a class member.
* @author jrose
*/
@AOTSafeClassInitializer
sealed class DirectMethodHandle extends MethodHandle {
final MemberName member;
final boolean crackable;
@ -466,6 +468,7 @@ sealed class DirectMethodHandle extends MethodHandle {
}
/** This subclass handles constructor references. */
@AOTSafeClassInitializer
static final class Constructor extends DirectMethodHandle {
final MemberName initMethod;
final Class<?> instanceClass;
@ -937,5 +940,6 @@ sealed class DirectMethodHandle extends MethodHandle {
}
/* Placeholder class for DirectMethodHandles generated ahead of time */
@AOTSafeClassInitializer
final class Holder {}
}

View File

@ -25,9 +25,12 @@
package java.lang.invoke;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import sun.invoke.util.Wrapper;
import java.lang.classfile.Annotation;
import java.lang.classfile.ClassFile;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.classfile.attribute.SourceFileAttribute;
import java.lang.constant.ClassDesc;
import java.util.ArrayList;
@ -67,6 +70,7 @@ class GenerateJLIClassesHelper {
static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";
static final String INVOKERS_HOLDER_CLASS_NAME = INVOKERS_HOLDER.replace('/', '.');
static final String BMH_SPECIES_PREFIX = "java.lang.invoke.BoundMethodHandle$Species_";
static final Annotation AOT_SAFE_ANNOTATION = Annotation.of(AOTSafeClassInitializer.class.describeConstable().orElseThrow());
static class HolderClassBuilder {
@ -562,6 +566,7 @@ class GenerateJLIClassesHelper {
return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
.withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
.with(RuntimeVisibleAnnotationsAttribute.of(AOT_SAFE_ANNOTATION))
.with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
for (int i = 0; i < forms.length; i++) {
new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false);

View File

@ -26,6 +26,7 @@
package java.lang.invoke;
import jdk.internal.invoke.MhUtil;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
@ -43,6 +44,7 @@ import static java.lang.invoke.LambdaForm.Kind.*;
* Construction and caching of often-used invokers.
* @author jrose
*/
@AOTSafeClassInitializer
class Invokers {
// exact type (sans leading target MH) for the outgoing call
private final MethodType targetType;
@ -696,5 +698,6 @@ class Invokers {
}
/* Placeholder class for Invokers generated ahead of time */
@AOTSafeClassInitializer
final class Holder {}
}

View File

@ -27,6 +27,7 @@ package java.lang.invoke;
import java.lang.classfile.TypeKind;
import jdk.internal.perf.PerfCounter;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.Stable;
@ -122,6 +123,7 @@ import static java.lang.invoke.MethodHandleStatics.*;
* <p>
* @author John Rose, JSR 292 EG
*/
@AOTSafeClassInitializer
class LambdaForm {
final int arity;
final int result;
@ -1032,6 +1034,7 @@ class LambdaForm {
return false;
}
@AOTSafeClassInitializer
static class NamedFunction {
final MemberName member;
private @Stable MethodHandle resolvedHandle;
@ -1728,6 +1731,7 @@ class LambdaForm {
}
/* Placeholder class for identity and constant forms generated ahead of time */
@AOTSafeClassInitializer
final class Holder {}
// The following hack is necessary in order to suppress TRACE_INTERPRETER

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -30,6 +30,8 @@ import java.util.Arrays;
import java.lang.reflect.Array;
import java.util.Objects;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* <p>Methods to facilitate the creation of simple "function objects" that
* implement one or more interfaces by delegation to a provided {@link MethodHandle},
@ -247,6 +249,7 @@ import java.util.Objects;
*
* @since 1.8
*/
@AOTSafeClassInitializer
public final class LambdaMetafactory {
private LambdaMetafactory() {}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 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
@ -27,6 +27,7 @@ package java.lang.invoke;
import jdk.internal.loader.ClassLoaders;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@ -442,6 +443,7 @@ mh.invokeExact(System.out, "Hello, world.");
* @author John Rose, JSR 292 EG
* @since 1.7
*/
@AOTSafeClassInitializer
public abstract sealed class MethodHandle implements Constable
permits NativeMethodHandle, DirectMethodHandle,
DelegatingMethodHandle, BoundMethodHandle {

View File

@ -33,6 +33,8 @@ import jdk.internal.constant.MethodTypeDescImpl;
import jdk.internal.foreign.abi.NativeEntryPoint;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
import jdk.internal.vm.annotation.Stable;
@ -72,6 +74,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
* Trusted implementation code for MethodHandle.
* @author jrose
*/
@AOTSafeClassInitializer
/*non-public*/
abstract class MethodHandleImpl {
@ -158,6 +161,7 @@ abstract class MethodHandleImpl {
return newInternalError("should not reach here (unmatched ArrayAccess: " + a + ")");
}
@AOTSafeClassInitializer
static final class ArrayAccessor {
/// Support for array element and length access
static final int GETTER_INDEX = 0, SETTER_INDEX = 1, LENGTH_INDEX = 2, INDEX_LIMIT = 3;
@ -453,6 +457,7 @@ abstract class MethodHandleImpl {
return new AsVarargsCollector(target, arrayType);
}
@AOTSafeClassInitializer
static final class AsVarargsCollector extends DelegatingMethodHandle {
private final MethodHandle target;
private final Class<?> arrayType;
@ -675,6 +680,7 @@ abstract class MethodHandleImpl {
DONT_INLINE_THRESHOLD);
}
@AOTSafeClassInitializer
private static final class Makers {
/** Constructs reinvoker lambda form which block inlining during JIT-compilation for a particular method handle */
static final Function<MethodHandle, LambdaForm> PRODUCE_BLOCK_INLINING_FORM = new Function<MethodHandle, LambdaForm>() {
@ -710,6 +716,7 @@ abstract class MethodHandleImpl {
* Behavior in counting and non-counting states is determined by lambda forms produced by
* countingFormProducer & nonCountingFormProducer respectively.
*/
@AOTSafeClassInitializer
static final class CountingWrapper extends DelegatingMethodHandle {
private final MethodHandle target;
private int count;
@ -1035,6 +1042,7 @@ abstract class MethodHandleImpl {
// Put the whole mess into its own nested class.
// That way we can lazily load the code and set up the constants.
@AOTSafeClassInitializer
private static class BindCaller {
private static final ClassDesc CD_Object_array = ConstantUtils.CD_Object_array;
@ -1143,6 +1151,7 @@ abstract class MethodHandleImpl {
return BindCaller.CV_makeInjectedInvoker.get(caller).reflectInvoker();
}
@AOTSafeClassInitializer
private static final class InjectedInvokerHolder {
private final Class<?> invokerClass;
// lazily resolved and cached DMH(s) of invoke_V methods
@ -1285,6 +1294,7 @@ abstract class MethodHandleImpl {
}
/** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
@AOTSafeClassInitializer
static final class WrappedMember extends DelegatingMethodHandle {
private final MethodHandle target;
private final MemberName member;
@ -1348,6 +1358,7 @@ abstract class MethodHandleImpl {
/** Mark arbitrary method handle as intrinsic.
* InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */
@AOTSafeClassInitializer
static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
private final MethodHandle target;
private final Intrinsic intrinsicName;
@ -1528,7 +1539,7 @@ abstract class MethodHandleImpl {
runtimeSetup();
}
// Also called from JVM when loading an AOT cache
@AOTRuntimeSetup
private static void runtimeSetup() {
SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
@Override
@ -1778,6 +1789,7 @@ abstract class MethodHandleImpl {
return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes);
}
@AOTSafeClassInitializer
static class LoopClauses {
@Stable final MethodHandle[][] clauses;
LoopClauses(MethodHandle[][] clauses) {
@ -2105,6 +2117,7 @@ abstract class MethodHandleImpl {
}
// use a wrapper because we need this array to be @Stable
@AOTSafeClassInitializer
static class CasesHolder {
@Stable
final MethodHandle[] cases;
@ -2134,6 +2147,7 @@ abstract class MethodHandleImpl {
return mh;
}
@AOTSafeClassInitializer
private static class TableSwitchCacheKey {
private static final Map<TableSwitchCacheKey, LambdaForm> CACHE = new ConcurrentHashMap<>();

View File

@ -32,6 +32,7 @@ import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection;
import jdk.internal.util.ClassFileDumper;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.ValueConversions;
@ -83,6 +84,7 @@ import static java.lang.invoke.MethodType.methodType;
* @author John Rose, JSR 292 EG
* @since 1.7
*/
@AOTSafeClassInitializer
public final class MethodHandles {
private MethodHandles() { } // do not instantiate

View File

@ -25,17 +25,16 @@
package java.lang.invoke;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import static java.lang.invoke.LambdaForm.BasicType.*;
import static java.lang.invoke.MethodHandleStatics.*;
/**
* A method handle whose behavior is determined only by its LambdaForm.
* Access to SimpleMethodHandle should ensure BoundMethodHandle is initialized
* first.
* @author jrose
*/
@AOTSafeClassInitializer
final class SimpleMethodHandle extends BoundMethodHandle {
private SimpleMethodHandle(MethodType type, LambdaForm form) {

View File

@ -35,6 +35,7 @@ import jdk.internal.misc.VM;
import jdk.internal.util.ClassFileDumper;
import jdk.internal.util.ReferenceKey;
import jdk.internal.util.ReferencedKeyMap;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
@ -116,6 +117,7 @@ import static java.lang.invoke.MethodType.methodType;
*
* @since 9
*/
@AOTSafeClassInitializer
public final class StringConcatFactory {
private static final int HIGH_ARITY_THRESHOLD;
private static final int CACHE_THRESHOLD;

View File

@ -24,10 +24,12 @@
*/
package java.lang.invoke;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Hidden;
// This class is auto-generated by java.lang.invoke.VarHandles$GuardMethodGenerator. Do not edit.
@AOTSafeClassInitializer
final class VarHandleGuards {
@ForceInline

View File

@ -746,12 +746,14 @@ final class VarHandles {
// public static void main(String[] args) {
// System.out.println("package java.lang.invoke;");
// System.out.println();
// System.out.println("import jdk.internal.vm.annotation.AOTSafeClassInitializer;");
// System.out.println("import jdk.internal.vm.annotation.ForceInline;");
// System.out.println("import jdk.internal.vm.annotation.Hidden;");
// System.out.println();
// System.out.println("// This class is auto-generated by " +
// GuardMethodGenerator.class.getName() +
// ". Do not edit.");
// System.out.println("@AOTSafeClassInitializer");
// System.out.println("final class VarHandleGuards {");
//
// System.out.println();

View File

@ -25,6 +25,8 @@
package java.lang.ref;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.access.JavaLangRefAccess;
@ -41,7 +43,7 @@ import jdk.internal.access.SharedSecrets;
* @since 1.2
* @sealedGraph
*/
@AOTSafeClassInitializer
public abstract sealed class Reference<@jdk.internal.RequiresIdentity T>
permits PhantomReference, SoftReference, WeakReference, FinalReference {
@ -292,7 +294,7 @@ public abstract sealed class Reference<@jdk.internal.RequiresIdentity T>
runtimeSetup();
}
// Also called from JVM when loading an AOT cache
@AOTRuntimeSetup
private static void runtimeSetup() {
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {

View File

@ -30,6 +30,8 @@ import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* This class provides a skeletal implementation of the {@code Map}
* interface, to minimize the effort required to implement this interface.
@ -68,7 +70,7 @@ import java.util.function.Predicate;
* @see Collection
* @since 1.2
*/
@AOTSafeClassInitializer
public abstract class AbstractMap<K,V> implements Map<K,V> {
/**
* Sole constructor. (For invocation by subclass constructors, typically

View File

@ -42,6 +42,7 @@ import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* This class consists exclusively of static methods that operate on or return
@ -82,6 +83,7 @@ import jdk.internal.access.SharedSecrets;
* @since 1.2
*/
@AOTSafeClassInitializer
public final class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {

View File

@ -70,6 +70,8 @@ import java.util.function.ToLongFunction;
import java.util.stream.Stream;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
/**
@ -263,6 +265,7 @@ import jdk.internal.vm.annotation.Stable;
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
@AOTSafeClassInitializer
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
private static final long serialVersionUID = 7249069246763182397L;
@ -602,7 +605,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
runtimeSetup();
}
// Called from JVM when loading an AOT cache.
@AOTRuntimeSetup
private static void runtimeSetup() {
NCPU = Runtime.getRuntime().availableProcessors();
}

View File

@ -57,6 +57,7 @@ import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* Implementations of {@link Collector} that implement various useful reduction
@ -103,6 +104,7 @@ import jdk.internal.access.SharedSecrets;
*
* @since 1.8
*/
@AOTSafeClassInitializer
public final class Collectors {
static final Set<Collector.Characteristics> CH_CONCURRENT_ID

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -24,6 +24,7 @@
*/
package jdk.internal.constant;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
@ -41,6 +42,7 @@ import static jdk.internal.constant.PrimitiveClassDescImpl.*;
/**
* Helper methods for the implementation of {@code java.lang.constant}.
*/
@AOTSafeClassInitializer // initialization dependency of PrimitiveClassDescImpl
public final class ConstantUtils {
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -29,6 +29,7 @@ import java.lang.constant.ConstantDescs;
import java.lang.constant.DynamicConstantDesc;
import java.lang.invoke.MethodHandles;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import sun.invoke.util.Wrapper;
@ -38,6 +39,7 @@ import static java.util.Objects.requireNonNull;
* A <a href="package-summary.html#nominal">nominal descriptor</a> for the class
* constant corresponding to a primitive type (e.g., {@code int.class}).
*/
@AOTSafeClassInitializer // identity-sensitive static final fields
public final class PrimitiveClassDescImpl
extends DynamicConstantDesc<Class<?>> implements ClassDesc {

View File

@ -25,6 +25,8 @@
package jdk.internal.misc;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import sun.nio.Cleaner;
@ -52,7 +54,7 @@ import static jdk.internal.misc.UnsafeConstants.*;
* @author John R. Rose
* @see #getUnsafe
*/
@AOTSafeClassInitializer
public final class Unsafe {
private static native void registerNatives();
@ -60,7 +62,9 @@ public final class Unsafe {
runtimeSetup();
}
// Called from JVM when loading an AOT cache
/// BASE_OFFSET, INDEX_SCALE, and ADDRESS_SIZE fields are equivalent if the
/// AOT initialized heap is reused, so just register natives
@AOTRuntimeSetup
private static void runtimeSetup() {
registerNatives();
}

View File

@ -0,0 +1,83 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.internal.vm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/// Indicates that if the enclosing class or interface is present in the AOT
/// cache in the AOT-initialized state, the annotated method must be executed
/// before bootstrap phase 3 (that is, before [System#initPhase3]).
///
/// The annotated method must be declared `private` and `static`, must be named
/// `runtimeSetup`, and must have no arguments or return value. The enclosing
/// class must be annotated with [AOTSafeClassInitializer], meaning that it is
/// allowed to be stored in the AOT-initialized state.
///
/// The annotated method will be executed if and only if the class was loaded
/// in the AOT-initialized state from the AOT cache.
///
/// The author of the class is responsible for deciding whether some or all of
/// a class's initialization state should be re-initialized in any way. In all
/// cases, the static initializer (`<clinit>` method) of any given class or
/// interface is run at most once, either in the assembly phase (only for an
/// AOT-initialized class) or in the production run.
///
/// After a `static` `final` field is assigned a value in an AOT-initialized
/// class, its value may never be changed, as such values are always immutable
/// runtime constants. (...Barring `System.out` and its two siblings.)
/// Rarely, a `static` field may require differing values in the assembly phase
/// for an AOT cache, and for the production run. Such variables must be
/// marked non-`final`, and should be adjusted by the `runtimeSetup` method.
/// Full constant folding (as if with a `final` field) may usually be recovered
/// by also marking the field as [Stable]. That annotation instructs the JIT
/// to perform constant folding, and _only_ during the production run, after
/// `runtimeSetup` has had a chance to give the field its "finally final"
/// value.
///
/// A related method is `resetArchivedStates`, which allows special handling of
/// an AOT-initialized class, at the end of the assembly phase run which builds
/// an AOT cache. The `resetArchivedStates` may "tear down" state that should
/// not be stored in the AOT cache, which the `runtimeSetup` method may then
/// "build up again" as the production run begins. This additional method is
/// currently only used by [Class] to reset a cache field, but it may be
/// expanded to other classes and interfaces later on, using more
/// annotation-driven logic.
///
/// The logic in `classFileParser.cpp` performs checks on the annotated method: If the
/// annotated method's signature differs from that described above, or if (during the
/// assembly phase) the class is not marked to have an AOT-safe initializer, a
/// [ClassFormatError] will be thrown.
///
/// This annotation is only recognized on privileged code and is ignored elsewhere.
///
/// @since 26
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AOTRuntimeSetup {
}

View File

@ -0,0 +1,137 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.internal.vm.annotation;
import java.lang.annotation.ElementType;
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.
///
/// 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.)
///
/// 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.
///
/// Currently, this annotation is used mainly for supporting AOT
/// linking of APIs, including bootstrap methods, in the
/// `java.lang.invoke` package.
///
/// In more detail, the AOT assembly phase performs the following:
///
/// 1. It loads and links (but does not initialize) the classes that were loaded
/// during the application's training run.
/// 2. During linking of these classes, it resolves their constant pool
/// entries, when it is safe and beneficial to do so.
/// 3. As part of those resolutions, bootstrap methods may be called and may
/// create graphs of Java objects to support linkage states.
/// 4. Every object within those graphs must have its class AOT-initialized,
/// 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).
///
/// 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
/// the `<clinit>` of _X_ will initialize (during AOT cache assembly), and
/// ensuring that each such _Y_ is (recursively) safe to AOT-initialize.
///
/// For example, an AOT-resolved constant pool entry for an invokedynamic or
/// invokehandle bytecode can have direct or indirect references to Java objects.
/// To ensure the correctness of the AOT-resolved constant pool entrties, the VM
/// must AOT-initialize the classes of such Java objects.
///
/// In addition, such Java objects may have references to static fields whose
/// object identity is important. For example, `PrimitiveClassDescImpl::CD_void`.
/// To ensure correctness, we must also store classes like `PrimitiveClassDescImpl`
/// in the initialized state. The VM requires implementor to manually annotate
/// such classes with `@AOTSafeClassInitializer`.
///
/// There is one more requirement for a class to be safe for
/// AOT initialization, and that is compatibility with all eventual production
/// runs. The state of an AOT-initialized class _X_ must not contain any data
/// (anything reachable from _X_) that is incompatible with the eventual
/// production run.
///
/// In general, if some sort of computed datum, environmental setting, or
/// variable behavior may differ between the AOT assembly phase and the
/// production run, it may not be immutably bound to _X_, if _X_ is to be
/// marked AOT-initialized. Here are specific examples:
///
/// - The value of an environment string (if it may differ in the production run).
///
/// - A transient configuration parameter specific to this VM run, such as
/// wall clock time, process ID, host name, temporary directory names, etc.
///
/// - A random seed or key that may need to be re-sampled at production
/// startup.
///
/// What is more, if the initialization of _X_ computes with some value _V_
/// obtained from some other class _Y_, _Y_ should also be safe for AOT
/// initialization, if there is any way for _X_ to detect a mismatch between
/// the version of _V_ produced at AOT time, and the version of _V_ produced in
/// the production run. Specifically, if _V_ has an object identity, _X_
/// should not test that identity (compare it against another or get its
/// hashcode) unless _Y_ is also marked for AOT initialization.
///
/// Thus, to support AOT-time linkage, a class _X_ should be marked for (possible)
/// AOT initialization whenever objects it creates (such as `MethodHandle`s)
/// may be required to execute a `java.lang.invoke` API request, or (more
/// 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.
///
/// 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)
///
/// This annotation is only recognized on privileged code and is ignored elsewhere.
///
/// @since 26
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AOTSafeClassInitializer {
}