Merge branch 'openjdk:master' into JDK-8373118

This commit is contained in:
Doug Lea 2025-12-18 08:47:20 -05:00 committed by GitHub
commit 2d8cff2bc1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
125 changed files with 3087 additions and 1374 deletions

View File

@ -145,7 +145,7 @@ void AOTArtifactFinder::find_artifacts() {
#if INCLUDE_CDS_JAVA_HEAP
// Keep scanning until we discover no more class that need to be AOT-initialized.
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
while (_pending_aot_inited_classes->length() > 0) {
InstanceKlass* ik = _pending_aot_inited_classes->pop();
HeapShared::copy_and_rescan_aot_inited_mirror(ik);
@ -188,7 +188,7 @@ void AOTArtifactFinder::end_scanning_for_oops() {
}
void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) {
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
if (RegeneratedClasses::is_regenerated_object(ik)) {
precond(RegeneratedClasses::get_original_object(ik)->is_initialized());
} else {
@ -258,7 +258,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) {
return;
}
scan_oops_in_instance_class(ik);
if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) {
if (ik->is_hidden() && CDSConfig::is_dumping_aot_linked_classes()) {
bool succeed = AOTClassLinker::try_add_candidate(ik);
guarantee(succeed, "All cached hidden classes must be aot-linkable");
add_aot_inited_class(ik);

View File

@ -40,7 +40,7 @@ DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;)
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()) {
if (!CDSConfig::is_dumping_aot_linked_classes()) {
return false;
}
@ -64,7 +64,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
// Automatic selection for aot-inited classes
// ==========================================
//
// When CDSConfig::is_initing_classes_at_dump_time is enabled,
// When CDSConfig::is_dumping_aot_linked_classes is enabled,
// AOTArtifactFinder::find_artifacts() finds the classes of all
// heap objects that are reachable from HeapShared::_run_time_special_subgraph,
// and mark these classes as aot-inited. This preserves the initialized
@ -310,7 +310,7 @@ void AOTClassInitializer::init_test_class(TRAPS) {
//
// -XX:AOTInitTestClass is NOT a general mechanism for including user-defined objects into
// the AOT cache. Therefore, this option is NOT available in product JVM.
if (AOTInitTestClass != nullptr && CDSConfig::is_initing_classes_at_dump_time()) {
if (AOTInitTestClass != nullptr && CDSConfig::is_dumping_aot_linked_classes()) {
log_info(aot)("Debug build only: force initialization of AOTInitTestClass %s", AOTInitTestClass);
TempNewSymbol class_name = SymbolTable::new_symbol(AOTInitTestClass);
Handle app_loader(THREAD, SystemDictionary::java_system_loader());

View File

@ -1141,7 +1141,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
AOTReferenceObjSupport::initialize(CHECK);
AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK);
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
// java.lang.Class::reflectionFactory cannot be archived yet. We set this field
// to null, and it will be initialized again at runtime.
log_debug(aot)("Resetting Class::reflectionFactory");

View File

@ -1026,23 +1026,19 @@ void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) {
_has_aot_linked_classes |= has_aot_linked_classes;
}
bool CDSConfig::is_initing_classes_at_dump_time() {
return is_dumping_heap() && is_dumping_aot_linked_classes();
}
bool CDSConfig::is_dumping_invokedynamic() {
// Requires is_dumping_aot_linked_classes(). Otherwise the classes of some archived heap
// objects used by the archive indy callsites may be replaced at runtime.
return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap();
}
// When we are dumping aot-linked classes and we are able to write archived heap objects, we automatically
// enable the archiving of MethodHandles. This will in turn enable the archiving of MethodTypes and hidden
// When we are dumping aot-linked classes, we automatically enable the archiving of MethodHandles.
// This will in turn enable the archiving of MethodTypes and hidden
// classes that are used in the implementation of MethodHandles.
// Archived MethodHandles are required for higher-level optimizations such as AOT resolution of invokedynamic
// and dynamic proxies.
bool CDSConfig::is_dumping_method_handles() {
return is_initing_classes_at_dump_time();
return is_dumping_aot_linked_classes();
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -187,7 +187,6 @@ public:
static void disable_heap_dumping() { CDS_ONLY(_disable_heap_dumping = true); }
static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_initing_classes_at_dump_time() NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false);
static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false);

View File

@ -40,7 +40,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
}
// !!! This is legacy support for enum classes before JEP 483. This file is not used when
// !!! CDSConfig::is_initing_classes_at_dump_time()==true.
// !!! CDSConfig::is_dumping_aot_linked_classes()==true.
//
// Java Enum classes have synthetic <clinit> methods that look like this
// enum MyEnum {FOO, BAR}
@ -63,7 +63,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
void CDSEnumKlass::handle_enum_obj(int level,
KlassSubGraphInfo* subgraph_info,
oop orig_obj) {
assert(!CDSConfig::is_initing_classes_at_dump_time(), "only for legacy support of enums");
assert(!CDSConfig::is_dumping_aot_linked_classes(), "only for legacy support of enums");
assert(level > 1, "must never be called at the first (outermost) level");
assert(is_enum_obj(orig_obj), "must be");

View File

@ -35,7 +35,7 @@ class JavaFieldStream;
class KlassSubGraphInfo;
// This is legacy support for enum classes before JEP 483. This code is not needed when
// CDSConfig::is_initing_classes_at_dump_time()==true.
// CDSConfig::is_dumping_aot_linked_classes()==true.
class CDSEnumKlass: AllStatic {
public:
static bool is_enum_obj(oop orig_obj);

View File

@ -156,7 +156,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
# undef ADD_EXCL
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
add_shared_secret_accessors();
}
ClassLoaderDataGraph::classes_do(this);

View File

@ -206,6 +206,8 @@ void FinalImageRecipes::load_all_classes(TRAPS) {
if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) {
assert(ik->class_loader() == nullptr, "supported only for boot classes for now");
ResourceMark rm(THREAD);
log_info(aot, init)("Initializing %s", ik->external_name());
ik->initialize(CHECK);
}
}

View File

@ -209,8 +209,14 @@ static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], Instan
}
bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) {
return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik);
assert(CDSConfig::is_dumping_heap(), "dump-time only");
if (!CDSConfig::is_dumping_aot_linked_classes()) {
// Legacy CDS archive support (to be deprecated)
return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik);
} else {
return false;
}
}
oop HeapShared::CachedOopInfo::orig_referrer() const {
@ -934,12 +940,16 @@ void HeapShared::scan_java_class(Klass* orig_k) {
void HeapShared::archive_subgraphs() {
assert(CDSConfig::is_dumping_heap(), "must be");
archive_object_subgraphs(archive_subgraph_entry_fields,
false /* is_full_module_graph */);
if (!CDSConfig::is_dumping_aot_linked_classes()) {
archive_object_subgraphs(archive_subgraph_entry_fields,
false /* is_full_module_graph */);
if (CDSConfig::is_dumping_full_module_graph()) {
archive_object_subgraphs(fmg_archive_subgraph_entry_fields,
true /* is_full_module_graph */);
}
}
if (CDSConfig::is_dumping_full_module_graph()) {
archive_object_subgraphs(fmg_archive_subgraph_entry_fields,
true /* is_full_module_graph */);
Modules::verify_archived_modules();
}
}
@ -1295,8 +1305,10 @@ void HeapShared::resolve_classes(JavaThread* current) {
if (!is_archived_heap_in_use()) {
return; // nothing to do
}
resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields);
resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields);
if (!CDSConfig::is_using_aot_linked_classes()) {
resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields);
resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields);
}
}
void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]) {
@ -1734,13 +1746,13 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap
}
}
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
if (java_lang_Class::is_instance(orig_obj)) {
orig_obj = scratch_java_mirror(orig_obj);
assert(orig_obj != nullptr, "must be archived");
}
} else if (java_lang_Class::is_instance(orig_obj) && subgraph_info != _dump_time_special_subgraph) {
// Without CDSConfig::is_initing_classes_at_dump_time(), we only allow archived objects to
// Without CDSConfig::is_dumping_aot_linked_classes(), we only allow archived objects to
// point to the mirrors of (1) j.l.Object, (2) primitive classes, and (3) box classes. These are initialized
// very early by HeapShared::init_box_classes().
if (orig_obj == vmClasses::Object_klass()->java_mirror()
@ -1808,9 +1820,9 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap
orig_obj->oop_iterate(&pusher);
}
if (CDSConfig::is_initing_classes_at_dump_time()) {
// The classes of all archived enum instances have been marked as aot-init,
// so there's nothing else to be done in the production run.
if (CDSConfig::is_dumping_aot_linked_classes()) {
// The enum klasses are archived with aot-initialized mirror.
// See AOTClassInitializer::can_archive_initialized_mirror().
} else {
// This is legacy support for enum classes before JEP 483 -- we cannot rerun
// the enum's <clinit> in the production run, so special handling is needed.
@ -1949,7 +1961,7 @@ void HeapShared::verify_reachable_objects_from(oop obj) {
#endif
void HeapShared::check_special_subgraph_classes() {
if (CDSConfig::is_initing_classes_at_dump_time()) {
if (CDSConfig::is_dumping_aot_linked_classes()) {
// We can have aot-initialized classes (such as Enums) that can reference objects
// of arbitrary types. Currently, we trust the JEP 483 implementation to only
// aot-initialize classes that are "safe".
@ -2136,9 +2148,11 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
void HeapShared::init_subgraph_entry_fields(TRAPS) {
assert(CDSConfig::is_dumping_heap(), "must be");
_dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable();
init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK);
if (CDSConfig::is_dumping_full_module_graph()) {
init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK);
if (!CDSConfig::is_dumping_aot_linked_classes()) {
init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK);
if (CDSConfig::is_dumping_full_module_graph()) {
init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK);
}
}
}

View File

@ -190,6 +190,9 @@
/* GC support */ \
do_klass(FillerObject_klass, jdk_internal_vm_FillerObject ) \
\
/* Scoped Values */ \
do_klass(ScopedValue_Carrier_klass, java_lang_ScopedValue_Carrier ) \
\
/*end*/
#endif // SHARE_CLASSFILE_VMCLASSMACROS_HPP

View File

@ -94,13 +94,17 @@ static double strdedup_elapsed_param_ms(Tickspan t) {
void StringDedup::Stat::log_summary(const Stat* last_stat, const Stat* total_stat) {
log_info(stringdedup)(
"Concurrent String Deduplication "
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (new), "
"%zu (inspected), "
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (new unknown), "
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (deduped), "
"avg " STRDEDUP_PERCENT_FORMAT_NS ", "
"total avg deduped/new unknown bytes " STRDEDUP_PERCENT_FORMAT_NS ", "
STRDEDUP_BYTES_FORMAT_NS " (total deduped)," STRDEDUP_BYTES_FORMAT_NS " (total new unknown), "
STRDEDUP_ELAPSED_FORMAT_MS " of " STRDEDUP_ELAPSED_FORMAT_MS,
last_stat->_inspected,
last_stat->_new, STRDEDUP_BYTES_PARAM(last_stat->_new_bytes),
last_stat->_deduped, STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes),
percent_of(total_stat->_deduped_bytes, total_stat->_new_bytes),
STRDEDUP_BYTES_PARAM(total_stat->_deduped_bytes), STRDEDUP_BYTES_PARAM(total_stat->_new_bytes),
strdedup_elapsed_param_ms(last_stat->_process_elapsed),
strdedup_elapsed_param_ms(last_stat->_active_elapsed));
}
@ -210,14 +214,14 @@ void StringDedup::Stat::log_statistics() const {
double deduped_bytes_percent = percent_of(_deduped_bytes, _new_bytes);
double replaced_percent = percent_of(_replaced, _new);
double deleted_percent = percent_of(_deleted, _new);
log_debug(stringdedup)(" Inspected: %12zu", _inspected);
log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
log_debug(stringdedup)(" New: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
log_debug(stringdedup)(" Inspected: %12zu", _inspected);
log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
log_debug(stringdedup)(" New unknown: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
_new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes));
log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
_deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent);
log_debug(stringdedup)(" Skipped: %zu (dead), %zu (incomplete), %zu (shared)",
_skipped_dead, _skipped_incomplete, _skipped_shared);

View File

@ -335,7 +335,13 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
size_t garbage = region->garbage();
size_t live_bytes = region->get_live_data_bytes();
live_data += live_bytes;
if (!region->was_promoted_in_place()) {
// As currently implemented, region->get_live_data_bytes() represents bytes concurrently marked.
// Expansion of the region by promotion during concurrent marking is above TAMS, and is not included
// as live-data at [start of] old marking.
live_data += live_bytes;
}
// else, regions that were promoted in place had 0 old live data at mark start
if (region->is_regular() || region->is_regular_pinned()) {
// Only place regular or pinned regions with live data into the candidate set.
@ -374,7 +380,6 @@ void ShenandoahOldHeuristics::prepare_for_old_collections() {
}
}
// TODO: subtract from live_data bytes promoted during concurrent GC.
_old_generation->set_live_bytes_at_last_mark(live_data);
// Unlike young, we are more interested in efficiently packing OLD-gen than in reclaiming garbage first. We sort by live-data.

View File

@ -223,7 +223,6 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
// We do not need to scan above TAMS because restored top equals tams
assert(obj_addr == tams, "Expect loop to terminate when obj_addr equals tams");
{
ShenandoahHeapLocker locker(_heap->lock());
@ -251,6 +250,7 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
// Transfer this region from young to old, increasing promoted_reserve if available space exceeds plab_min_size()
_heap->free_set()->add_promoted_in_place_region_to_old_collector(region);
region->set_affiliation(OLD_GENERATION);
region->set_promoted_in_place();
}
}
@ -289,6 +289,7 @@ void ShenandoahGenerationalEvacuationTask::promote_humongous(ShenandoahHeapRegio
r->index(), p2i(r->bottom()), p2i(r->top()));
// We mark the entire humongous object's range as dirty after loop terminates, so no need to dirty the range here
r->set_affiliation(OLD_GENERATION);
r->set_promoted_in_place();
}
ShenandoahFreeSet* freeset = _heap->free_set();

View File

@ -262,6 +262,7 @@ private:
HeapWord* volatile _update_watermark;
uint _age;
bool _promoted_in_place;
CENSUS_NOISE(uint _youth;) // tracks epochs of retrograde ageing (rejuvenation)
ShenandoahSharedFlag _recycling; // Used to indicate that the region is being recycled; see try_recycle*().
@ -354,6 +355,15 @@ public:
inline void save_top_before_promote();
inline HeapWord* get_top_before_promote() const { return _top_before_promoted; }
inline void set_promoted_in_place() {
_promoted_in_place = true;
}
// Returns true iff this region was promoted in place subsequent to the most recent start of concurrent old marking.
inline bool was_promoted_in_place() {
return _promoted_in_place;
}
inline void restore_top_before_promote();
inline size_t garbage_before_padded_for_promote() const;
@ -379,7 +389,13 @@ public:
inline void increase_live_data_gc_words(size_t s);
inline bool has_live() const;
// Represents the number of live bytes identified by most recent marking effort. Does not include the bytes
// above TAMS.
inline size_t get_live_data_bytes() const;
// Represents the number of live words identified by most recent marking effort. Does not include the words
// above TAMS.
inline size_t get_live_data_words() const;
inline size_t garbage() const;

View File

@ -152,6 +152,7 @@ inline void ShenandoahHeapRegion::internal_increase_live_data(size_t s) {
inline void ShenandoahHeapRegion::clear_live_data() {
AtomicAccess::store(&_live_data, (size_t)0);
_promoted_in_place = false;
}
inline size_t ShenandoahHeapRegion::get_live_data_words() const {

View File

@ -1289,8 +1289,8 @@
<Field type="ulong" name="inspected" label="Inspected" />
<Field type="ulong" name="known" label="Known" />
<Field type="ulong" name="shared" label="Shared" />
<Field type="ulong" name="newStrings" label="New Strings" />
<Field type="ulong" name="newSize" contentType="bytes" label="New Size" />
<Field type="ulong" name="newStrings" label="New Strings" description="New unknown strings" />
<Field type="ulong" name="newSize" contentType="bytes" label="New Size" description="Size of new unknown strings" />
<Field type="ulong" name="replaced" label="Replaced" />
<Field type="ulong" name="deleted" label="Deleted" />
<Field type="ulong" name="deduplicated" label="Deduplicated" />

View File

@ -31,8 +31,8 @@
#include "opto/movenode.hpp"
#include "opto/mulnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/rangeinference.hpp"
#include "opto/subnode.hpp"
#include "opto/utilities/xor.hpp"
#include "runtime/stubRoutines.hpp"
// Portions of code courtesy of Clifford Click
@ -1011,35 +1011,8 @@ Node* OrINode::Ideal(PhaseGVN* phase, bool can_reshape) {
// the logical operations the ring's ADD is really a logical OR function.
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *OrINode::add_ring( const Type *t0, const Type *t1 ) const {
const TypeInt *r0 = t0->is_int(); // Handy access
const TypeInt *r1 = t1->is_int();
// If both args are bool, can figure out better types
if ( r0 == TypeInt::BOOL ) {
if ( r1 == TypeInt::ONE) {
return TypeInt::ONE;
} else if ( r1 == TypeInt::BOOL ) {
return TypeInt::BOOL;
}
} else if ( r0 == TypeInt::ONE ) {
if ( r1 == TypeInt::BOOL ) {
return TypeInt::ONE;
}
}
// If either input is all ones, the output is all ones.
// x | ~0 == ~0 <==> x | -1 == -1
if (r0 == TypeInt::MINUS_1 || r1 == TypeInt::MINUS_1) {
return TypeInt::MINUS_1;
}
// If either input is not a constant, just return all integers.
if( !r0->is_con() || !r1->is_con() )
return TypeInt::INT; // Any integer, but still no symbols.
// Otherwise just OR them bits.
return TypeInt::make( r0->get_con() | r1->get_con() );
const Type* OrINode::add_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_or(t1->is_int(), t2->is_int());
}
//=============================================================================
@ -1087,22 +1060,8 @@ Node* OrLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
}
//------------------------------add_ring---------------------------------------
const Type *OrLNode::add_ring( const Type *t0, const Type *t1 ) const {
const TypeLong *r0 = t0->is_long(); // Handy access
const TypeLong *r1 = t1->is_long();
// If either input is all ones, the output is all ones.
// x | ~0 == ~0 <==> x | -1 == -1
if (r0 == TypeLong::MINUS_1 || r1 == TypeLong::MINUS_1) {
return TypeLong::MINUS_1;
}
// If either input is not a constant, just return all integers.
if( !r0->is_con() || !r1->is_con() )
return TypeLong::LONG; // Any integer, but still no symbols.
// Otherwise just OR them bits.
return TypeLong::make( r0->get_con() | r1->get_con() );
const Type* OrLNode::add_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_or(t1->is_long(), t2->is_long());
}
//---------------------------Helper -------------------------------------------
@ -1189,46 +1148,14 @@ const Type* XorINode::Value(PhaseGVN* phase) const {
// the logical operations the ring's ADD is really a logical OR function.
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const {
const TypeInt *r0 = t0->is_int(); // Handy access
const TypeInt *r1 = t1->is_int();
if (r0->is_con() && r1->is_con()) {
// compute constant result
return TypeInt::make(r0->get_con() ^ r1->get_con());
}
// At least one of the arguments is not constant
if (r0->_lo >= 0 && r1->_lo >= 0) {
// Combine [r0->_lo, r0->_hi] ^ [r0->_lo, r1->_hi] -> [0, upper_bound]
jint upper_bound = xor_upper_bound_for_ranges<jint, juint>(r0->_hi, r1->_hi);
return TypeInt::make(0, upper_bound, MAX2(r0->_widen, r1->_widen));
}
return TypeInt::INT;
const Type* XorINode::add_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_xor(t1->is_int(), t2->is_int());
}
//=============================================================================
//------------------------------add_ring---------------------------------------
const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const {
const TypeLong *r0 = t0->is_long(); // Handy access
const TypeLong *r1 = t1->is_long();
if (r0->is_con() && r1->is_con()) {
// compute constant result
return TypeLong::make(r0->get_con() ^ r1->get_con());
}
// At least one of the arguments is not constant
if (r0->_lo >= 0 && r1->_lo >= 0) {
// Combine [r0->_lo, r0->_hi] ^ [r0->_lo, r1->_hi] -> [0, upper_bound]
julong upper_bound = xor_upper_bound_for_ranges<jlong, julong>(r0->_hi, r1->_hi);
return TypeLong::make(0, upper_bound, MAX2(r0->_widen, r1->_widen));
}
return TypeLong::LONG;
const Type* XorLNode::add_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_xor(t1->is_long(), t2->is_long());
}
Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
@ -1401,6 +1328,10 @@ static ConstAddOperands as_add_with_constant(Node* n) {
}
Node* MaxNode::IdealI(PhaseGVN* phase, bool can_reshape) {
Node* n = AddNode::Ideal(phase, can_reshape);
if (n != nullptr) {
return n;
}
int opcode = Opcode();
assert(opcode == Op_MinI || opcode == Op_MaxI, "Unexpected opcode");
// Try to transform the following pattern, in any of its four possible

View File

@ -933,8 +933,8 @@ bool RegionNode::optimize_trichotomy(PhaseIterGVN* igvn) {
}
// At this point we know that region->in(idx1) and region->(idx2) map to the same
// value and control flow. Now search for ifs that feed into these region inputs.
ProjNode* proj1 = region->in(idx1)->isa_Proj();
ProjNode* proj2 = region->in(idx2)->isa_Proj();
IfProjNode* proj1 = region->in(idx1)->isa_IfProj();
IfProjNode* proj2 = region->in(idx2)->isa_IfProj();
if (proj1 == nullptr || proj1->outcnt() != 1 ||
proj2 == nullptr || proj2->outcnt() != 1) {
return false; // No projection inputs with region as unique user found
@ -1547,18 +1547,9 @@ Node* PhiNode::Identity(PhaseGVN* phase) {
Node* phi_reg = region();
for (DUIterator_Fast imax, i = phi_reg->fast_outs(imax); i < imax; i++) {
Node* u = phi_reg->fast_out(i);
if (u->is_Phi() && u->as_Phi()->type() == Type::MEMORY &&
u->adr_type() == TypePtr::BOTTOM && u->in(0) == phi_reg &&
u->req() == phi_len) {
for (uint j = 1; j < phi_len; j++) {
if (in(j) != u->in(j)) {
u = nullptr;
break;
}
}
if (u != nullptr) {
return u;
}
assert(!u->is_Phi() || u->in(0) == phi_reg, "broken Phi/Region subgraph");
if (u->is_Phi() && u->req() == phi_len && can_be_replaced_by(u->as_Phi())) {
return u;
}
}
}
@ -2790,6 +2781,25 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
progress = merge_through_phi(this, phase->is_IterGVN());
}
// PhiNode::Identity replaces a non-bottom memory phi with a bottom memory phi with the same inputs, if it exists.
// If the bottom memory phi's inputs are changed (so it can now replace the non-bottom memory phi) or if it's created
// only after the non-bottom memory phi is processed by igvn, PhiNode::Identity doesn't run and the transformation
// doesn't happen.
// Look for non-bottom Phis that should be transformed and enqueue them for igvn so that PhiNode::Identity executes for
// them.
if (can_reshape && type() == Type::MEMORY && adr_type() == TypePtr::BOTTOM) {
PhaseIterGVN* igvn = phase->is_IterGVN();
uint phi_len = req();
Node* phi_reg = region();
for (DUIterator_Fast imax, i = phi_reg->fast_outs(imax); i < imax; i++) {
Node* u = phi_reg->fast_out(i);
assert(!u->is_Phi() || (u->in(0) == phi_reg && u->req() == phi_len), "broken Phi/Region subgraph");
if (u->is_Phi() && u->as_Phi()->can_be_replaced_by(this)) {
igvn->_worklist.push(u);
}
}
}
return progress; // Return any progress
}
@ -2839,6 +2849,11 @@ const TypeTuple* PhiNode::collect_types(PhaseGVN* phase) const {
return TypeTuple::make(types.length(), flds);
}
bool PhiNode::can_be_replaced_by(const PhiNode* other) const {
return type() == Type::MEMORY && other->type() == Type::MEMORY && adr_type() != TypePtr::BOTTOM &&
other->adr_type() == TypePtr::BOTTOM && has_same_inputs_as(other);
}
Node* PhiNode::clone_through_phi(Node* root_phi, const Type* t, uint c, PhaseIterGVN* igvn) {
Node_Stack stack(1);
VectorSet visited;

View File

@ -274,6 +274,7 @@ public:
#endif //ASSERT
const TypeTuple* collect_types(PhaseGVN* phase) const;
bool can_be_replaced_by(const PhiNode* other) const;
};
//------------------------------GotoNode---------------------------------------
@ -342,15 +343,15 @@ class IfNode : public MultiBranchNode {
// Helper methods for fold_compares
bool cmpi_folds(PhaseIterGVN* igvn, bool fold_ne = false);
bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);
bool has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fail);
bool has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNode*& fail, PhaseIterGVN* igvn);
Node* merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn);
bool has_shared_region(IfProjNode* proj, IfProjNode*& success, IfProjNode*& fail) const;
bool has_only_uncommon_traps(IfProjNode* proj, IfProjNode*& success, IfProjNode*& fail, PhaseIterGVN* igvn) const;
Node* merge_uncommon_traps(IfProjNode* proj, IfProjNode* success, IfProjNode* fail, PhaseIterGVN* igvn);
static void improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGVN* igvn);
bool is_cmp_with_loadrange(ProjNode* proj);
bool is_null_check(ProjNode* proj, PhaseIterGVN* igvn);
bool is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn);
void reroute_side_effect_free_unc(ProjNode* proj, ProjNode* dom_proj, PhaseIterGVN* igvn);
bool fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn);
bool is_cmp_with_loadrange(IfProjNode* proj) const;
bool is_null_check(IfProjNode* proj, PhaseIterGVN* igvn) const;
bool is_side_effect_free_test(IfProjNode* proj, PhaseIterGVN* igvn) const;
static void reroute_side_effect_free_unc(IfProjNode* proj, IfProjNode* dom_proj, PhaseIterGVN* igvn);
bool fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjNode* fail, PhaseIterGVN* igvn);
static bool is_dominator_unc(CallStaticJavaNode* dom_unc, CallStaticJavaNode* unc);
protected:
@ -559,6 +560,11 @@ public:
IfProjNode(IfNode *ifnode, uint idx) : CProjNode(ifnode,idx) {}
virtual Node* Identity(PhaseGVN* phase);
// Return the other IfProj node.
IfProjNode* other_if_proj() const {
return in(0)->as_If()->proj_out(1 - _con)->as_IfProj();
}
void pin_array_access_nodes(PhaseIterGVN* igvn);
protected:

View File

@ -504,6 +504,102 @@ Node* unsigned_div_ideal(PhaseGVN* phase, bool can_reshape, Node* div) {
return nullptr;
}
template<typename IntegerType>
static const IntegerType* compute_signed_div_type(const IntegerType* i1, const IntegerType* i2) {
typedef typename IntegerType::NativeType NativeType;
assert(!i2->is_con() || i2->get_con() != 0, "Can't handle zero constant divisor");
int widen = MAX2(i1->_widen, i2->_widen);
// Case A: divisor range spans zero (i2->_lo < 0 < i2->_hi)
// We split into two subproblems to avoid division by 0:
// - negative part: [i2->_lo, 1]
// - positive part: [1, i2->_hi]
// Then we union the results by taking the min of all lowerbounds and
// the max of all upperbounds from the two halves.
if (i2->_lo < 0 && i2->_hi > 0) {
// Handle negative part of the divisor range
const IntegerType* neg_part = compute_signed_div_type(i1, IntegerType::make(i2->_lo, -1, widen));
// Handle positive part of the divisor range
const IntegerType* pos_part = compute_signed_div_type(i1, IntegerType::make(1, i2->_hi, widen));
// Merge results
NativeType new_lo = MIN2(neg_part->_lo, pos_part->_lo);
NativeType new_hi = MAX2(neg_part->_hi, pos_part->_hi);
assert(new_hi >= new_lo, "sanity");
return IntegerType::make(new_lo, new_hi, widen);
}
// Case B: divisor range does NOT span zero.
// Here i2 is entirely negative or entirely positive.
// Then i1/i2 is monotonic in i1 and i2 (when i2 keeps the same sign).
// Therefore the extrema occur at the four “corners”:
// (i1->_lo, i2->_hi), (i1->_lo, i2->_lo), (i1->_hi, i2->_lo), (i1->_hi, i2->_hi).
// We compute all four and take the min and max.
// A special case handles overflow when dividing the mostnegative value by 1.
// adjust i2 bounds to not include zero, as zero always throws
NativeType i2_lo = i2->_lo == 0 ? 1 : i2->_lo;
NativeType i2_hi = i2->_hi == 0 ? -1 : i2->_hi;
constexpr NativeType min_val = std::numeric_limits<NativeType>::min();
static_assert(min_val == min_jint || min_val == min_jlong, "min has to be either min_jint or min_jlong");
constexpr NativeType max_val = std::numeric_limits<NativeType>::max();
static_assert(max_val == max_jint || max_val == max_jlong, "max has to be either max_jint or max_jlong");
// Special overflow case: min_val / (-1) == min_val (cf. JVMS§6.5 idiv/ldiv)
// We need to be careful that we never run min_val / (-1) in C++ code, as this overflow is UB there
if (i1->_lo == min_val && i2_hi == -1) {
NativeType new_lo = min_val;
NativeType new_hi;
// compute new_hi depending on whether divisor or dividend is non-constant.
// i2 is purely in the negative domain here (as i2_hi is -1)
// which means the maximum value this division can yield is either
if (!i1->is_con()) {
// a) non-constant dividend: i1 could be min_val + 1.
// -> i1 / i2 = (min_val + 1) / -1 = max_val is possible.
new_hi = max_val;
assert((min_val + 1) / -1 == new_hi, "new_hi should be max_val");
} else if (i2_lo != i2_hi) {
// b) i1 is constant min_val, i2 is non-constant.
// if i2 = -1 -> i1 / i2 = min_val / -1 = min_val
// if i2 < -1 -> i1 / i2 <= min_val / -2 = (max_val / 2) + 1
new_hi = (max_val / 2) + 1;
assert(min_val / -2 == new_hi, "new_hi should be (max_val / 2) + 1)");
} else {
// c) i1 is constant min_val, i2 is constant -1.
// -> i1 / i2 = min_val / -1 = min_val
new_hi = min_val;
}
#ifdef ASSERT
// validate new_hi for non-constant divisor
if (i2_lo != i2_hi) {
assert(i2_lo != -1, "Special case not possible here, as i2_lo has to be < i2_hi");
NativeType result = i1->_lo / i2_lo;
assert(new_hi >= result, "computed wrong value for new_hi");
}
// validate new_hi for non-constant dividend
if (!i1->is_con()) {
assert(i2_hi > min_val, "Special case not possible here, as i1->_hi has to be > min");
NativeType result1 = i1->_hi / i2_lo;
NativeType result2 = i1->_hi / i2_hi;
assert(new_hi >= result1 && new_hi >= result2, "computed wrong value for new_hi");
}
#endif
return IntegerType::make(new_lo, new_hi, widen);
}
assert((i1->_lo != min_val && i1->_hi != min_val) || (i2_hi != -1 && i2_lo != -1), "should have filtered out before");
// Special case not possible here, calculate all corners normally
NativeType corner1 = i1->_lo / i2_lo;
NativeType corner2 = i1->_lo / i2_hi;
NativeType corner3 = i1->_hi / i2_lo;
NativeType corner4 = i1->_hi / i2_hi;
NativeType new_lo = MIN4(corner1, corner2, corner3, corner4);
NativeType new_hi = MAX4(corner1, corner2, corner3, corner4);
return IntegerType::make(new_lo, new_hi, widen);
}
//=============================================================================
//------------------------------Identity---------------------------------------
@ -549,65 +645,26 @@ Node *DivINode::Ideal(PhaseGVN *phase, bool can_reshape) {
// prevent hoisting the divide above an unsafe test.
const Type* DivINode::Value(PhaseGVN* phase) const {
// Either input is TOP ==> the result is TOP
const Type *t1 = phase->type( in(1) );
const Type *t2 = phase->type( in(2) );
if( t1 == Type::TOP ) return Type::TOP;
if( t2 == Type::TOP ) return Type::TOP;
const Type* t1 = phase->type(in(1));
const Type* t2 = phase->type(in(2));
if (t1 == Type::TOP || t2 == Type::TOP) {
return Type::TOP;
}
if (t2 == TypeInt::ZERO) {
// this division will always throw an exception
return Type::TOP;
}
// x/x == 1 since we always generate the dynamic divisor check for 0.
if (in(1) == in(2)) {
return TypeInt::ONE;
}
// Either input is BOTTOM ==> the result is the local BOTTOM
const Type *bot = bottom_type();
if( (t1 == bot) || (t2 == bot) ||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
return bot;
const TypeInt* i1 = t1->is_int();
const TypeInt* i2 = t2->is_int();
// Divide the two numbers. We approximate.
// If divisor is a constant and not zero
const TypeInt *i1 = t1->is_int();
const TypeInt *i2 = t2->is_int();
int widen = MAX2(i1->_widen, i2->_widen);
if( i2->is_con() && i2->get_con() != 0 ) {
int32_t d = i2->get_con(); // Divisor
jint lo, hi;
if( d >= 0 ) {
lo = i1->_lo/d;
hi = i1->_hi/d;
} else {
if( d == -1 && i1->_lo == min_jint ) {
// 'min_jint/-1' throws arithmetic exception during compilation
lo = min_jint;
// do not support holes, 'hi' must go to either min_jint or max_jint:
// [min_jint, -10]/[-1,-1] ==> [min_jint] UNION [10,max_jint]
hi = i1->_hi == min_jint ? min_jint : max_jint;
} else {
lo = i1->_hi/d;
hi = i1->_lo/d;
}
}
return TypeInt::make(lo, hi, widen);
}
// If the dividend is a constant
if( i1->is_con() ) {
int32_t d = i1->get_con();
if( d < 0 ) {
if( d == min_jint ) {
// (-min_jint) == min_jint == (min_jint / -1)
return TypeInt::make(min_jint, max_jint/2 + 1, widen);
} else {
return TypeInt::make(d, -d, widen);
}
}
return TypeInt::make(-d, d, widen);
}
// Otherwise we give up all hope
return TypeInt::INT;
return compute_signed_div_type<TypeInt>(i1, i2);
}
@ -655,65 +712,26 @@ Node *DivLNode::Ideal( PhaseGVN *phase, bool can_reshape) {
// prevent hoisting the divide above an unsafe test.
const Type* DivLNode::Value(PhaseGVN* phase) const {
// Either input is TOP ==> the result is TOP
const Type *t1 = phase->type( in(1) );
const Type *t2 = phase->type( in(2) );
if( t1 == Type::TOP ) return Type::TOP;
if( t2 == Type::TOP ) return Type::TOP;
const Type* t1 = phase->type(in(1));
const Type* t2 = phase->type(in(2));
if (t1 == Type::TOP || t2 == Type::TOP) {
return Type::TOP;
}
if (t2 == TypeLong::ZERO) {
// this division will always throw an exception
return Type::TOP;
}
// x/x == 1 since we always generate the dynamic divisor check for 0.
if (in(1) == in(2)) {
return TypeLong::ONE;
}
// Either input is BOTTOM ==> the result is the local BOTTOM
const Type *bot = bottom_type();
if( (t1 == bot) || (t2 == bot) ||
(t1 == Type::BOTTOM) || (t2 == Type::BOTTOM) )
return bot;
const TypeLong* i1 = t1->is_long();
const TypeLong* i2 = t2->is_long();
// Divide the two numbers. We approximate.
// If divisor is a constant and not zero
const TypeLong *i1 = t1->is_long();
const TypeLong *i2 = t2->is_long();
int widen = MAX2(i1->_widen, i2->_widen);
if( i2->is_con() && i2->get_con() != 0 ) {
jlong d = i2->get_con(); // Divisor
jlong lo, hi;
if( d >= 0 ) {
lo = i1->_lo/d;
hi = i1->_hi/d;
} else {
if( d == CONST64(-1) && i1->_lo == min_jlong ) {
// 'min_jlong/-1' throws arithmetic exception during compilation
lo = min_jlong;
// do not support holes, 'hi' must go to either min_jlong or max_jlong:
// [min_jlong, -10]/[-1,-1] ==> [min_jlong] UNION [10,max_jlong]
hi = i1->_hi == min_jlong ? min_jlong : max_jlong;
} else {
lo = i1->_hi/d;
hi = i1->_lo/d;
}
}
return TypeLong::make(lo, hi, widen);
}
// If the dividend is a constant
if( i1->is_con() ) {
jlong d = i1->get_con();
if( d < 0 ) {
if( d == min_jlong ) {
// (-min_jlong) == min_jlong == (min_jlong / -1)
return TypeLong::make(min_jlong, max_jlong/2 + 1, widen);
} else {
return TypeLong::make(d, -d, widen);
}
}
return TypeLong::make(-d, d, widen);
}
// Otherwise we give up all hope
return TypeLong::LONG;
return compute_signed_div_type<TypeLong>(i1, i2);
}

View File

@ -771,7 +771,7 @@ bool IfNode::cmpi_folds(PhaseIterGVN* igvn, bool fold_ne) {
// Is a dominating control suitable for folding with this if?
bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) {
return ctrl != nullptr &&
ctrl->is_Proj() &&
ctrl->is_IfProj() &&
ctrl->outcnt() == 1 && // No side-effects
ctrl->in(0) != nullptr &&
ctrl->in(0)->Opcode() == Op_If &&
@ -784,8 +784,8 @@ bool IfNode::is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn) {
}
// Do this If and the dominating If share a region?
bool IfNode::has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fail) {
ProjNode* otherproj = proj->other_if_proj();
bool IfNode::has_shared_region(IfProjNode* proj, IfProjNode*& success, IfProjNode*& fail) const {
IfProjNode* otherproj = proj->other_if_proj();
Node* otherproj_ctrl_use = otherproj->unique_ctrl_out_or_null();
RegionNode* region = (otherproj_ctrl_use != nullptr && otherproj_ctrl_use->is_Region()) ? otherproj_ctrl_use->as_Region() : nullptr;
success = nullptr;
@ -793,13 +793,14 @@ bool IfNode::has_shared_region(ProjNode* proj, ProjNode*& success, ProjNode*& fa
if (otherproj->outcnt() == 1 && region != nullptr && !region->has_phi()) {
for (int i = 0; i < 2; i++) {
ProjNode* proj = proj_out(i);
if (success == nullptr && proj->outcnt() == 1 && proj->unique_out() == region) {
success = proj;
IfProjNode* next_proj = proj_out(i)->as_IfProj();
if (success == nullptr && next_proj->outcnt() == 1 && next_proj->unique_out() == region) {
success = next_proj;
} else if (fail == nullptr) {
fail = proj;
fail = next_proj;
} else {
success = fail = nullptr;
success = nullptr;
fail = nullptr;
}
}
}
@ -850,8 +851,8 @@ ProjNode* IfNode::uncommon_trap_proj(CallStaticJavaNode*& call, Deoptimization::
}
// Do this If and the dominating If both branch out to an uncommon trap
bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNode*& fail, PhaseIterGVN* igvn) {
ProjNode* otherproj = proj->other_if_proj();
bool IfNode::has_only_uncommon_traps(IfProjNode* proj, IfProjNode*& success, IfProjNode*& fail, PhaseIterGVN* igvn) const {
IfProjNode* otherproj = proj->other_if_proj();
CallStaticJavaNode* dom_unc = otherproj->is_uncommon_trap_proj();
if (otherproj->outcnt() == 1 && dom_unc != nullptr) {
@ -888,8 +889,8 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod
!igvn->C->too_many_traps(dom_method, dom_bci, Deoptimization::Reason_range_check) &&
// Return true if c2 manages to reconcile with UnstableIf optimization. See the comments for it.
igvn->C->remove_unstable_if_trap(dom_unc, true/*yield*/)) {
success = unc_proj;
fail = unc_proj->other_if_proj();
success = unc_proj->as_IfProj();
fail = unc_proj->as_IfProj()->other_if_proj();
return true;
}
}
@ -898,7 +899,7 @@ bool IfNode::has_only_uncommon_traps(ProjNode* proj, ProjNode*& success, ProjNod
}
// Check that the 2 CmpI can be folded into as single CmpU and proceed with the folding
bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn) {
bool IfNode::fold_compares_helper(IfProjNode* proj, IfProjNode* success, IfProjNode* fail, PhaseIterGVN* igvn) {
Node* this_cmp = in(1)->in(1);
BoolNode* this_bool = in(1)->as_Bool();
IfNode* dom_iff = proj->in(0)->as_If();
@ -906,7 +907,7 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
Node* lo = dom_iff->in(1)->in(1)->in(2);
Node* hi = this_cmp->in(2);
Node* n = this_cmp->in(1);
ProjNode* otherproj = proj->other_if_proj();
IfProjNode* otherproj = proj->other_if_proj();
const TypeInt* lo_type = IfNode::filtered_int_type(igvn, n, otherproj);
const TypeInt* hi_type = IfNode::filtered_int_type(igvn, n, success);
@ -1108,11 +1109,11 @@ bool IfNode::fold_compares_helper(ProjNode* proj, ProjNode* success, ProjNode* f
// Merge the branches that trap for this If and the dominating If into
// a single region that branches to the uncommon trap for the
// dominating If
Node* IfNode::merge_uncommon_traps(ProjNode* proj, ProjNode* success, ProjNode* fail, PhaseIterGVN* igvn) {
Node* IfNode::merge_uncommon_traps(IfProjNode* proj, IfProjNode* success, IfProjNode* fail, PhaseIterGVN* igvn) {
Node* res = this;
assert(success->in(0) == this, "bad projection");
ProjNode* otherproj = proj->other_if_proj();
IfProjNode* otherproj = proj->other_if_proj();
CallStaticJavaNode* unc = success->is_uncommon_trap_proj();
CallStaticJavaNode* dom_unc = otherproj->is_uncommon_trap_proj();
@ -1239,7 +1240,7 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV
#endif
}
bool IfNode::is_cmp_with_loadrange(ProjNode* proj) {
bool IfNode::is_cmp_with_loadrange(IfProjNode* proj) const {
if (in(1) != nullptr &&
in(1)->in(1) != nullptr &&
in(1)->in(1)->in(2) != nullptr) {
@ -1258,7 +1259,7 @@ bool IfNode::is_cmp_with_loadrange(ProjNode* proj) {
return false;
}
bool IfNode::is_null_check(ProjNode* proj, PhaseIterGVN* igvn) {
bool IfNode::is_null_check(IfProjNode* proj, PhaseIterGVN* igvn) const {
Node* other = in(1)->in(1)->in(2);
if (other->in(MemNode::Address) != nullptr &&
proj->in(0)->in(1) != nullptr &&
@ -1275,7 +1276,7 @@ bool IfNode::is_null_check(ProjNode* proj, PhaseIterGVN* igvn) {
// Check that the If that is in between the 2 integer comparisons has
// no side effect
bool IfNode::is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn) {
bool IfNode::is_side_effect_free_test(IfProjNode* proj, PhaseIterGVN* igvn) const {
if (proj == nullptr) {
return false;
}
@ -1315,9 +1316,9 @@ bool IfNode::is_side_effect_free_test(ProjNode* proj, PhaseIterGVN* igvn) {
// won't be guarded by the first CmpI anymore. It can trap in cases
// where the first CmpI would have prevented it from executing: on a
// trap, we need to restart execution at the state of the first CmpI
void IfNode::reroute_side_effect_free_unc(ProjNode* proj, ProjNode* dom_proj, PhaseIterGVN* igvn) {
void IfNode::reroute_side_effect_free_unc(IfProjNode* proj, IfProjNode* dom_proj, PhaseIterGVN* igvn) {
CallStaticJavaNode* dom_unc = dom_proj->is_uncommon_trap_if_pattern();
ProjNode* otherproj = proj->other_if_proj();
IfProjNode* otherproj = proj->other_if_proj();
CallStaticJavaNode* unc = proj->is_uncommon_trap_if_pattern();
Node* call_proj = dom_unc->unique_ctrl_out();
Node* halt = call_proj->unique_ctrl_out();
@ -1348,9 +1349,9 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) {
if (is_ctrl_folds(ctrl, igvn)) {
// A integer comparison immediately dominated by another integer
// comparison
ProjNode* success = nullptr;
ProjNode* fail = nullptr;
ProjNode* dom_cmp = ctrl->as_Proj();
IfProjNode* success = nullptr;
IfProjNode* fail = nullptr;
IfProjNode* dom_cmp = ctrl->as_IfProj();
if (has_shared_region(dom_cmp, success, fail) &&
// Next call modifies graph so must be last
fold_compares_helper(dom_cmp, success, fail, igvn)) {
@ -1364,11 +1365,11 @@ Node* IfNode::fold_compares(PhaseIterGVN* igvn) {
return nullptr;
} else if (ctrl->in(0) != nullptr &&
ctrl->in(0)->in(0) != nullptr) {
ProjNode* success = nullptr;
ProjNode* fail = nullptr;
IfProjNode* success = nullptr;
IfProjNode* fail = nullptr;
Node* dom = ctrl->in(0)->in(0);
ProjNode* dom_cmp = dom->isa_Proj();
ProjNode* other_cmp = ctrl->isa_Proj();
IfProjNode* dom_cmp = dom->isa_IfProj();
IfProjNode* other_cmp = ctrl->isa_IfProj();
// Check if it's an integer comparison dominated by another
// integer comparison with another test in between

View File

@ -6171,7 +6171,7 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr) {
CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* node) {
if (node->is_IfProj()) {
Node* other_proj = node->as_IfProj()->other_if_proj();
IfProjNode* other_proj = node->as_IfProj()->other_if_proj();
for (DUIterator_Fast jmax, j = other_proj->fast_outs(jmax); j < jmax; j++) {
Node* obs = other_proj->fast_out(j);
if (obs->in(0) == other_proj && obs->is_CallStaticJava() &&

View File

@ -3103,7 +3103,7 @@ MergePrimitiveStores::CFGStatus MergePrimitiveStores::cfg_status_for_pair(const
ctrl_use->in(0)->outcnt() != 2) {
return CFGStatus::Failure; // Not RangeCheck.
}
ProjNode* other_proj = ctrl_use->as_IfProj()->other_if_proj();
IfProjNode* other_proj = ctrl_use->as_IfProj()->other_if_proj();
Node* trap = other_proj->is_uncommon_trap_proj(Deoptimization::Reason_range_check);
if (trap != merge_mem->unique_out() ||
ctrl_use->in(0)->in(0) != ctrl_def) {

View File

@ -29,6 +29,7 @@
#include "opto/memnode.hpp"
#include "opto/mulnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/rangeinference.hpp"
#include "opto/subnode.hpp"
#include "utilities/powerOfTwo.hpp"
@ -620,80 +621,14 @@ const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) {
return TypeLong::LONG;
}
template<typename IntegerType>
static const IntegerType* and_value(const IntegerType* r0, const IntegerType* r1) {
typedef typename IntegerType::NativeType NativeType;
static_assert(std::is_signed<NativeType>::value, "Native type of IntegerType must be signed!");
int widen = MAX2(r0->_widen, r1->_widen);
// If both types are constants, we can calculate a constant result.
if (r0->is_con() && r1->is_con()) {
return IntegerType::make(r0->get_con() & r1->get_con());
}
// If both ranges are positive, the result will range from 0 up to the hi value of the smaller range. The minimum
// of the two constrains the upper bound because any higher value in the other range will see all zeroes, so it will be masked out.
if (r0->_lo >= 0 && r1->_lo >= 0) {
return IntegerType::make(0, MIN2(r0->_hi, r1->_hi), widen);
}
// If only one range is positive, the result will range from 0 up to that range's maximum value.
// For the operation 'x & C' where C is a positive constant, the result will be in the range [0..C]. With that observation,
// we can say that for any integer c such that 0 <= c <= C will also be in the range [0..C]. Therefore, 'x & [c..C]'
// where c >= 0 will be in the range [0..C].
if (r0->_lo >= 0) {
return IntegerType::make(0, r0->_hi, widen);
}
if (r1->_lo >= 0) {
return IntegerType::make(0, r1->_hi, widen);
}
// At this point, all positive ranges will have already been handled, so the only remaining cases will be negative ranges
// and constants.
assert(r0->_lo < 0 && r1->_lo < 0, "positive ranges should already be handled!");
// As two's complement means that both numbers will start with leading 1s, the lower bound of both ranges will contain
// the common leading 1s of both minimum values. In order to count them with count_leading_zeros, the bits are inverted.
NativeType sel_val = ~MIN2(r0->_lo, r1->_lo);
NativeType min;
if (sel_val == 0) {
// Since count_leading_zeros is undefined at 0, we short-circuit the condition where both ranges have a minimum of -1.
min = -1;
} else {
// To get the number of bits to shift, we count the leading 0-bits and then subtract one, as the sign bit is already set.
int shift_bits = count_leading_zeros(sel_val) - 1;
min = std::numeric_limits<NativeType>::min() >> shift_bits;
}
NativeType max;
if (r0->_hi < 0 && r1->_hi < 0) {
// If both ranges are negative, then the same optimization as both positive ranges will apply, and the smaller hi
// value will mask off any bits set by higher values.
max = MIN2(r0->_hi, r1->_hi);
} else {
// In the case of ranges that cross zero, negative values can cause the higher order bits to be set, so the maximum
// positive value can be as high as the larger hi value.
max = MAX2(r0->_hi, r1->_hi);
}
return IntegerType::make(min, max, widen);
}
//=============================================================================
//------------------------------mul_ring---------------------------------------
// Supplied function returns the product of the inputs IN THE CURRENT RING.
// For the logical operations the ring's MUL is really a logical AND function.
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *AndINode::mul_ring( const Type *t0, const Type *t1 ) const {
const TypeInt* r0 = t0->is_int();
const TypeInt* r1 = t1->is_int();
return and_value<TypeInt>(r0, r1);
const Type* AndINode::mul_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_and(t1->is_int(), t2->is_int());
}
static bool AndIL_is_zero_element_under_mask(const PhaseGVN* phase, const Node* expr, const Node* mask, BasicType bt);
@ -822,11 +757,8 @@ Node *AndINode::Ideal(PhaseGVN *phase, bool can_reshape) {
// For the logical operations the ring's MUL is really a logical AND function.
// This also type-checks the inputs for sanity. Guaranteed never to
// be passed a TOP or BOTTOM type, these are filtered out by pre-check.
const Type *AndLNode::mul_ring( const Type *t0, const Type *t1 ) const {
const TypeLong* r0 = t0->is_long();
const TypeLong* r1 = t1->is_long();
return and_value<TypeLong>(r0, r1);
const Type* AndLNode::mul_ring(const Type* t1, const Type* t2) const {
return RangeInference::infer_and(t1->is_long(), t2->is_long());
}
const Type* AndLNode::Value(PhaseGVN* phase) const {

View File

@ -260,12 +260,7 @@ CallStaticJavaNode* ProjNode::is_uncommon_trap_if_pattern(Deoptimization::DeoptR
// Not a projection of an If or variation of a dead If node.
return nullptr;
}
return other_if_proj()->is_uncommon_trap_proj(reason);
}
ProjNode* ProjNode::other_if_proj() const {
assert(_con == 0 || _con == 1, "not an if?");
return in(0)->as_If()->proj_out(1-_con);
return as_IfProj()->other_if_proj()->is_uncommon_trap_proj(reason);
}
NarrowMemProjNode::NarrowMemProjNode(InitializeNode* src, const TypePtr* adr_type)

View File

@ -200,9 +200,6 @@ public:
// other_proj->[region->..]call_uct"
// null otherwise
CallStaticJavaNode* is_uncommon_trap_if_pattern(Deoptimization::DeoptReason reason = Deoptimization::Reason_none) const;
// Return other proj node when this is a If proj node
ProjNode* other_if_proj() const;
};
// A ProjNode variant that captures an adr_type(). Used as a projection of InitializeNode to have the right adr_type()

View File

@ -999,18 +999,22 @@ bool Node::has_out_with(int opcode1, int opcode2, int opcode3, int opcode4) {
//---------------------------uncast_helper-------------------------------------
Node* Node::uncast_helper(const Node* p, bool keep_deps) {
#ifdef ASSERT
// If we end up traversing more nodes than we actually have,
// it is definitely an infinite loop.
uint max_depth = Compile::current()->unique();
uint depth_count = 0;
const Node* orig_p = p;
#endif
while (true) {
#ifdef ASSERT
if (depth_count >= K) {
if (depth_count++ >= max_depth) {
orig_p->dump(4);
if (p != orig_p)
if (p != orig_p) {
p->dump(1);
}
fatal("infinite loop in Node::uncast_helper");
}
assert(depth_count++ < K, "infinite loop in Node::uncast_helper");
#endif
if (p == nullptr || p->req() != 2) {
break;
@ -2875,16 +2879,9 @@ Node* Node::find_similar(int opc) {
Node* use = def->fast_out(i);
if (use != this &&
use->Opcode() == opc &&
use->req() == req()) {
uint j;
for (j = 0; j < use->req(); j++) {
if (use->in(j) != in(j)) {
break;
}
}
if (j == use->req()) {
return use;
}
use->req() == req() &&
has_same_inputs_as(use)) {
return use;
}
}
}
@ -2892,6 +2889,16 @@ Node* Node::find_similar(int opc) {
return nullptr;
}
bool Node::has_same_inputs_as(const Node* other) const {
assert(req() == other->req(), "should have same number of inputs");
for (uint j = 0; j < other->req(); j++) {
if (in(j) != other->in(j)) {
return false;
}
}
return true;
}
Node* Node::unique_multiple_edges_out_or_null() const {
Node* use = nullptr;
for (DUIterator_Fast kmax, k = fast_outs(kmax); k < kmax; k++) {

View File

@ -1179,6 +1179,7 @@ public:
// Return a node with opcode "opc" and same inputs as "this" if one can
// be found; Otherwise return null;
Node* find_similar(int opc);
bool has_same_inputs_as(const Node* other) const;
// Return the unique control out if only one. Null if none or more than one.
Node* unique_ctrl_out_or_null() const;

View File

@ -65,7 +65,7 @@ bool AssertionPredicate::has_assertion_predicate_opaque(const Node* predicate_pr
// Check if the other projection (UCT projection) of `success_proj` has a Halt node as output.
bool AssertionPredicate::has_halt(const IfTrueNode* success_proj) {
ProjNode* other_proj = success_proj->other_if_proj();
IfProjNode* other_proj = success_proj->other_if_proj();
return other_proj->outcnt() == 1 && other_proj->unique_out()->Opcode() == Op_Halt;
}
@ -396,7 +396,7 @@ bool InitializedAssertionPredicate::is_predicate(const Node* maybe_success_proj)
#ifdef ASSERT
bool InitializedAssertionPredicate::has_halt(const IfTrueNode* success_proj) {
ProjNode* other_proj = success_proj->other_if_proj();
IfProjNode* other_proj = success_proj->other_if_proj();
if (other_proj->outcnt() != 1) {
return false;
}

View File

@ -25,7 +25,6 @@
#include "opto/rangeinference.hpp"
#include "opto/type.hpp"
#include "utilities/intn_t.hpp"
#include "utilities/tuple.hpp"
// If the cardinality of a TypeInt is below this threshold, use min widen, see
// TypeIntPrototype<S, U>::normalize_widen
@ -688,6 +687,8 @@ template class TypeIntPrototype<intn_t<1>, uintn_t<1>>;
template class TypeIntPrototype<intn_t<2>, uintn_t<2>>;
template class TypeIntPrototype<intn_t<3>, uintn_t<3>>;
template class TypeIntPrototype<intn_t<4>, uintn_t<4>>;
template class TypeIntPrototype<intn_t<5>, uintn_t<5>>;
template class TypeIntPrototype<intn_t<6>, uintn_t<6>>;
// Compute the meet of 2 types. When dual is true, the subset relation in CT is
// reversed. This means that the result of 2 CTs would be the intersection of
@ -709,10 +710,7 @@ const Type* TypeIntHelper::int_type_xmeet(const CT* i1, const Type* t2) {
if (!i1->_is_dual) {
// meet (a.k.a union)
return CT::make_or_top(TypeIntPrototype<S, U>{{MIN2(i1->_lo, i2->_lo), MAX2(i1->_hi, i2->_hi)},
{MIN2(i1->_ulo, i2->_ulo), MAX2(i1->_uhi, i2->_uhi)},
{i1->_bits._zeros & i2->_bits._zeros, i1->_bits._ones & i2->_bits._ones}},
MAX2(i1->_widen, i2->_widen), false);
return int_type_union(i1, i2);
} else {
// join (a.k.a intersection)
return CT::make_or_top(TypeIntPrototype<S, U>{{MAX2(i1->_lo, i2->_lo), MIN2(i1->_hi, i2->_hi)},

View File

@ -25,6 +25,7 @@
#ifndef SHARE_OPTO_RANGEINFERENCE_HPP
#define SHARE_OPTO_RANGEINFERENCE_HPP
#include "cppstdlib/limits.hpp"
#include "cppstdlib/type_traits.hpp"
#include "utilities/globalDefinitions.hpp"
@ -92,19 +93,6 @@ public:
RangeInt<U> _urange;
KnownBits<U> _bits;
private:
friend class TypeInt;
friend class TypeLong;
template <class T1, class T2>
friend void test_canonicalize_constraints_exhaustive();
template <class T1, class T2>
friend void test_canonicalize_constraints_simple();
template <class T1, class T2>
friend void test_canonicalize_constraints_random();
// A canonicalized version of a TypeIntPrototype, if the prototype represents
// an empty type, _present is false, otherwise, _data is canonical
class CanonicalizedTypeIntPrototype {
@ -158,21 +146,33 @@ public:
template <class CT>
static const Type* int_type_xmeet(const CT* i1, const Type* t2);
template <class CT>
static bool int_type_is_equal(const CT* t1, const CT* t2) {
template <class CTP>
static CTP int_type_union(CTP t1, CTP t2) {
using CT = std::conditional_t<std::is_pointer_v<CTP>, std::remove_pointer_t<CTP>, CTP>;
using S = std::remove_const_t<decltype(CT::_lo)>;
using U = std::remove_const_t<decltype(CT::_ulo)>;
return CT::make(TypeIntPrototype<S, U>{{MIN2(t1->_lo, t2->_lo), MAX2(t1->_hi, t2->_hi)},
{MIN2(t1->_ulo, t2->_ulo), MAX2(t1->_uhi, t2->_uhi)},
{t1->_bits._zeros & t2->_bits._zeros, t1->_bits._ones & t2->_bits._ones}},
MAX2(t1->_widen, t2->_widen));
}
template <class CTP>
static bool int_type_is_equal(const CTP t1, const CTP t2) {
return t1->_lo == t2->_lo && t1->_hi == t2->_hi &&
t1->_ulo == t2->_ulo && t1->_uhi == t2->_uhi &&
t1->_bits._zeros == t2->_bits._zeros && t1->_bits._ones == t2->_bits._ones;
}
template <class CT>
static bool int_type_is_subset(const CT* super, const CT* sub) {
template <class CTP>
static bool int_type_is_subset(const CTP super, const CTP sub) {
using U = decltype(super->_ulo);
return super->_lo <= sub->_lo && super->_hi >= sub->_hi &&
super->_ulo <= sub->_ulo && super->_uhi >= sub->_uhi &&
// All bits that are known in super must also be known to be the same
// value in sub, &~ (and not) is the same as a set subtraction on bit
// sets
(super->_bits._zeros &~ sub->_bits._zeros) == 0 && (super->_bits._ones &~ sub->_bits._ones) == 0;
(super->_bits._zeros &~ sub->_bits._zeros) == U(0) && (super->_bits._ones &~ sub->_bits._ones) == U(0);
}
template <class CT>
@ -195,4 +195,199 @@ public:
#endif // PRODUCT
};
// A TypeIntMirror is structurally similar to a TypeInt or a TypeLong but it decouples the range
// inference from the Type infrastructure of the compiler. It also allows more flexibility with the
// bit width of the integer type. As a result, it is more efficient to use for intermediate steps
// of inference, as well as more flexible to perform testing on different integer types.
template <class S, class U>
class TypeIntMirror {
public:
S _lo;
S _hi;
U _ulo;
U _uhi;
KnownBits<U> _bits;
int _widen = 0; // dummy field to mimic the same field in TypeInt, useful in testing
static TypeIntMirror make(const TypeIntPrototype<S, U>& t, int widen) {
auto canonicalized_t = t.canonicalize_constraints();
assert(!canonicalized_t.empty(), "must not be empty");
return TypeIntMirror{canonicalized_t._data._srange._lo, canonicalized_t._data._srange._hi,
canonicalized_t._data._urange._lo, canonicalized_t._data._urange._hi,
canonicalized_t._data._bits};
}
// These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be
// passed into RangeInference methods. These are only used in testing, so they are implemented in
// the test file.
const TypeIntMirror* operator->() const;
TypeIntMirror meet(const TypeIntMirror& o) const;
bool contains(U u) const;
bool contains(const TypeIntMirror& o) const;
bool operator==(const TypeIntMirror& o) const;
template <class T>
TypeIntMirror cast() const;
};
// This class contains methods for inferring the Type of the result of several arithmetic
// operations from those of the corresponding inputs. For example, given a, b such that the Type of
// a is [0, 1] and the Type of b is [-1, 3], then the Type of the sum a + b is [-1, 4].
// The methods in this class receive one or more template parameters which are often TypeInt* or
// TypeLong*, or they can be TypeIntMirror which behave similar to TypeInt* and TypeLong* during
// testing. This allows us to verify the correctness of the implementation without coupling with
// the hotspot compiler allocation infrastructure.
class RangeInference {
private:
// If CTP is a pointer, get the underlying type. For the test helper classes, using the struct
// directly allows straightfoward equality comparison.
template <class CTP>
using CT = std::remove_const_t<std::conditional_t<std::is_pointer_v<CTP>, std::remove_pointer_t<CTP>, CTP>>;
// The type of CT::_lo, should be jint for TypeInt* and jlong for TypeLong*
template <class CTP>
using S = std::remove_const_t<decltype(CT<CTP>::_lo)>;
// The type of CT::_ulo, should be juint for TypeInt* and julong for TypeLong*
template <class CTP>
using U = std::remove_const_t<decltype(CT<CTP>::_ulo)>;
// A TypeInt consists of 1 or 2 simple intervals, each of which will lie either in the interval
// [0, max_signed] or [min_signed, -1]. It is more optimal to analyze each simple interval
// separately when doing inference. For example, consider a, b whose Types are both [-2, 2]. By
// analyzing the interval [-2, -1] and [0, 2] separately, we can easily see that the result of
// a & b must also be in the interval [-2, 2]. This is much harder if we want to work with the
// whole value range at the same time.
// This class offers a convenient way to traverse all the simple interval of a TypeInt.
template <class CTP>
class SimpleIntervalIterable {
private:
TypeIntMirror<S<CTP>, U<CTP>> _first_interval;
TypeIntMirror<S<CTP>, U<CTP>> _second_interval;
int _interval_num;
public:
SimpleIntervalIterable(CTP t) {
if (U<CTP>(t->_lo) <= U<CTP>(t->_hi)) {
_interval_num = 1;
_first_interval = TypeIntMirror<S<CTP>, U<CTP>>{t->_lo, t->_hi, t->_ulo, t->_uhi, t->_bits};
} else {
_interval_num = 2;
_first_interval = TypeIntMirror<S<CTP>, U<CTP>>::make(TypeIntPrototype<S<CTP>, U<CTP>>{{t->_lo, S<CTP>(t->_uhi)}, {U<CTP>(t->_lo), t->_uhi}, t->_bits}, 0);
_second_interval = TypeIntMirror<S<CTP>, U<CTP>>::make(TypeIntPrototype<S<CTP>, U<CTP>>{{S<CTP>(t->_ulo), t->_hi}, {t->_ulo, U<CTP>(t->_hi)}, t->_bits}, 0);
}
}
class Iterator {
private:
const SimpleIntervalIterable& _iterable;
int _current_interval;
Iterator(const SimpleIntervalIterable& iterable) : _iterable(iterable), _current_interval(0) {}
friend class SimpleIntervalIterable;
public:
const TypeIntMirror<S<CTP>, U<CTP>>& operator*() const {
assert(_current_interval < _iterable._interval_num, "out of bounds, %d - %d", _current_interval, _iterable._interval_num);
if (_current_interval == 0) {
return _iterable._first_interval;
} else {
return _iterable._second_interval;
}
}
Iterator& operator++() {
assert(_current_interval < _iterable._interval_num, "out of bounds, %d - %d", _current_interval, _iterable._interval_num);
_current_interval++;
return *this;
}
bool operator!=(const Iterator& o) const {
assert(&_iterable == &o._iterable, "not on the same iterable");
return _current_interval != o._current_interval;
}
};
Iterator begin() const {
return Iterator(*this);
}
Iterator end() const {
Iterator res(*this);
res._current_interval = _interval_num;
return res;
}
};
// Infer a result given the input types of a binary operation
template <class CTP, class Inference>
static CTP infer_binary(CTP t1, CTP t2, Inference infer) {
CTP res;
bool is_init = false;
SimpleIntervalIterable<CTP> t1_simple_intervals(t1);
SimpleIntervalIterable<CTP> t2_simple_intervals(t2);
for (auto& st1 : t1_simple_intervals) {
for (auto& st2 : t2_simple_intervals) {
CTP current = infer(st1, st2);
if (is_init) {
res = res->meet(current)->template cast<CT<CTP>>();
} else {
is_init = true;
res = current;
}
}
}
assert(is_init, "must be initialized");
return res;
}
public:
template <class CTP>
static CTP infer_and(CTP t1, CTP t2) {
return infer_binary(t1, t2, [&](const TypeIntMirror<S<CTP>, U<CTP>>& st1, const TypeIntMirror<S<CTP>, U<CTP>>& st2) {
S<CTP> lo = std::numeric_limits<S<CTP>>::min();
S<CTP> hi = std::numeric_limits<S<CTP>>::max();
U<CTP> ulo = std::numeric_limits<U<CTP>>::min();
// The unsigned value of the result of 'and' is always not greater than both of its inputs
// since there is no position at which the bit is 1 in the result and 0 in either input
U<CTP> uhi = MIN2(st1._uhi, st2._uhi);
U<CTP> zeros = st1._bits._zeros | st2._bits._zeros;
U<CTP> ones = st1._bits._ones & st2._bits._ones;
return CT<CTP>::make(TypeIntPrototype<S<CTP>, U<CTP>>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen));
});
}
template <class CTP>
static CTP infer_or(CTP t1, CTP t2) {
return infer_binary(t1, t2, [&](const TypeIntMirror<S<CTP>, U<CTP>>& st1, const TypeIntMirror<S<CTP>, U<CTP>>& st2) {
S<CTP> lo = std::numeric_limits<S<CTP>>::min();
S<CTP> hi = std::numeric_limits<S<CTP>>::max();
// The unsigned value of the result of 'or' is always not less than both of its inputs since
// there is no position at which the bit is 0 in the result and 1 in either input
U<CTP> ulo = MAX2(st1._ulo, st2._ulo);
U<CTP> uhi = std::numeric_limits<U<CTP>>::max();
U<CTP> zeros = st1._bits._zeros & st2._bits._zeros;
U<CTP> ones = st1._bits._ones | st2._bits._ones;
return CT<CTP>::make(TypeIntPrototype<S<CTP>, U<CTP>>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen));
});
}
template <class CTP>
static CTP infer_xor(CTP t1, CTP t2) {
return infer_binary(t1, t2, [&](const TypeIntMirror<S<CTP>, U<CTP>>& st1, const TypeIntMirror<S<CTP>, U<CTP>>& st2) {
S<CTP> lo = std::numeric_limits<S<CTP>>::min();
S<CTP> hi = std::numeric_limits<S<CTP>>::max();
U<CTP> ulo = std::numeric_limits<U<CTP>>::min();
U<CTP> uhi = std::numeric_limits<U<CTP>>::max();
U<CTP> zeros = (st1._bits._zeros & st2._bits._zeros) | (st1._bits._ones & st2._bits._ones);
U<CTP> ones = (st1._bits._zeros & st2._bits._ones) | (st1._bits._ones & st2._bits._zeros);
return CT<CTP>::make(TypeIntPrototype<S<CTP>, U<CTP>>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, MAX2(t1->_widen, t2->_widen));
});
}
};
#endif // SHARE_OPTO_RANGEINFERENCE_HPP

View File

@ -798,6 +798,7 @@ public:
// must always specify w
static const TypeInt* make(jint lo, jint hi, int widen);
static const Type* make_or_top(const TypeIntPrototype<jint, juint>& t, int widen);
static const TypeInt* make(const TypeIntPrototype<jint, juint>& t, int widen) { return make_or_top(t, widen)->is_int(); }
// Check for single integer
bool is_con() const { return _lo == _hi; }
@ -879,6 +880,7 @@ public:
// must always specify w
static const TypeLong* make(jlong lo, jlong hi, int widen);
static const Type* make_or_top(const TypeIntPrototype<jlong, julong>& t, int widen);
static const TypeLong* make(const TypeIntPrototype<jlong, julong>& t, int widen) { return make_or_top(t, widen)->is_long(); }
// Check for single integer
bool is_con() const { return _lo == _hi; }

View File

@ -1,47 +0,0 @@
#ifndef SHARE_OPTO_UTILITIES_XOR_HPP
#define SHARE_OPTO_UTILITIES_XOR_HPP
#include "utilities/powerOfTwo.hpp"
// Code separated into its own header to allow access from GTEST
// Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise
// xor of these values should also be non-negative. This method calculates an upper bound.
// S and U type parameters correspond to the signed and unsigned
// variants of an integer to operate on.
template<class S, class U>
static S xor_upper_bound_for_ranges(const S hi_0, const S hi_1) {
static_assert(S(-1) < S(0), "S must be signed");
static_assert(U(-1) > U(0), "U must be unsigned");
assert(hi_0 >= 0, "must be non-negative");
assert(hi_1 >= 0, "must be non-negative");
// x ^ y cannot have any bit set that is higher than both the highest bits set in x and y
// x cannot have any bit set that is higher than the highest bit set in hi_0
// y cannot have any bit set that is higher than the highest bit set in hi_1
// We want to find a value that has all 1 bits everywhere up to and including
// the highest bits set in hi_0 as well as hi_1. For this, we can take the next
// power of 2 strictly greater than both hi values and subtract 1 from it.
// Example 1:
// hi_0 = 5 (0b0101) hi_1=1 (0b0001)
// (5|1)+1 = 0b0110
// round_up_pow2 = 0b1000
// -1 = 0b0111 = max
// Example 2 - this demonstrates need for the +1:
// hi_0 = 4 (0b0100) hi_1=4 (0b0100)
// (4|4)+1 = 0b0101
// round_up_pow2 = 0b1000
// -1 = 0b0111 = max
// Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max
// Note: cast to unsigned happens before +1 to avoid signed overflow, and
// round_up is safe because high bit is unset (0 <= lo <= hi)
return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1;
}
#endif // SHARE_OPTO_UTILITIES_XOR_HPP

View File

@ -1060,6 +1060,29 @@ bool VPointer::can_make_speculative_aliasing_check_with(const VPointer& other) c
return false;
}
// The speculative check also needs to create the pointer expressions for both
// VPointers. We must check that we can do that, i.e. that all variables of the
// VPointers are available at the speculative check (and not just pre-loop invariant).
if (!this->can_make_pointer_expression_at_speculative_check()) {
#ifdef ASSERT
if (_vloop.is_trace_speculative_aliasing_analysis()) {
tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: not all variables of VPointer are avaialbe at speculative check!");
this->print_on(tty);
}
#endif
return false;
}
if (!other.can_make_pointer_expression_at_speculative_check()) {
#ifdef ASSERT
if (_vloop.is_trace_speculative_aliasing_analysis()) {
tty->print_cr("VPointer::can_make_speculative_aliasing_check_with: not all variables of VPointer are avaialbe at speculative check!");
other.print_on(tty);
}
#endif
return false;
}
return true;
}
@ -1147,6 +1170,8 @@ BoolNode* VPointer::make_speculative_aliasing_check_with(const VPointer& other,
Node* main_init = new ConvL2INode(main_initL);
phase->register_new_node_with_ctrl_of(main_init, pre_init);
assert(vp1.can_make_pointer_expression_at_speculative_check(), "variables must be available early enough to avoid cycles");
assert(vp2.can_make_pointer_expression_at_speculative_check(), "variables must be available early enough to avoid cycles");
Node* p1_init = vp1.make_pointer_expression(main_init, ctrl);
Node* p2_init = vp2.make_pointer_expression(main_init, ctrl);
Node* size1 = igvn.longcon(vp1.size());

View File

@ -1188,6 +1188,22 @@ private:
return true;
}
// We already know that all non-iv summands are pre loop invariant.
// See init_are_non_iv_summands_pre_loop_invariant
// That is good enough for alignment computations in the pre-loop limit. But it is not
// sufficient if we want to use the variables of the VPointer at the speculative check,
// which is further up before the pre-loop.
bool can_make_pointer_expression_at_speculative_check() const {
bool success = true;
mem_pointer().for_each_non_empty_summand([&] (const MemPointerSummand& s) {
Node* variable = s.variable();
if (variable != _vloop.iv() && !_vloop.is_available_for_speculative_check(variable)) {
success = false;
}
});
return success;
}
// In the pointer analysis, and especially the AlignVector analysis, we assume that
// stride and scale are not too large. For example, we multiply "iv_scale * iv_stride",
// and assume that this does not overflow the int range. We also take "abs(iv_scale)"

View File

@ -1211,22 +1211,11 @@ JVM_ENTRY(jboolean, JVM_IsHiddenClass(JNIEnv *env, jclass cls))
JVM_END
class ScopedValueBindingsResolver {
public:
InstanceKlass* Carrier_klass;
ScopedValueBindingsResolver(JavaThread* THREAD) {
Klass *k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_ScopedValue_Carrier(), true, THREAD);
Carrier_klass = InstanceKlass::cast(k);
}
};
JVM_ENTRY(jobject, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
ResourceMark rm(THREAD);
GrowableArray<Handle>* local_array = new GrowableArray<Handle>(12);
JvmtiVMObjectAllocEventCollector oam;
static ScopedValueBindingsResolver resolver(THREAD);
// Iterate through Java frames
vframeStream vfst(thread);
for(; !vfst.at_end(); vfst.next()) {
@ -1239,7 +1228,7 @@ JVM_ENTRY(jobject, JVM_FindScopedValueBindings(JNIEnv *env, jclass cls))
InstanceKlass* holder = method->method_holder();
if (name == vmSymbols::runWith_method_name()) {
if (holder == vmClasses::Thread_klass()
|| holder == resolver.Carrier_klass) {
|| holder == vmClasses::ScopedValue_Carrier_klass()) {
loc = 1;
}
}

View File

@ -1204,8 +1204,10 @@ void JvmtiTagMap::flush_object_free_events() {
assert_not_at_safepoint();
if (env()->is_enabled(JVMTI_EVENT_OBJECT_FREE)) {
{
// The other thread can block for safepoints during event callbacks, so ensure we
// are safepoint-safe while waiting.
ThreadBlockInVM tbivm(JavaThread::current());
MonitorLocker ml(lock(), Mutex::_no_safepoint_check_flag);
// If another thread is posting events, let it finish
while (_posting_events) {
ml.wait();
}

View File

@ -283,6 +283,8 @@ const char* Abstract_VM_Version::internal_vm_info_string() {
#define HOTSPOT_BUILD_COMPILER "MS VC++ 17.13 (VS2022)"
#elif _MSC_VER == 1944
#define HOTSPOT_BUILD_COMPILER "MS VC++ 17.14 (VS2022)"
#elif _MSC_VER == 1950
#define HOTSPOT_BUILD_COMPILER "MS VC++ 18.0 (VS2026)"
#else
#define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER)
#endif

View File

@ -79,6 +79,7 @@ public:
static_assert(min < max, "");
constexpr bool operator==(intn_t o) const { return (_v & _mask) == (o._v & _mask); }
constexpr bool operator!=(intn_t o) const { return !(*this == o); }
constexpr bool operator<(intn_t o) const { return int(*this) < int(o); }
constexpr bool operator>(intn_t o) const { return int(*this) > int(o); }
constexpr bool operator<=(intn_t o) const { return int(*this) <= int(o); }

View File

@ -26,6 +26,7 @@
package java.lang;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -103,6 +104,7 @@ public final class Byte extends Number implements Comparable<Byte>, Constable {
return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_byte, intValue()));
}
@AOTSafeClassInitializer
private static final class ByteCache {
private ByteCache() {}

View File

@ -26,6 +26,7 @@
package java.lang;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -9379,6 +9380,7 @@ class Character implements java.io.Serializable, Comparable<Character>, Constabl
this.value = value;
}
@AOTSafeClassInitializer
private static final class CharacterCache {
private CharacterCache(){}

View File

@ -28,6 +28,8 @@ package java.lang;
import jdk.internal.misc.CDS;
import jdk.internal.misc.VM;
import jdk.internal.util.DecimalDigits;
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.vm.annotation.Stable;
@ -891,15 +893,20 @@ public final class Integer extends Number
* with new Integer object(s) after initialization.
*/
@AOTSafeClassInitializer
private static final class IntegerCache {
static final int low = -128;
static final int high;
@Stable static int high;
@Stable
static final Integer[] cache;
@Stable static Integer[] cache;
static Integer[] archivedCache;
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
@ -915,34 +922,50 @@ public final class Integer extends Number
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
// If archive has Integer cache, we must use all instances from it.
// Otherwise, the identity checks between archived Integers and
// runtime-cached Integers would fail.
int archivedSize = (archivedCache == null) ? 0 : archivedCache.length;
for (int i = 0; i < archivedSize; i++) {
c[i] = archivedCache[i];
assert j == archivedCache[i];
j++;
}
// Fill the rest of the cache.
for (int i = archivedSize; i < size; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
Integer[] precomputed = null;
if (cache != null) {
// IntegerCache has been AOT-initialized.
precomputed = cache;
} else {
// Legacy CDS archive support (to be deprecated):
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
precomputed = archivedCache;
}
cache = archivedCache;
cache = loadOrInitializeCache(precomputed);
archivedCache = cache; // Legacy CDS archive support (to be deprecated)
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private static Integer[] loadOrInitializeCache(Integer[] precomputed) {
int size = (high - low) + 1;
// Use the precomputed cache if it exists and is large enough
if (precomputed != null && size <= precomputed.length) {
return precomputed;
}
Integer[] c = new Integer[size];
int j = low;
// If we loading a precomputed cache (from AOT cache or CDS archive),
// we must use all instances from it.
// Otherwise, the Integers from the AOT cache (or CDS archive) will not
// have the same object identity as items in IntegerCache.cache[].
int precomputedSize = (precomputed == null) ? 0 : precomputed.length;
for (int i = 0; i < precomputedSize; i++) {
c[i] = precomputed[i];
assert j == precomputed[i];
j++;
}
// Fill the rest of the cache.
for (int i = precomputedSize; i < size; i++) {
c[i] = new Integer(j++);
}
return c;
}
private IntegerCache() {}
}

View File

@ -35,6 +35,7 @@ import java.util.Optional;
import jdk.internal.misc.CDS;
import jdk.internal.util.DecimalDigits;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -911,6 +912,7 @@ public final class Long extends Number
return Long.valueOf(parseLong(s, 10));
}
@AOTSafeClassInitializer
private static final class LongCache {
private LongCache() {}

View File

@ -69,6 +69,7 @@ import jdk.internal.module.ServicesCatalog;
import jdk.internal.module.Resources;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
/**
@ -391,6 +392,7 @@ public final class Module implements AnnotatedElement {
private static final Module EVERYONE_MODULE;
private static final Set<Module> EVERYONE_SET;
@AOTSafeClassInitializer
private static class ArchivedData {
private static ArchivedData archivedData;
private final Module allUnnamedModule;

View File

@ -53,6 +53,7 @@ import jdk.internal.module.ServicesCatalog;
import jdk.internal.misc.CDS;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
/**
@ -145,6 +146,7 @@ import jdk.internal.vm.annotation.Stable;
* @see Module#getLayer()
*/
@AOTSafeClassInitializer
public final class ModuleLayer {
// the empty layer (may be initialized from the CDS archive)

View File

@ -26,6 +26,7 @@
package java.lang;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import jdk.internal.vm.annotation.Stable;
@ -230,6 +231,7 @@ public final class Short extends Number implements Comparable<Short>, Constable
return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_short, intValue()));
}
@AOTSafeClassInitializer
private static final class ShortCache {
private ShortCache() {}

View File

@ -44,6 +44,7 @@ import java.util.stream.Stream;
import jdk.internal.misc.CDS;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.internal.module.ModuleTarget;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
/**
@ -155,6 +156,7 @@ import jdk.internal.vm.annotation.Stable;
* @since 9
* @see java.lang.ModuleLayer
*/
@AOTSafeClassInitializer
public final class Configuration {
// @see Configuration#empty()

View File

@ -42,6 +42,9 @@ import java.util.function.UnaryOperator;
import jdk.internal.access.JavaUtilCollectionAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
/**
@ -52,6 +55,7 @@ import jdk.internal.vm.annotation.Stable;
* classes use a serial proxy and thus have no need to declare serialVersionUID.
*/
@SuppressWarnings("serial")
@AOTSafeClassInitializer
class ImmutableCollections {
/**
* A "salt" value used for randomizing iteration order. This is initialized once
@ -59,14 +63,20 @@ class ImmutableCollections {
* it needs to vary sufficiently from one run to the next so that iteration order
* will vary between JVM runs.
*/
private static final long SALT32L;
@Stable private static long SALT32L;
/**
* For set and map iteration, we will iterate in "reverse" stochastically,
* decided at bootstrap time.
*/
private static final boolean REVERSE;
@Stable private static boolean REVERSE;
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
// to generate a reasonably random and well-mixed SALT, use an arbitrary
// value (a slice of pi), multiply with a random seed, then pick
// the mid 32-bits from the product. By picking a SALT value in the
@ -102,6 +112,7 @@ class ImmutableCollections {
static final MapN<?,?> EMPTY_MAP;
static {
// Legacy CDS archive support (to be deprecated)
CDS.initializeFromArchive(ImmutableCollections.class);
if (archivedObjects == null) {
EMPTY = new Object();

View File

@ -36,6 +36,7 @@ import java.util.Objects;
import java.util.Set;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import sun.nio.cs.UTF_8;
@ -60,6 +61,7 @@ import sun.util.logging.PlatformLogger;
* @see Manifest
* @since 1.2
*/
@AOTSafeClassInitializer
public class Attributes implements Map<Object,Object>, Cloneable {
/**
* The attribute name-value mappings.
@ -450,6 +452,7 @@ public class Attributes implements Map<Object,Object>, Cloneable {
*
* @spec jar/jar.html JAR File Specification
*/
@AOTSafeClassInitializer
public static class Name {
private final String name;
private final int hashCode;
@ -669,6 +672,7 @@ public class Attributes implements Map<Object,Object>, Cloneable {
static {
// Legacy CDS archive support (to be deprecated)
CDS.initializeFromArchive(Attributes.Name.class);
if (KNOWN_NAMES == null) {

View File

@ -27,11 +27,13 @@ package jdk.internal.loader;
import java.util.Map;
import jdk.internal.misc.CDS;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* Used to archive the built-in class loaders, their services catalogs, and the
* package-to-module map used by the built-in class loaders.
*/
@AOTSafeClassInitializer
class ArchivedClassLoaders {
private static ArchivedClassLoaders archivedClassLoaders;

View File

@ -26,6 +26,7 @@
package jdk.internal.math;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import java.util.Arrays;
@ -33,6 +34,7 @@ import java.util.Arrays;
/**
* A simple big integer class specifically for floating point base conversion.
*/
@AOTSafeClassInitializer
final class FDBigInteger {
@Stable
@ -53,6 +55,7 @@ final class FDBigInteger {
// Initialize FDBigInteger cache of powers of 5.
static {
// Legacy CDS archive support (to be deprecated)
CDS.initializeFromArchive(FDBigInteger.class);
Object[] caches = archivedCaches;
if (caches == null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 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
@ -25,10 +25,12 @@
package jdk.internal.module;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* Used by ModuleBootstrap for archiving the boot layer.
*/
@AOTSafeClassInitializer
class ArchivedBootLayer {
private static ArchivedBootLayer archivedBootLayer;

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
@ -30,11 +30,13 @@ import java.util.function.Function;
import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import jdk.internal.misc.CDS;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* Used by ModuleBootstrap for archiving the configuration for the boot layer,
* and the system module finder.
*/
@AOTSafeClassInitializer
class ArchivedModuleGraph {
private static ArchivedModuleGraph archivedModuleGraph;
@ -126,6 +128,7 @@ class ArchivedModuleGraph {
}
static {
// Legacy CDS archive support (to be deprecated)
CDS.initializeFromArchive(ArchivedModuleGraph.class);
}
}

View File

@ -34,11 +34,14 @@ package sun.util.locale;
import jdk.internal.misc.CDS;
import jdk.internal.util.ReferencedKeySet;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import jdk.internal.vm.annotation.Stable;
import java.util.StringJoiner;
import java.util.function.Supplier;
@AOTSafeClassInitializer
public final class BaseLocale {
public static @Stable BaseLocale[] constantBaseLocales;
@ -63,6 +66,7 @@ public final class BaseLocale {
CANADA_FRENCH = 18,
NUM_CONSTANTS = 19;
static {
// Legacy CDS archive support (to be deprecated)
CDS.initializeFromArchive(BaseLocale.class);
BaseLocale[] baseLocales = constantBaseLocales;
if (baseLocales == null) {
@ -91,13 +95,21 @@ public final class BaseLocale {
}
// Interned BaseLocale cache
private static final LazyConstant<ReferencedKeySet<BaseLocale>> CACHE =
@Stable private static LazyConstant<ReferencedKeySet<BaseLocale>> CACHE;
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
CACHE =
LazyConstant.of(new Supplier<>() {
@Override
public ReferencedKeySet<BaseLocale> get() {
return ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier());
}
});
}
public static final String SEP = "_";

View File

@ -344,10 +344,6 @@ final class ConnectionTerminatorImpl implements ConnectionTerminator {
}
}
failHandshakeCFs();
// remap the connection to a draining connection
final QuicEndpoint endpoint = this.connection.endpoint();
assert endpoint != null : "QUIC endpoint is null";
endpoint.draining(connection);
discardConnectionState();
connection.streams.terminate(terminationCause);
if (Log.quic()) {
@ -439,7 +435,7 @@ final class ConnectionTerminatorImpl implements ConnectionTerminator {
final ProtectionRecord protectionRecord = ProtectionRecord.single(packet,
connection::allocateDatagramForEncryption);
// while sending the packet containing the CONNECTION_CLOSE frame, the pushDatagram will
// remap (or remove) the QuicConnectionImpl in QuicEndpoint.
// remap the QuicConnectionImpl in QuicEndpoint.
connection.pushDatagram(protectionRecord);
}

View File

@ -2811,7 +2811,7 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece
// a CONNECTION_CLOSE frame is being sent to the peer when the local
// connection state is in DRAINING. This implies that the local endpoint
// is responding to an incoming CONNECTION_CLOSE frame from the peer.
// we remove the connection from the endpoint for such cases.
// we switch this connection to one that does not respond to incoming packets.
endpoint.pushClosedDatagram(this, peerAddress(), datagram);
} else if (stateHandle.isMarked(QuicConnectionState.CLOSING)) {
// a CONNECTION_CLOSE frame is being sent to the peer when the local

View File

@ -1509,7 +1509,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable
/**
* Called to schedule sending of a datagram that contains a single {@code ConnectionCloseFrame}
* sent in response to a {@code ConnectionClose} frame.
* This will completely remove the connection from the connection map.
* This will replace the {@link QuicConnectionImpl} with a {@link DrainingConnection} that
* will discard all incoming packets.
* @param connection the connection being closed
* @param destination the peer address
* @param datagram the datagram
@ -1518,7 +1519,7 @@ public abstract sealed class QuicEndpoint implements AutoCloseable
InetSocketAddress destination,
ByteBuffer datagram) {
if (debug.on()) debug.log("Pushing closed datagram for " + connection.logTag());
removeConnection(connection);
draining(connection);
pushDatagram(connection, destination, datagram);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -86,18 +86,22 @@ class BsdCDebugger implements CDebugger {
String cpu = dbg.getCPU();
if (cpu.equals("amd64") || cpu.equals("x86_64")) {
AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext();
Address rsp = context.getRegisterAsAddress(AMD64ThreadContext.RSP);
if (rsp == null) return null;
Address rbp = context.getRegisterAsAddress(AMD64ThreadContext.RBP);
if (rbp == null) return null;
Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP);
if (pc == null) return null;
return new BsdAMD64CFrame(dbg, rbp, pc);
return new BsdAMD64CFrame(dbg, rsp, rbp, pc);
} else if (cpu.equals("aarch64")) {
AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext();
Address sp = context.getRegisterAsAddress(AARCH64ThreadContext.SP);
if (sp == null) return null;
Address fp = context.getRegisterAsAddress(AARCH64ThreadContext.FP);
if (fp == null) return null;
Address pc = context.getRegisterAsAddress(AARCH64ThreadContext.PC);
if (pc == null) return null;
return new BsdAARCH64CFrame(dbg, fp, pc);
return new BsdAARCH64CFrame(dbg, sp, fp, pc);
} else {
throw new DebuggerException(cpu + " is not yet supported");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, Red Hat Inc.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@ -26,15 +26,19 @@
package sun.jvm.hotspot.debugger.bsd.aarch64;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.aarch64.*;
import sun.jvm.hotspot.debugger.bsd.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.debugger.cdbg.basic.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.runtime.aarch64.*;
public final class BsdAARCH64CFrame extends BasicCFrame {
public BsdAARCH64CFrame(BsdDebugger dbg, Address fp, Address pc) {
public BsdAARCH64CFrame(BsdDebugger dbg, Address sp, Address fp, Address pc) {
super(dbg.getCDebugger());
this.sp = sp;
this.fp = fp;
this.pc = pc;
this.dbg = dbg;
@ -54,28 +58,65 @@ public final class BsdAARCH64CFrame extends BasicCFrame {
return fp;
}
@Override
public CFrame sender(ThreadProxy thread) {
AARCH64ThreadContext context = (AARCH64ThreadContext) thread.getContext();
Address rsp = context.getRegisterAsAddress(AARCH64ThreadContext.SP);
return sender(thread, null, null, null);
}
if ((fp == null) || fp.lessThan(rsp)) {
@Override
public CFrame sender(ThreadProxy thread, Address nextSP, Address nextFP, Address nextPC) {
// Check fp
// Skip if both nextFP and nextPC are given - do not need to load from fp.
if (nextFP == null && nextPC == null) {
if (fp == null) {
return null;
}
// Check alignment of fp
if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) {
return null;
}
}
if (nextFP == null) {
nextFP = fp.getAddressAt(0);
}
if (nextFP == null) {
return null;
}
// Check alignment of fp
if (dbg.getAddressValue(fp) % (2 * ADDRESS_SIZE) != 0) {
return null;
if (nextPC == null) {
nextPC = fp.getAddressAt(ADDRESS_SIZE);
}
Address nextFP = fp.getAddressAt(0 * ADDRESS_SIZE);
if (nextFP == null || nextFP.lessThanOrEqual(fp)) {
return null;
}
Address nextPC = fp.getAddressAt(1 * ADDRESS_SIZE);
if (nextPC == null) {
return null;
}
return new BsdAARCH64CFrame(dbg, nextFP, nextPC);
if (nextSP == null) {
CodeCache cc = VM.getVM().getCodeCache();
CodeBlob currentBlob = cc.findBlobUnsafe(pc());
// This case is different from HotSpot. See JDK-8371194 for details.
if (currentBlob != null && (currentBlob.isContinuationStub() || currentBlob.isNativeMethod())) {
// Use FP since it should always be valid for these cases.
// TODO: These should be walked as Frames not CFrames.
nextSP = fp.addOffsetTo(2 * ADDRESS_SIZE);
} else {
CodeBlob codeBlob = cc.findBlobUnsafe(nextPC);
boolean useCodeBlob = codeBlob != null && codeBlob.getFrameSize() > 0;
nextSP = useCodeBlob ? nextFP.addOffsetTo((2 * ADDRESS_SIZE) - codeBlob.getFrameSize()) : nextFP;
}
}
if (nextSP == null) {
return null;
}
return new BsdAARCH64CFrame(dbg, nextSP, nextFP, nextPC);
}
@Override
public Frame toFrame() {
return new AARCH64Frame(sp, fp, pc);
}
// package/class internals only

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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,10 +29,13 @@ import sun.jvm.hotspot.debugger.amd64.*;
import sun.jvm.hotspot.debugger.bsd.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.debugger.cdbg.basic.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.runtime.amd64.*;
public final class BsdAMD64CFrame extends BasicCFrame {
public BsdAMD64CFrame(BsdDebugger dbg, Address rbp, Address rip) {
public BsdAMD64CFrame(BsdDebugger dbg, Address rsp, Address rbp, Address rip) {
super(dbg.getCDebugger());
this.rsp = rsp;
this.rbp = rbp;
this.rip = rip;
this.dbg = dbg;
@ -52,32 +55,49 @@ public final class BsdAMD64CFrame extends BasicCFrame {
return rbp;
}
@Override
public CFrame sender(ThreadProxy thread) {
AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext();
Address rsp = context.getRegisterAsAddress(AMD64ThreadContext.RSP);
return sender(thread, null, null, null);
}
if ( (rbp == null) || rbp.lessThan(rsp) ) {
return null;
@Override
public CFrame sender(ThreadProxy thread, Address sp, Address fp, Address pc) {
// Check fp
// Skip if both fp and pc are given - do not need to load from rbp.
if (fp == null && pc == null) {
if (rbp == null) {
return null;
}
// Check alignment of rbp
if (dbg.getAddressValue(rbp) % ADDRESS_SIZE != 0) {
return null;
}
}
// Check alignment of rbp
if (dbg.getAddressValue(rbp) % ADDRESS_SIZE != 0) {
Address nextRSP = sp != null ? sp : rbp.addOffsetTo(2 * ADDRESS_SIZE);
if (nextRSP == null) {
return null;
}
Address nextRBP = rbp.getAddressAt( 0 * ADDRESS_SIZE);
if (nextRBP == null || nextRBP.lessThanOrEqual(rbp)) {
Address nextRBP = fp != null ? fp : rbp.getAddressAt(0);
if (nextRBP == null) {
return null;
}
Address nextPC = rbp.getAddressAt( 1 * ADDRESS_SIZE);
Address nextPC = pc != null ? pc : rbp.getAddressAt(ADDRESS_SIZE);
if (nextPC == null) {
return null;
}
return new BsdAMD64CFrame(dbg, nextRBP, nextPC);
return new BsdAMD64CFrame(dbg, nextRSP, nextRBP, nextPC);
}
@Override
public Frame toFrame() {
return new AMD64Frame(rsp, rbp, rip);
}
// package/class internals only
private static final int ADDRESS_SIZE = 8;
private Address rsp;
private Address rip;
private Address rbp;
private BsdDebugger dbg;

View File

@ -260,6 +260,9 @@ public final class Float16
* Float#toString(float)} in the handling of special values
* (signed zeros, infinities, and NaN) and the generation of a
* decimal string that will convert back to the argument value.
* However, the range for plain notation is defined to be the interval
* [10<sup>-3</sup>, 10<sup>3</sup>) rather than the interval used
* for {@code float} and {@code double}.
*
* @param f16 the {@code Float16} to be converted.
* @return a string representation of the argument.
@ -2106,7 +2109,7 @@ public final class Float16
int h = (int) (f * 107_375L >>> 30);
int l = f - 10_000 * h;
if (0 < e && e <= 7) {
if (0 < e && e <= 3) {
return toChars1(h, l, e);
}
if (-3 < e && e <= 0) {

View File

@ -41,5 +41,3 @@
#############################################################################
jdk/javadoc/doccheck/checks/jdkCheckLinks.java 8370249 generic-all
jdk/javadoc/doccheck/checks/jdkCheckHtml.java 8370970 generic-all
jdk/javadoc/doccheck/checks/jdkDoctypeBadcharsCheck.java 8370970 generic-all

View File

@ -25,15 +25,16 @@
#include "opto/rangeinference.hpp"
#include "opto/type.hpp"
#include "runtime/os.hpp"
#include "utilities/intn_t.hpp"
#include "unittest.hpp"
#include "utilities/intn_t.hpp"
#include "utilities/rbTree.hpp"
#include <array>
#include <limits>
#include <type_traits>
template <class U>
static U uniform_random();
template <>
juint uniform_random<juint>() {
return os::random();
static U uniform_random() {
return U(juint(os::random()));
}
template <>
@ -201,7 +202,7 @@ static void test_canonicalize_constraints_random() {
}
}
TEST_VM(opto, canonicalize_constraints) {
TEST(opto, canonicalize_constraints) {
test_canonicalize_constraints_trivial();
test_canonicalize_constraints_exhaustive<intn_t<1>, uintn_t<1>>();
test_canonicalize_constraints_exhaustive<intn_t<2>, uintn_t<2>>();
@ -212,3 +213,413 @@ TEST_VM(opto, canonicalize_constraints) {
test_canonicalize_constraints_random<jint, juint>();
test_canonicalize_constraints_random<jlong, julong>();
}
// Implementations of TypeIntMirror methods for testing purposes
template <class S, class U>
const TypeIntMirror<S, U>* TypeIntMirror<S, U>::operator->() const {
return this;
}
template <class S, class U>
TypeIntMirror<S, U> TypeIntMirror<S, U>::meet(const TypeIntMirror& o) const {
return TypeIntHelper::int_type_union(*this, o);
}
template <class S, class U>
bool TypeIntMirror<S, U>::contains(U u) const {
S s = S(u);
return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u);
}
template <class S, class U>
bool TypeIntMirror<S, U>::contains(const TypeIntMirror& o) const {
return TypeIntHelper::int_type_is_subset(*this, o);
}
template <class S, class U>
bool TypeIntMirror<S, U>::operator==(const TypeIntMirror& o) const {
return TypeIntHelper::int_type_is_equal(*this, o);
}
template <class S, class U>
template <class T>
TypeIntMirror<S, U> TypeIntMirror<S, U>::cast() const {
static_assert(std::is_same_v<T, TypeIntMirror>);
return *this;
}
// The number of TypeIntMirror instances for integral types with a few bits. These values are
// calculated once and written down for usage in constexpr contexts.
template <class CTP>
static constexpr size_t all_instances_size() {
using U = decltype(CTP::_ulo);
constexpr juint max_unsigned = juint(std::numeric_limits<U>::max());
if constexpr (max_unsigned == 1U) {
// 1 bit
return 3;
} else if constexpr (max_unsigned == 3U) {
// 2 bits
return 15;
} else if constexpr (max_unsigned == 7U) {
// 3 bits
return 134;
} else {
// 4 bits
static_assert(max_unsigned == 15U);
// For more than 4 bits, the number of instances is too large and it is not realistic to
// compute all of them.
return 1732;
}
}
template <class CTP>
static std::array<CTP, all_instances_size<CTP>()> compute_all_instances() {
using S = decltype(CTP::_lo);
using U = decltype(CTP::_ulo);
class CTPComparator {
public:
static RBTreeOrdering cmp(const CTP& x, const RBNode<CTP, int>* node) {
// Quick helper for the tediousness below
auto f = [](auto x, auto y) {
assert(x != y, "we only handle lt and gt cases");
return x < y ? RBTreeOrdering::LT : RBTreeOrdering::GT;
};
const CTP& y = node->key();
if (x._lo != y._lo) {
return f(x._lo, y._lo);
} else if (x._hi != y._hi) {
return f(x._hi, y._hi);
} else if (x._ulo != y._ulo) {
return f(x._ulo, y._ulo);
} else if (x._uhi != y._uhi) {
return f(x._uhi, y._uhi);
} else if (x._bits._zeros != y._bits._zeros) {
return f(x._bits._zeros, y._bits._zeros);
} else if (x._bits._ones != y._bits._ones) {
return f(x._bits._ones, y._bits._ones);
} else {
return RBTreeOrdering::EQ;
}
}
};
RBTreeCHeap<CTP, int, CTPComparator, MemTag::mtCompiler> collector;
for (jint lo = jint(std::numeric_limits<S>::min()); lo <= jint(std::numeric_limits<S>::max()); lo++) {
for (jint hi = lo; hi <= jint(std::numeric_limits<S>::max()); hi++) {
for (juint ulo = 0; ulo <= juint(std::numeric_limits<U>::max()); ulo++) {
for (juint uhi = ulo; uhi <= juint(std::numeric_limits<U>::max()); uhi++) {
for (juint zeros = 0; zeros <= juint(std::numeric_limits<U>::max()); zeros++) {
for (juint ones = 0; ones <= juint(std::numeric_limits<U>::max()); ones++) {
TypeIntPrototype<S, U> t{{S(lo), S(hi)}, {U(ulo), U(uhi)}, {U(zeros), U(ones)}};
auto canonicalized_t = t.canonicalize_constraints();
if (canonicalized_t.empty()) {
continue;
}
TypeIntPrototype<S, U> ct = canonicalized_t._data;
collector.upsert(CTP{ct._srange._lo, ct._srange._hi, ct._urange._lo, ct._urange._hi, ct._bits}, 0);
}
}
}
}
}
}
assert(collector.size() == all_instances_size<CTP>(), "unexpected size of all_instance, expected %d, actual %d", jint(all_instances_size<CTP>()), jint(collector.size()));
std::array<CTP, all_instances_size<CTP>()> res;
size_t idx = 0;
collector.visit_in_order([&](RBNode<CTP, int>* node) {
res[idx] = node->key();
idx++;
return true;
});
return res;
}
template <class CTP>
static const std::array<CTP, all_instances_size<CTP>()>& all_instances() {
static std::array<CTP, all_instances_size<CTP>()> res = compute_all_instances<CTP>();
static_assert(std::is_trivially_destructible_v<decltype(res)>);
return res;
}
// Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then
// op(v1, v2) must be an element of infer(input1, input2). This version does the check exhaustively
// on all elements of input1 and input2.
template <class InputType, class Operation, class Inference>
static void test_binary_instance_correctness_exhaustive(Operation op, Inference infer, const InputType& input1, const InputType& input2) {
using S = std::remove_const_t<decltype(input1->_lo)>;
using U = std::remove_const_t<decltype(input1->_ulo)>;
InputType result = infer(input1, input2);
for (juint v1 = juint(std::numeric_limits<U>::min()); v1 <= juint(std::numeric_limits<U>::max()); v1++) {
if (!input1.contains(U(v1))) {
continue;
}
for (juint v2 = juint(std::numeric_limits<U>::min()); v2 <= juint(std::numeric_limits<U>::max()); v2++) {
if (!input2.contains(U(v2))) {
continue;
}
U r = op(U(v1), U(v2));
ASSERT_TRUE(result.contains(r));
}
}
}
// Check the correctness, that is, if v1 is an element of input1, v2 is an element of input2, then
// op(v1, v2) must be an element of infer(input1, input2). This version does the check randomly on
// a number of elements in input1 and input2.
template <class InputType, class Operation, class Inference>
static void test_binary_instance_correctness_samples(Operation op, Inference infer, const InputType& input1, const InputType& input2) {
using U = std::remove_const_t<decltype(input1->_ulo)>;
auto result = infer(input1, input2);
constexpr size_t sample_count = 6;
U input1_samples[sample_count] {U(input1._lo), U(input1._hi), input1._ulo, input1._uhi, input1._ulo, input1._ulo};
U input2_samples[sample_count] {U(input2._lo), U(input2._hi), input2._ulo, input2._uhi, input2._ulo, input2._ulo};
auto random_sample = [](U* samples, const InputType& input) {
constexpr size_t max_tries = 100;
constexpr size_t start_random_idx = 4;
for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) {
U n = uniform_random<U>();
if (input.contains(n)) {
samples[idx] = n;
idx++;
}
}
};
random_sample(input1_samples, input1);
random_sample(input2_samples, input2);
for (size_t i = 0; i < sample_count; i++) {
for (size_t j = 0; j < sample_count; j++) {
U r = op(input1_samples[i], input2_samples[j]);
ASSERT_TRUE(result.contains(r));
}
}
}
// Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2,
// then infer(input1, input2) must be a subset of infer(super1, super2). This version does the
// check exhaustively on all supersets of input1 and input2.
template <class InputType, class Inference>
static void test_binary_instance_monotonicity_exhaustive(Inference infer, const InputType& input1, const InputType& input2) {
InputType result = infer(input1, input2);
for (const InputType& super1 : all_instances<InputType>()) {
if (!super1.contains(input1) || super1 == input1) {
continue;
}
for (const InputType& super2 : all_instances<InputType>()) {
if (!super2.contains(input2) || super2 == input2) {
continue;
}
ASSERT_TRUE(infer(input1, super2).contains(result));
ASSERT_TRUE(infer(super1, input2).contains(result));
ASSERT_TRUE(infer(super1, super2).contains(result));
}
}
}
// Check the monotonicity, that is, if input1 is a subset of super1, input2 is a subset of super2,
// then infer(input1, input2) must be a subset of infer(super1, super2). This version does the
// check randomly on a number of supersets of input1 and input2.
template <class InputType, class Inference>
static void test_binary_instance_monotonicity_samples(Inference infer, const InputType& input1, const InputType& input2) {
using S = std::remove_const_t<decltype(input1->_lo)>;
using U = std::remove_const_t<decltype(input1->_ulo)>;
auto result = infer(input1, input2);
// The set that is a superset of all other sets
InputType universe = InputType{std::numeric_limits<S>::min(), std::numeric_limits<S>::max(), U(0), U(-1), {U(0), U(0)}};
ASSERT_TRUE(infer(universe, input2).contains(result));
ASSERT_TRUE(infer(input1, universe).contains(result));
ASSERT_TRUE(infer(universe, universe).contains(result));
auto random_superset = [](const InputType& input) {
S lo = MIN2(input->_lo, S(uniform_random<U>()));
S hi = MAX2(input->_hi, S(uniform_random<U>()));
U ulo = MIN2(input->_ulo, uniform_random<U>());
U uhi = MAX2(input->_uhi, uniform_random<U>());
U zeros = input->_bits._zeros & uniform_random<U>();
U ones = input->_bits._ones & uniform_random<U>();
InputType super = InputType::make(TypeIntPrototype<S, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}, 0);
assert(super.contains(input), "impossible");
return super;
};
InputType super1 = random_superset(input1);
InputType super2 = random_superset(input2);
ASSERT_TRUE(infer(super1, input2).contains(result));
ASSERT_TRUE(infer(input1, super2).contains(result));
ASSERT_TRUE(infer(super1, super2).contains(result));
}
// Verify the correctness and monotonicity of an inference function by exhautively analyzing all
// instances of InputType
template <class InputType, class Operation, class Inference>
static void test_binary_exhaustive(Operation op, Inference infer) {
for (const InputType& input1 : all_instances<InputType>()) {
for (const InputType& input2 : all_instances<InputType>()) {
test_binary_instance_correctness_exhaustive(op, infer, input1, input2);
if (all_instances<InputType>().size() < 100) {
// This effectively covers the cases up to uintn_t<2>
test_binary_instance_monotonicity_exhaustive(infer, input1, input2);
} else {
// This effectively covers the cases of uintn_t<3>
test_binary_instance_monotonicity_samples(infer, input1, input2);
}
}
}
}
// Verify the correctness and monotonicity of an inference function by randomly sampling instances
// of InputType
template <class InputType, class Operation, class Inference>
static void test_binary_random(Operation op, Inference infer) {
using S = std::remove_const_t<decltype(InputType::_lo)>;
using U = std::remove_const_t<decltype(InputType::_ulo)>;
constexpr size_t sample_count = 100;
InputType samples[sample_count];
// Fill with {0}
for (size_t i = 0; i < sample_count; i++) {
samples[i] = InputType::make(TypeIntPrototype<S, U>{{S(0), S(0)}, {U(0), U(0)}, {U(0), U(0)}}, 0);
}
// {1}
samples[1] = InputType::make(TypeIntPrototype<S, U>{{S(1), S(1)}, {U(1), U(1)}, {U(0), U(0)}}, 0);
// {-1}
samples[2] = InputType::make(TypeIntPrototype<S, U>{{S(-1), S(-1)}, {U(-1), U(-1)}, {U(0), U(0)}}, 0);
// {0, 1}
samples[3] = InputType::make(TypeIntPrototype<S, U>{{S(0), S(1)}, {U(0), U(1)}, {U(0), U(0)}}, 0);
// {-1, 0, 1}
samples[4] = InputType::make(TypeIntPrototype<S, U>{{S(-1), S(1)}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
// {-1, 1}
samples[5] = InputType::make(TypeIntPrototype<S, U>{{S(-1), S(1)}, {U(1), U(-1)}, {U(0), U(0)}}, 0);
// {0, 1, 2}
samples[6] = InputType::make(TypeIntPrototype<S, U>{{S(0), S(2)}, {U(0), U(2)}, {U(0), U(0)}}, 0);
// {0, 2}
samples[7] = InputType::make(TypeIntPrototype<S, U>{{S(0), S(2)}, {U(0), U(2)}, {U(1), U(0)}}, 0);
// [min_signed, max_signed]
samples[8] = InputType::make(TypeIntPrototype<S, U>{{std::numeric_limits<S>::min(), std::numeric_limits<S>::max()}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
// [0, max_signed]
samples[9] = InputType::make(TypeIntPrototype<S, U>{{S(0), std::numeric_limits<S>::max()}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
// [min_signed, 0)
samples[10] = InputType::make(TypeIntPrototype<S, U>{{std::numeric_limits<S>::min(), S(-1)}, {U(0), U(-1)}, {U(0), U(0)}}, 0);
constexpr size_t max_tries = 1000;
constexpr size_t start_random_idx = 11;
for (size_t tries = 0, idx = start_random_idx; tries < max_tries && idx < sample_count; tries++) {
// Try to have lo < hi
S signed_bound1 = S(uniform_random<U>());
S signed_bound2 = S(uniform_random<U>());
S lo = MIN2(signed_bound1, signed_bound2);
S hi = MAX2(signed_bound1, signed_bound2);
// Try to have ulo < uhi
U unsigned_bound1 = uniform_random<U>();
U unsigned_bound2 = uniform_random<U>();
U ulo = MIN2(unsigned_bound1, unsigned_bound2);
U uhi = MAX2(unsigned_bound1, unsigned_bound2);
// Try to have (zeros & ones) == 0
U zeros = uniform_random<U>();
U ones = uniform_random<U>();
U common = zeros & ones;
zeros = zeros ^ common;
ones = ones ^ common;
TypeIntPrototype<S, U> t{{lo, hi}, {ulo, uhi}, {zeros, ones}};
auto canonicalized_t = t.canonicalize_constraints();
if (canonicalized_t.empty()) {
continue;
}
samples[idx] = TypeIntMirror<S, U>{canonicalized_t._data._srange._lo, canonicalized_t._data._srange._hi,
canonicalized_t._data._urange._lo, canonicalized_t._data._urange._hi,
canonicalized_t._data._bits};
idx++;
}
for (size_t i = 0; i < sample_count; i++) {
for (size_t j = 0; j < sample_count; j++) {
test_binary_instance_correctness_samples(op, infer, samples[i], samples[j]);
test_binary_instance_monotonicity_samples(infer, samples[i], samples[j]);
}
}
}
template <template <class U> class Operation, template <class CTP> class Inference>
static void test_binary() {
test_binary_exhaustive<TypeIntMirror<intn_t<1>, uintn_t<1>>>(Operation<uintn_t<1>>(), Inference<TypeIntMirror<intn_t<1>, uintn_t<1>>>());
test_binary_exhaustive<TypeIntMirror<intn_t<2>, uintn_t<2>>>(Operation<uintn_t<2>>(), Inference<TypeIntMirror<intn_t<2>, uintn_t<2>>>());
test_binary_exhaustive<TypeIntMirror<intn_t<3>, uintn_t<3>>>(Operation<uintn_t<3>>(), Inference<TypeIntMirror<intn_t<3>, uintn_t<3>>>());
test_binary_random<TypeIntMirror<intn_t<4>, uintn_t<4>>>(Operation<uintn_t<4>>(), Inference<TypeIntMirror<intn_t<4>, uintn_t<4>>>());
test_binary_random<TypeIntMirror<intn_t<5>, uintn_t<5>>>(Operation<uintn_t<5>>(), Inference<TypeIntMirror<intn_t<5>, uintn_t<5>>>());
test_binary_random<TypeIntMirror<intn_t<6>, uintn_t<6>>>(Operation<uintn_t<6>>(), Inference<TypeIntMirror<intn_t<6>, uintn_t<6>>>());
test_binary_random<TypeIntMirror<jint, juint>>(Operation<juint>(), Inference<TypeIntMirror<jint, juint>>());
test_binary_random<TypeIntMirror<jlong, julong>>(Operation<julong>(), Inference<TypeIntMirror<jlong, julong>>());
}
template <class U>
class OpAnd {
public:
U operator()(U v1, U v2) const {
return v1 & v2;
}
};
template <class CTP>
class InferAnd {
public:
CTP operator()(CTP t1, CTP t2) const {
return RangeInference::infer_and(t1, t2);
}
};
template <class U>
class OpOr {
public:
U operator()(U v1, U v2) const {
return v1 | v2;
}
};
template <class CTP>
class InferOr {
public:
CTP operator()(CTP t1, CTP t2) const {
return RangeInference::infer_or(t1, t2);
}
};
template <class U>
class OpXor {
public:
U operator()(U v1, U v2) const {
return v1 ^ v2;
}
};
template <class CTP>
class InferXor {
public:
CTP operator()(CTP t1, CTP t2) const {
return RangeInference::infer_xor(t1, t2);
}
};
TEST(opto, range_inference) {
test_binary<OpAnd, InferAnd>();
test_binary<OpOr, InferOr>();
test_binary<OpXor, InferXor>();
}

View File

@ -1,102 +0,0 @@
/*
* 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.
*
*/
#include "unittest.hpp"
#include "opto/utilities/xor.hpp"
#include "utilities/globalDefinitions.hpp" // For jint, juint
jint test_calc_max(const jint hi_0, const jint hi_1) {
return xor_upper_bound_for_ranges<jint, juint>(hi_0, hi_1);
}
jlong test_calc_max(const jlong hi_0, const jlong hi_1) {
return xor_upper_bound_for_ranges<jlong, julong>(hi_0, hi_1);
}
template <class S>
void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) {
ASSERT_GE(hi_0, 0);
ASSERT_GE(hi_1, 0);
// Skip out-of-bounds values for convenience
if (val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) {
return;
}
S v = val_0 ^ val_1;
S max = test_calc_max(hi_0, hi_1);
EXPECT_LE(v, max);
}
template <class S>
void test_sample_values(S hi_0, S hi_1) {
for (S i = 0; i <= 3; i++) {
for (S j = 0; j <= 3; j++) {
// Some bit combinations near the low and high ends of the range
test_xor_bounds(hi_0, hi_1, i, j);
test_xor_bounds(hi_0, hi_1, hi_0 - i, hi_1 - j);
}
}
}
template <class S>
void test_in_ranges(S lo, S hi){
ASSERT_GE(lo, 0);
ASSERT_LE(lo, hi);
for (S hi_0 = lo; hi_0 <= hi; hi_0++) {
for (S hi_1 = hi_0; hi_1 <=hi; hi_1++) {
test_sample_values(hi_0, hi_1);
}
}
}
template <class S>
void test_exhaustive(S limit) {
for (S hi_0 = 0; hi_0 <= limit; hi_0++) {
for (S hi_1 = hi_0; hi_1 <= limit; hi_1++) {
for (S val_0 = 0; val_0 <= hi_0; val_0++) {
for (S val_1 = val_0; val_1 <= hi_1; val_1++) {
test_xor_bounds(hi_0, hi_1, val_0, val_1);
}
}
}
}
}
template <class S>
void exec_tests() {
S top_bit = max_power_of_2<S>();
S prev_bit = top_bit >> 1;
test_exhaustive<S>(15);
test_in_ranges<S>(top_bit - 1, top_bit);
test_in_ranges<S>(prev_bit - 1, prev_bit);
}
TEST_VM(opto, xor_max) {
exec_tests<jint>();
exec_tests<jlong>();
}

View File

@ -522,6 +522,7 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/aotFlags \
-runtime/cds/appcds/aotProfile \
-runtime/cds/appcds/BadBSM.java \
-runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java \
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
-runtime/cds/appcds/CDSandJFR.java \

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2025 IBM Corporation. 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
* @bug 8370200
* @library /test/lib /
* @run driver ${test.main.class}
*/
package compiler.c2;
import compiler.lib.ir_framework.*;
import compiler.lib.ir_framework.Test;
public class TestReplaceNarrowPhiWithBottomPhi {
private int field1;
private volatile int field2;
static public void main(String[] args) {
TestFramework.run();
}
@Test
@IR(counts = { IRNode.PHI, "2" })
public void test1() {
int j;
for (j = 0; j < 10; j++) {
}
inlined1(j);
// Initially, there are 2 memory Phis: one for bottom, one for field1. After loop opts, both
// Phis have the same inputs and the narrower Phi should be replaced by the bottom Phi.
for (int i = 1; i < 100; i *= 2) {
field2 = 42;
}
}
private void inlined1(int j) {
if (j == 42) {
field1 = 42;
}
}
@Run(test = "test1")
private void test1Runner() {
test1();
inlined1(42);
}
}

View File

@ -0,0 +1,505 @@
/*
* 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.
*/
package compiler.igvn;
import compiler.lib.generators.*;
import compiler.lib.ir_framework.*;
import jdk.test.lib.Asserts;
/*
* @test
* @bug 8364766
* @summary Test value method of DivINode and DivLNode
* @key randomness
* @library /test/lib /
* @run driver compiler.igvn.IntegerDivValueTests
*/
public class IntegerDivValueTests {
private static final RestrictableGenerator<Integer> INTS = Generators.G.ints();
private static final RestrictableGenerator<Long> LONGS = Generators.G.longs();
public static void main(String[] args) {
TestFramework.run();
}
@ForceInline
private int getIntConstant(int value) {
// Simply return the given value to avoid javac already optimizing the operation away
return value;
}
private static final int INT_CONST_1 = INTS.next();
private static final int INT_CONST_2 = INTS.next();
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public int testIntConstantFolding() {
// All constants available during parsing
return INT_CONST_1 / INT_CONST_2;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public int testIntConstantFoldingSpecialCase() {
// All constants available during parsing
return getIntConstant(Integer.MIN_VALUE) / getIntConstant(-1);
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public int testIntRange(int in) {
int a = (in & 7) + 16;
return a / 12; // [16, 23] / 12 is constant 1
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testIntRange2(int in) {
int a = (in & 7) + 16;
return a / 4 > 3; // [16, 23] / 4 => [4, 5]
}
@Test
@IR(counts = {IRNode.DIV_I, "1"})
public boolean testIntRange3(int in, int in2) {
int a = (in & 31) + 16;
int b = (in2 & 3) + 5;
return a / b > 4; // [16, 47] / [5, 8] => [2, 9]
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testIntRange4(int in, int in2) {
int a = (in & 15); // [0, 15]
int b = (in2 & 3) + 1; // [1, 4]
return a / b >= 0;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testIntRange5(int in, int in2) {
int a = (in & 15) + 5; // [5, 20]
int b = (in2 & 3) + 1; // [1, 4]
return a / b > 0;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testIntRange6(int in, int in2) {
int a = (in & 15) + 5; // [5, 20]
int b = (in2 & 7) - 1; // [-1, 5]
if (b == 0) return false;
return a / b < -20;
}
@Test
@IR(counts = {IRNode.DIV_I, "1"})
public boolean testIntRange7(int in, int in2) {
int a = (in & 15) + 5; // [5, 20]
int b = (in2 & 7) - 1; // [-1, 5]
if (b == 0) return false;
return a / b > 0;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public int testIntRange8(int in, int in2) {
int a = (in & 31) + 128; // [128, 159]
int b = (in2 & 15) + 100; // [100, 115]
return a / b; // [1, 1] -> can be constant
}
private static final int INT_LIMIT_1 = INTS.next();
private static final int INT_LIMIT_2 = INTS.next();
private static final int INT_LIMIT_3 = INTS.next();
private static final int INT_LIMIT_4 = INTS.next();
private static final int INT_LIMIT_5 = INTS.next();
private static final int INT_LIMIT_6 = INTS.next();
private static final int INT_LIMIT_7 = INTS.next();
private static final int INT_LIMIT_8 = INTS.next();
private static final int INT_RANGE_LIMIT_X_LO;
private static final int INT_RANGE_LIMIT_X_HI;
private static final int INT_RANGE_LIMIT_Y_LO;
private static final int INT_RANGE_LIMIT_Y_HI;
static {
int limit1 = INTS.next();
int limit2 = INTS.next();
if (limit2 > limit1) {
INT_RANGE_LIMIT_X_LO = limit1;
INT_RANGE_LIMIT_X_HI = limit2;
} else {
INT_RANGE_LIMIT_X_LO = limit2;
INT_RANGE_LIMIT_X_HI = limit1;
}
int limit3 = INTS.next();
int limit4 = INTS.next();
if (limit4 > limit3) {
INT_RANGE_LIMIT_Y_LO = limit3;
INT_RANGE_LIMIT_Y_HI = limit4;
} else {
INT_RANGE_LIMIT_Y_LO = limit4;
INT_RANGE_LIMIT_Y_HI = limit3;
}
}
@ForceInline
private int clampInt(int val, int lo, int hi) {
return Math.min(hi, Math.max(val, lo));
}
@ForceInline
private int calculateIntSum(int z) {
int sum = 0;
if (z < INT_LIMIT_1) sum += 1;
if (z < INT_LIMIT_2) sum += 2;
if (z < INT_LIMIT_3) sum += 4;
if (z < INT_LIMIT_4) sum += 8;
if (z > INT_LIMIT_5) sum += 16;
if (z > INT_LIMIT_6) sum += 32;
if (z > INT_LIMIT_7) sum += 64;
if (z > INT_LIMIT_8) sum += 128;
return sum;
}
@Test
public int testIntRandomLimits(int x, int y) {
x = clampInt(x, INT_RANGE_LIMIT_X_LO, INT_RANGE_LIMIT_X_HI);
y = clampInt(y, INT_RANGE_LIMIT_Y_LO, INT_RANGE_LIMIT_Y_HI);
int z = x / y;
return calculateIntSum(z);
}
@DontCompile
public int testIntRandomLimitsInterpreted(int x, int y) {
x = clampInt(x, INT_RANGE_LIMIT_X_LO, INT_RANGE_LIMIT_X_HI);
y = clampInt(y, INT_RANGE_LIMIT_Y_LO, INT_RANGE_LIMIT_Y_HI);
int z = x / y;
return calculateIntSum(z);
}
@Run(test = {"testIntConstantFolding", "testIntConstantFoldingSpecialCase"})
public void checkIntConstants(RunInfo info) {
Asserts.assertEquals(INT_CONST_1 / INT_CONST_2, testIntConstantFolding());
Asserts.assertEquals(Integer.MIN_VALUE, testIntConstantFoldingSpecialCase());
}
@Run(test = {"testIntRange", "testIntRange2", "testIntRange3", "testIntRange4", "testIntRange5", "testIntRange6", "testIntRange7", "testIntRange8", "testIntRandomLimits"})
public void checkIntRanges(RunInfo info) {
for (int j = 0; j < 20; j++) {
int i1 = INTS.next();
int i2 = INTS.next();
checkInt(i1, i2);
}
}
@DontCompile
public void checkInt(int in, int in2) {
int a;
int b;
a = (in & 7) + 16;
Asserts.assertEquals(a / 12, testIntRange(in));
a = (in & 7) + 16;
Asserts.assertEquals(a / 4 > 3, testIntRange2(in));
a = (in & 31) + 16;
b = (in2 & 3) + 5;
Asserts.assertEquals(a / b > 4, testIntRange3(in, in2));
a = (in & 15);
b = (in2 & 3) + 1;
Asserts.assertEquals(a / b >= 0, testIntRange4(in, in2));
a = (in & 15) + 5;
b = (in2 & 3) + 1;
Asserts.assertEquals(a / b > 0, testIntRange5(in, in2));
a = (in & 15) + 5;
b = (in2 & 7) - 1;
Asserts.assertEquals(b == 0 ? false : a / b < -20, testIntRange6(in, in2));
a = (in & 15) + 5;
b = (in2 & 7) - 1;
Asserts.assertEquals(b == 0 ? false : a / b > 0, testIntRange7(in, in2));
a = (in & 31) + 128;
b = (in2 & 15) + 100;
Asserts.assertEquals(a / b, testIntRange8(in, in2));
int res;
try {
res = testIntRandomLimitsInterpreted(a, b);
} catch (ArithmeticException _) {
try {
testIntRandomLimits(a, b);
Asserts.fail("Expected ArithmeticException");
return; // unreachable
} catch (ArithmeticException _) {
return; // test succeeded, no result to assert
}
}
Asserts.assertEQ(res, testIntRandomLimits(a, b));
}
// Long variants
@ForceInline
private long getLongConstant(long value) {
// Simply return the given value to avoid javac already optimizing the operation away
return value;
}
private static final long LONG_CONST_1 = LONGS.next();
private static final long LONG_CONST_2 = LONGS.next();
@Test
//@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
// This results in a series of nodes due to DivLNode::Ideal and in particular transform_long_divide, which operates on non-constant divisors.
// transform_long_divide splits up the division into multiple other nodes, such as MulHiLNode, which does not have a good Value() implemantion.
// When JDK-8366815 is fixed, these rules should be reenabled
// Alternatively, a better MulHiLNode::Value() implemantion should also lead to constant folding
public long testLongConstantFolding() {
// All constants available during parsing
return LONG_CONST_1 / LONG_CONST_2;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public long testLongConstantFoldingSpecialCase() {
// All constants available during parsing
return getLongConstant(Long.MIN_VALUE) / getLongConstant(-1L);
}
@Test
//@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
// This results in a series of nodes due to DivLNode::Ideal and in particular transform_long_divide, which operates on non-constant divisors.
// transform_long_divide splits up the division into multiple other nodes, such as MulHiLNode, which does not have a good Value() implemantion.
// When JDK-8366815 is fixed, these rules should be reenabled
// Alternatively, a better MulHiLNode::Value() implemantion should also lead to constant folding
@IR(counts = {IRNode.RSHIFT_L, "> 0", IRNode.ADD_L, "> 0", IRNode.AND_L, "> 0"}, failOn = {IRNode.DIV})
public long testLongRange(long in) {
long a = (in & 7L) + 16L;
return a / 12L; // [16, 23] / 12 is constant 1
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testLongRange2(long in) {
long a = (in & 7L) + 16L;
return a / 4L > 3L; // [16, 23] / 4 => [4, 5]
}
@Test
@IR(counts = {IRNode.DIV_L, "1"})
public boolean testLongRange3(long in, long in2) {
long a = (in & 31L) + 16L;
long b = (in2 & 3L) + 5L;
return a / b > 4L; // [16, 47] / [5, 8] => [2, 9]
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testLongRange4(long in, long in2) {
long a = (in & 15L); // [0, 15]
long b = (in2 & 3L) + 1L; // [1, 4]
return a / b >= 0L;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testLongRange5(long in, long in2) {
long a = (in & 15L) + 5L; // [5, 20]
long b = (in2 & 3L) + 1L; // [1, 4]
return a / b > 0L;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public boolean testLongRange6(long in, long in2) {
long a = (in & 15L) + 5L; // [5, 20]
long b = (in2 & 7L) - 1L; // [-1, 5]
if (b == 0L) return false;
return a / b < -20L;
}
@Test
@IR(counts = {IRNode.DIV_L, "1"})
public boolean testLongRange7(long in, long in2) {
long a = (in & 15L) + 5L; // [5, 20]
long b = (in2 & 7L) - 1L; // [-1, 5]
if (b == 0L) return false;
return a / b > 0L;
}
@Test
@IR(failOn = {IRNode.DIV, IRNode.URSHIFT, IRNode.RSHIFT, IRNode.MUL, IRNode.ADD, IRNode.SUB, IRNode.AND})
public long testLongRange8(long in, long in2) {
long a = (in & 31L) + 128L; // [128, 159]
long b = (in2 & 15L) + 100L; // [100, 115]
return a / b; // [1, 1] -> can be constant
}
private static final long LONG_LIMIT_1 = LONGS.next();
private static final long LONG_LIMIT_2 = LONGS.next();
private static final long LONG_LIMIT_3 = LONGS.next();
private static final long LONG_LIMIT_4 = LONGS.next();
private static final long LONG_LIMIT_5 = LONGS.next();
private static final long LONG_LIMIT_6 = LONGS.next();
private static final long LONG_LIMIT_7 = LONGS.next();
private static final long LONG_LIMIT_8 = LONGS.next();
private static final long LONG_RANGE_LIMIT_X_LO;
private static final long LONG_RANGE_LIMIT_X_HI;
private static final long LONG_RANGE_LIMIT_Y_LO;
private static final long LONG_RANGE_LIMIT_Y_HI;
static {
long limit1 = LONGS.next();
long limit2 = LONGS.next();
if (limit2 > limit1) {
LONG_RANGE_LIMIT_X_LO = limit1;
LONG_RANGE_LIMIT_X_HI = limit2;
} else {
LONG_RANGE_LIMIT_X_LO = limit2;
LONG_RANGE_LIMIT_X_HI = limit1;
}
long limit3 = LONGS.next();
long limit4 = LONGS.next();
if (limit4 > limit3) {
LONG_RANGE_LIMIT_Y_LO = limit3;
LONG_RANGE_LIMIT_Y_HI = limit4;
} else {
LONG_RANGE_LIMIT_Y_LO = limit4;
LONG_RANGE_LIMIT_Y_HI = limit3;
}
}
@ForceInline
private long clampLong(long val, long lo, long hi) {
return Math.min(hi, Math.max(val, lo));
}
@ForceInline
private int calculateLongSum(long z) {
int sum = 0;
if (z < LONG_LIMIT_1) sum += 1;
if (z < LONG_LIMIT_2) sum += 2;
if (z < LONG_LIMIT_3) sum += 4;
if (z < LONG_LIMIT_4) sum += 8;
if (z > LONG_LIMIT_5) sum += 16;
if (z > LONG_LIMIT_6) sum += 32;
if (z > LONG_LIMIT_7) sum += 64;
if (z > LONG_LIMIT_8) sum += 128;
return sum;
}
@Test
public int testLongRandomLimits(long x, long y) {
x = clampLong(x, LONG_RANGE_LIMIT_X_LO, LONG_RANGE_LIMIT_X_HI);
y = clampLong(y, LONG_RANGE_LIMIT_Y_LO, LONG_RANGE_LIMIT_Y_HI);
long z = x / y;
return calculateLongSum(z);
}
@DontCompile
public int testLongRandomLimitsInterpreted(long x, long y) {
x = clampLong(x, LONG_RANGE_LIMIT_X_LO, LONG_RANGE_LIMIT_X_HI);
y = clampLong(y, LONG_RANGE_LIMIT_Y_LO, LONG_RANGE_LIMIT_Y_HI);
long z = x / y;
return calculateLongSum(z);
}
@Run(test = {"testLongConstantFolding", "testLongConstantFoldingSpecialCase"})
public void checkLongConstants(RunInfo infoLong) {
Asserts.assertEquals(LONG_CONST_1 / LONG_CONST_2, testLongConstantFolding());
Asserts.assertEquals(Long.MIN_VALUE, testLongConstantFoldingSpecialCase());
}
@Run(test = {"testLongRange", "testLongRange2", "testLongRange3", "testLongRange4", "testLongRange5", "testLongRange6", "testLongRange7", "testLongRange8", "testLongRandomLimits"})
public void checkLongRanges(RunInfo info) {
for (int j = 0; j < 20; j++) {
long l1 = LONGS.next();
long l2 = LONGS.next();
checkLong(l1, l2);
}
}
@DontCompile
public void checkLong(long in, long in2) {
long a;
long b;
a = (in & 7L) + 16L;
Asserts.assertEquals(a / 12L, testLongRange(in));
a = (in & 7L) + 16L;
Asserts.assertEquals(a / 4L > 3L, testLongRange2(in));
a = (in & 31L) + 16L;
b = (in2 & 3L) + 5L;
Asserts.assertEquals(a / b > 4L, testLongRange3(in, in2));
a = (in & 15L);
b = (in2 & 3L) + 1L;
Asserts.assertEquals(a / b >= 0L, testLongRange4(in, in2));
a = (in & 15L) + 5L;
b = (in2 & 3L) + 1L;
Asserts.assertEquals(a / b > 0L, testLongRange5(in, in2));
a = (in & 15L) + 5L;
b = (in2 & 7L) - 1L;
Asserts.assertEquals(b == 0 ? false : a / b < -20L, testLongRange6(in, in2));
a = (in & 15L) + 5L;
b = (in2 & 7L) - 1L;
Asserts.assertEquals(b == 0 ? false : a / b > 0L, testLongRange7(in, in2));
a = (in & 31L) + 128L;
b = (in2 & 15L) + 100L;
Asserts.assertEquals(a / b, testLongRange8(in, in2));
int res;
try {
res = testLongRandomLimitsInterpreted(a, b);
} catch (ArithmeticException _) {
try {
testLongRandomLimits(a, b);
Asserts.fail("Expected ArithmeticException");
return; // unreachable
} catch (ArithmeticException _) {
return; // test succeeded, no result to assert
}
}
Asserts.assertEQ(res, testLongRandomLimits(a, b));
}
}

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2025 IBM Corporation. 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
* @bug 8373396
* @summary Verify that min/max add ideal optimizations get applied correctly
* @modules java.base/jdk.internal.misc
* @library /test/lib /
* @run driver ${test.main.class}
*/
package compiler.igvn;
import compiler.lib.compile_framework.CompileFramework;
import compiler.lib.template_framework.Template;
import compiler.lib.template_framework.TemplateToken;
import compiler.lib.template_framework.library.CodeGenerationDataNameType;
import compiler.lib.template_framework.library.PrimitiveType;
import compiler.lib.template_framework.library.TestFrameworkClass;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import static compiler.lib.template_framework.Template.let;
import static compiler.lib.template_framework.Template.scope;
public class TestMinMaxIdeal {
public static void main(String[] args) {
// Create a new CompileFramework instance.
CompileFramework comp = new CompileFramework();
// Add a java source file.
comp.addJavaSourceCode("compiler.igvn.templated.MinMaxIdeal", generate(comp));
// Compile the source file.
comp.compile();
comp.invoke("compiler.igvn.templated.MinMaxIdeal", "main", new Object[] {new String[] {}});
}
private static String generate(CompileFramework comp) {
// Create a list to collect all tests.
List<TemplateToken> testTemplateTokens = Stream.of(Op.values())
.map(op -> new TestGenerator(op).generate())
.toList();
// Create the test class, which runs all testTemplateTokens.
return TestFrameworkClass.render(
// package and class name.
"compiler.igvn.templated", "MinMaxIdeal",
// List of imports.
Collections.emptySet(),
// classpath, so the Test VM has access to the compiled class files.
comp.getEscapedClassPathOfCompiledClasses(),
// The list of tests.
testTemplateTokens);
}
enum Op {
MIN_I("min", CodeGenerationDataNameType.ints()),
MAX_I("max", CodeGenerationDataNameType.ints()),
MIN_L("min", CodeGenerationDataNameType.longs()),
MAX_L("max", CodeGenerationDataNameType.longs());
final String functionName;
final PrimitiveType type;
Op(String functionName, PrimitiveType type) {
this.functionName = functionName;
this.type = type;
}
}
record TestGenerator(Op op) {
TemplateToken generate() {
var template = Template.make(() -> scope(
let("boxedTypeName", op.type.boxedTypeName()),
let("op", op.name()),
let("type", op.type.name()),
let("functionName", op.functionName),
"""
@Test
@IR(counts = {IRNode.#op, "= 1"},
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
@Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42})
public #type test_commute_#op(#type a, #type b) {
return #boxedTypeName.#functionName(a, b) + #boxedTypeName.#functionName(b, a);
}
@Test
@IR(counts = {IRNode.#op, "= 1"},
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
@Arguments(values = {Argument.NUMBER_42})
public #type test_flatten_#op(#type a) {
return #boxedTypeName.#functionName(#boxedTypeName.#functionName(a, 1), 2);
}
@Test
@IR(counts = {IRNode.#op, "= 2"},
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
@Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42})
public #type test_push_constant_left_#op(#type a, #type b) {
return #boxedTypeName.#functionName(#boxedTypeName.#functionName(a, 1), b) + #boxedTypeName.#functionName(b, a);
}
@Test
@IR(counts = {IRNode.#op, "= 2"},
phase = CompilePhase.BEFORE_MACRO_EXPANSION)
@Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42})
public #type test_push_constant_right_#op(#type a, #type b) {
return #boxedTypeName.#functionName(a, #boxedTypeName.#functionName(b, 1)) + #boxedTypeName.#functionName(b, a);
}
"""
));
return template.asToken();
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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 id=with-flags
* @bug 8373502
* @summary Test where a VPointer variable was pinned at the pre-loop, but not available at the
* Auto_Vectorization_Check, and so it should not be used for the auto vectorization
* aliasing check, to avoid a bad (circular) graph.
* @run main/othervm
* -XX:CompileCommand=compileonly,*TestAliasingCheckVPointerVariablesNotAvailable::test
* -XX:-TieredCompilation
* -Xcomp
* ${test.main.class}
*/
/*
* @test id=vanilla
* @bug 8373502
* @run main ${test.main.class}
*/
package compiler.loopopts.superword;
public class TestAliasingCheckVPointerVariablesNotAvailable {
static int iFld;
public static void main(String[] strArr) {
test();
}
static void test() {
int iArr[] = new int[400];
boolean flag = false;
for (int i = 6; i < 50000; i++) { // Trigger OSR
try {
int x = 234 / iFld;
iFld = iArr[3];
} catch (ArithmeticException a_e) {
}
for (int j = i; j < 2; j++) {
if (flag) {
iArr[j] = 117;
} else {
iArr[1] = 34;
}
iArr[1] += i;
}
}
}
}

View File

@ -162,7 +162,7 @@ public class TestReinterpretAndCast {
IRNode.STORE_VECTOR, "> 0",
IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least I2F
applyIfPlatform = {"64-bit", "true"},
applyIfCPUFeature = {"avx", "true"})
applyIfCPUFeatureAnd = {"avx", "true", "f16c", "true"})
@IR(counts = {IRNode.LOAD_VECTOR_I, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0",
IRNode.VECTOR_CAST_F2HF, IRNode.VECTOR_SIZE + "min(max_int, max_float, max_short)", "> 0",
IRNode.STORE_VECTOR, "> 0",
@ -208,7 +208,7 @@ public class TestReinterpretAndCast {
IRNode.STORE_VECTOR, "> 0",
IRNode.VECTOR_REINTERPRET, "> 0"}, // We have at least F2I
applyIfPlatform = {"64-bit", "true"},
applyIfCPUFeature = {"avx", "true"})
applyIfCPUFeatureAnd = {"avx", "true", "f16c", "true"})
@IR(counts = {IRNode.LOAD_VECTOR_S, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0",
IRNode.VECTOR_CAST_HF2F, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0",
IRNode.VECTOR_CAST_I2L, IRNode.VECTOR_SIZE + "min(max_float, max_short, max_long)", "> 0",

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 IBM Corporation. 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
* @bug 8370200
* @summary Crash: assert(outer->outcnt() >= phis + 2 - be_loads && outer->outcnt() <= phis + 2 + stores + 1) failed: only phis
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:StressSeed=36200582 -XX:CompileCommand=quiet
* -XX:CompileCommand=compileonly,*TestMismatchedMemoryPhis*::mainTest -XX:-TieredCompilation
* -Xcomp -XX:+StressIGVN -XX:+StressLoopPeeling -XX:PerMethodTrapLimit=0 ${test.main.class}
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=quiet
* -XX:CompileCommand=compileonly,*TestMismatchedMemoryPhis*::mainTest -XX:-TieredCompilation
* -Xcomp -XX:+StressIGVN -XX:+StressLoopPeeling -XX:PerMethodTrapLimit=0 ${test.main.class}
* @run main ${test.main.class}
*/
package compiler.loopstripmining;
public class TestMismatchedMemoryPhis {
long l;
volatile int iArrFld[];
void mainTest() {
int i, i1, i15 = 4, i16 = 4;
for (i = 1; i < 7; ++i) {
l = i;
}
int j = 1;
while (++j < 4) {
try {
i1 = i15 % i16;
i16 = i15;
i1 = 0 % iArrFld[j];
} catch (ArithmeticException a_e) {
}
}
}
static public void main(String[] args) {
try {
new TestMismatchedMemoryPhis().mainTest();
} catch (NullPointerException npe) {
// Expected
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2021, 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
@ -31,6 +31,8 @@
* java.management
*/
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.process.OutputAnalyzer;
@ -43,8 +45,44 @@ public class SharedSymbolTableBucketSize {
+ Integer.valueOf(bucket_size));
CDSTestUtils.checkMappingFailure(output);
String regex = "Average bucket size : ([0-9]+\\.[0-9]+).*";
String s = output.firstMatch(regex, 1);
/* [1] There may be other table stats that precede the symbol tabble.
Skip all thse until we find this:
[0.677s][info][aot,hashtables] Shared symbol table stats -------- base: 0x0000000800000000
[0.677s][info][aot,hashtables] Number of entries : 46244
[0.677s][info][aot,hashtables] Total bytes used : 393792
[0.677s][info][aot,hashtables] Average bytes per entry : 8.516
[0.677s][info][aot,hashtables] Average bucket size : 7.734
[0.677s][info][aot,hashtables] Variance of bucket size : 7.513
[0.677s][info][aot,hashtables] Std. dev. of bucket size: 2.741
[0.677s][info][aot,hashtables] Maximum bucket size : 20
[0.677s][info][aot,hashtables] Empty buckets : 2
[0.677s][info][aot,hashtables] Value_Only buckets : 24
[0.677s][info][aot,hashtables] Other buckets : 5953
....
*/
Pattern pattern0 = Pattern.compile("Shared symbol table stats.*", Pattern.DOTALL);
Matcher matcher0 = pattern0.matcher(output.getStdout());
String stat = null;
if (matcher0.find()) {
stat = matcher0.group(0);
}
if (stat == null) {
throw new Exception("FAILED: pattern \"" + pattern0 + "\" not found");
}
/* (2) The first "Average bucket size" line in the remaining output is for the
shared symbol table */
Pattern pattern = Pattern.compile("Average bucket size *: *([0-9]+\\.[0-9]+).*", Pattern.MULTILINE);
Matcher matcher = pattern.matcher(stat);
String s = null;
if (matcher.find()) {
s = matcher.group(1);
}
if (s == null) {
throw new Exception("FAILED: pattern \"" + pattern + "\" not found");
}
Float f = Float.parseFloat(s);
int size = Math.round(f);
if (size != bucket_size) {

View File

@ -83,17 +83,6 @@ public class AOTLoggingTag {
out.shouldContain("[aot] Opened AOT cache hello.aot");
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
printTestCase("All old -Xlog:cds+heap logs have been changed to -Xlog:aot+heap should alias to -Xlog:cds+heap");
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
"-XX:AOTCache=" + aotCacheFile,
"-Xlog:aot+heap",
"-cp", appJar, helloClass);
out = CDSTestUtils.executeAndLog(pb, "prod");
out.shouldNotContain("No tag set matches selection: aot+heap");
out.shouldContain("[aot,heap] resolve subgraph java.lang.Integer$IntegerCache");
out.shouldHaveExitValue(0);
//----------------------------------------------------------------------
printTestCase("Production Run: errors should be printed with [aot] decoration");
pb = ProcessTools.createLimitedTestJavaProcessBuilder(

View File

@ -0,0 +1,261 @@
/*
* 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 AOT cache should preserve heap object identity when required by JLS. For example, Enums and Integers.
* @requires vm.cds
* @requires vm.cds.supports.aot.class.linking
* @requires vm.debug
* @library /test/lib
* @build HeapObjectIdentity
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar dummy.jar
* Dummy
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar
* HeapObjectIdentityApp
* MyAOTInitedClass
* MyAOTInitedClass$MyEnum
* MyAOTInitedClass$Wrapper
* @run driver HeapObjectIdentity AOT --two-step-training
*/
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
public class HeapObjectIdentity {
static final String appJar = ClassFileInstaller.getJarPath("dummy.jar");
static final String bootJar = ClassFileInstaller.getJarPath("boot.jar");
static final String mainClass = "HeapObjectIdentityApp"; // Loaded from boot.jar
public static void main(String[] args) throws Exception {
Tester t = new Tester();
t.run(args);
// Integer$IntegerCache should preserve the object identity of cached Integer objects,
// even when the cache size is different between assembly and production.
t.productionRun(new String[] {
"-XX:AutoBoxCacheMax=2048"
});
}
static class Tester extends CDSAppTester {
public Tester() {
super(mainClass);
}
@Override
public String classpath(RunMode runMode) {
return appJar;
}
@Override
public String[] vmArgs(RunMode runMode) {
String bootcp = "-Xbootclasspath/a:" + bootJar;
if (runMode == RunMode.ASSEMBLY) {
return new String[] {
"-Xlog:aot+class=debug",
"-XX:AOTInitTestClass=MyAOTInitedClass",
bootcp
};
} else {
return new String[] {bootcp};
}
}
@Override
public String[] appCommandLine(RunMode runMode) {
return new String[] {
mainClass,
runMode.toString(),
};
}
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (runMode == RunMode.ASSEMBLY) {
out.shouldContain("MyAOTInitedClass aot-linked inited");
}
}
}
}
class HeapObjectIdentityApp {
public static void main(String... args) {
MyAOTInitedClass.test();
}
}
// This class is loaded by the boot loader, as -XX:AOTInitTestClass is not too friendly
// with classes by other loaders.
class MyAOTInitedClass {
static Object[] archivedObjects;
static {
if (archivedObjects == null) {
archivedObjects = new Object[14];
archivedObjects[0] = Wrapper.BOOLEAN;
archivedObjects[1] = Wrapper.INT.zero();
archivedObjects[2] = Wrapper.DOUBLE.zero();
archivedObjects[3] = MyEnum.DUMMY1;
archivedObjects[4] = Boolean.class;
archivedObjects[5] = Byte.class;
archivedObjects[6] = Character.class;
archivedObjects[7] = Short.class;
archivedObjects[8] = Integer.class;
archivedObjects[9] = Long.class;
archivedObjects[10] = Float.class;
archivedObjects[11] = Double.class;
archivedObjects[12] = Void.class;
archivedObjects[13] = Integer.valueOf(1);
} else {
System.out.println("Initialized from CDS");
}
}
public static void test() {
if (archivedObjects[0] != Wrapper.BOOLEAN) {
throw new RuntimeException("Huh 0");
}
if (archivedObjects[1] != Wrapper.INT.zero()) {
throw new RuntimeException("Huh 1");
}
if (archivedObjects[2] != Wrapper.DOUBLE.zero()) {
throw new RuntimeException("Huh 2");
}
if (archivedObjects[3] != MyEnum.DUMMY1) {
throw new RuntimeException("Huh 3");
}
if (MyEnum.BOOLEAN != true) {
throw new RuntimeException("Huh 10.1");
}
if (MyEnum.BYTE != -128) {
throw new RuntimeException("Huh 10.2");
}
if (MyEnum.CHAR != 'c') {
throw new RuntimeException("Huh 10.3");
}
if (MyEnum.SHORT != -12345) {
throw new RuntimeException("Huh 10.4");
}
if (MyEnum.INT != -123456) {
throw new RuntimeException("Huh 10.5");
}
if (MyEnum.LONG != 0x1234567890L) {
throw new RuntimeException("Huh 10.6");
}
if (MyEnum.LONG2 != -0x1234567890L) {
throw new RuntimeException("Huh 10.7");
}
if (MyEnum.FLOAT != 567891.0f) {
throw new RuntimeException("Huh 10.8");
}
if (MyEnum.DOUBLE != 12345678905678.890) {
throw new RuntimeException("Huh 10.9");
}
checkClass(4, Boolean.class);
checkClass(5, Byte.class);
checkClass(6, Character.class);
checkClass(7, Short.class);
checkClass(8, Integer.class);
checkClass(9, Long.class);
checkClass(10, Float.class);
checkClass(11, Double.class);
checkClass(12, Void.class);
if (archivedObjects[13] != Integer.valueOf(1)) {
throw new RuntimeException("Integer cache identity test failed");
}
System.out.println("Success!");
}
static void checkClass(int index, Class c) {
if (archivedObjects[index] != c) {
throw new RuntimeException("archivedObjects[" + index + "] should be " + c);
}
}
// Simplified version of sun.invoke.util.Wrapper
public enum Wrapper {
// wrapperType simple primitiveType simple char emptyArray
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0]),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0]),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0])
;
public static final int COUNT = 10;
private static final Object DOUBLE_ZERO = (Double)(double)0;
private final Class<?> wrapperType;
private final Class<?> primitiveType;
private final char basicTypeChar;
private final String basicTypeString;
private final Object emptyArray;
Wrapper(Class<?> wtype,
String wtypeName,
Class<?> ptype,
String ptypeName,
char tchar,
Object emptyArray) {
this.wrapperType = wtype;
this.primitiveType = ptype;
this.basicTypeChar = tchar;
this.basicTypeString = String.valueOf(this.basicTypeChar);
this.emptyArray = emptyArray;
}
public Object zero() {
return switch (this) {
case BOOLEAN -> Boolean.FALSE;
case INT -> (Integer)0;
case DOUBLE -> DOUBLE_ZERO;
default -> null;
};
}
}
enum MyEnum {
DUMMY1,
DUMMY2;
static final boolean BOOLEAN = true;
static final byte BYTE = -128;
static final short SHORT = -12345;
static final char CHAR = 'c';
static final int INT = -123456;
static final long LONG = 0x1234567890L;
static final long LONG2 = -0x1234567890L;
static final float FLOAT = 567891.0f;
static final double DOUBLE = 12345678905678.890;
}
}
class Dummy {}

View File

@ -36,7 +36,7 @@
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar
* CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY
* CDSTestClassB CDSTestClassC CDSTestClassD
* CDSTestClassE CDSTestClassF CDSTestClassG CDSTestClassG$MyEnum CDSTestClassG$Wrapper
* CDSTestClassE CDSTestClassF
* pkg.ClassInPackage
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello
* @run driver ArchiveHeapTestClass
@ -58,7 +58,6 @@ public class ArchiveHeapTestClass {
static final String CDSTestClassD_name = CDSTestClassD.class.getName();
static final String CDSTestClassE_name = CDSTestClassE.class.getName();
static final String CDSTestClassF_name = CDSTestClassF.class.getName();
static final String CDSTestClassG_name = CDSTestClassG.class.getName();
static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/');
static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects";
@ -162,15 +161,6 @@ public class ArchiveHeapTestClass {
output = dumpBootAndHello(CDSTestClassF_name);
mustFail(output, "Class java.util.logging.Level not allowed in archive heap");
}
testCase("Complex enums");
output = dumpBootAndHello(CDSTestClassG_name, "-XX:+AOTClassLinking", "-Xlog:cds+class=debug");
mustSucceed(output);
TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:aot+heap,cds+init",
CDSTestClassG_name)
.assertNormalExit("init subgraph " + CDSTestClassG_name,
"Initialized from CDS");
}
}
@ -287,147 +277,3 @@ class CDSTestClassF {
archivedObjects[0] = java.util.logging.Level.OFF;
}
}
class CDSTestClassG {
static Object[] archivedObjects;
static {
if (archivedObjects == null) {
archivedObjects = new Object[13];
archivedObjects[0] = Wrapper.BOOLEAN;
archivedObjects[1] = Wrapper.INT.zero();
archivedObjects[2] = Wrapper.DOUBLE.zero();
archivedObjects[3] = MyEnum.DUMMY1;
archivedObjects[4] = Boolean.class;
archivedObjects[5] = Byte.class;
archivedObjects[6] = Character.class;
archivedObjects[7] = Short.class;
archivedObjects[8] = Integer.class;
archivedObjects[9] = Long.class;
archivedObjects[10] = Float.class;
archivedObjects[11] = Double.class;
archivedObjects[12] = Void.class;
} else {
System.out.println("Initialized from CDS");
}
}
public static void main(String args[]) {
if (archivedObjects[0] != Wrapper.BOOLEAN) {
throw new RuntimeException("Huh 0");
}
if (archivedObjects[1] != Wrapper.INT.zero()) {
throw new RuntimeException("Huh 1");
}
if (archivedObjects[2] != Wrapper.DOUBLE.zero()) {
throw new RuntimeException("Huh 2");
}
if (archivedObjects[3] != MyEnum.DUMMY1) {
throw new RuntimeException("Huh 3");
}
if (MyEnum.BOOLEAN != true) {
throw new RuntimeException("Huh 10.1");
}
if (MyEnum.BYTE != -128) {
throw new RuntimeException("Huh 10.2");
}
if (MyEnum.CHAR != 'c') {
throw new RuntimeException("Huh 10.3");
}
if (MyEnum.SHORT != -12345) {
throw new RuntimeException("Huh 10.4");
}
if (MyEnum.INT != -123456) {
throw new RuntimeException("Huh 10.5");
}
if (MyEnum.LONG != 0x1234567890L) {
throw new RuntimeException("Huh 10.6");
}
if (MyEnum.LONG2 != -0x1234567890L) {
throw new RuntimeException("Huh 10.7");
}
if (MyEnum.FLOAT != 567891.0f) {
throw new RuntimeException("Huh 10.8");
}
if (MyEnum.DOUBLE != 12345678905678.890) {
throw new RuntimeException("Huh 10.9");
}
checkClass(4, Boolean.class);
checkClass(5, Byte.class);
checkClass(6, Character.class);
checkClass(7, Short.class);
checkClass(8, Integer.class);
checkClass(9, Long.class);
checkClass(10, Float.class);
checkClass(11, Double.class);
checkClass(12, Void.class);
System.out.println("Success!");
}
static void checkClass(int index, Class c) {
if (archivedObjects[index] != c) {
throw new RuntimeException("archivedObjects[" + index + "] should be " + c);
}
}
// Simplified version of sun.invoke.util.Wrapper
public enum Wrapper {
// wrapperType simple primitiveType simple char emptyArray
BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0]),
INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0]),
DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0])
;
public static final int COUNT = 10;
private static final Object DOUBLE_ZERO = (Double)(double)0;
private final Class<?> wrapperType;
private final Class<?> primitiveType;
private final char basicTypeChar;
private final String basicTypeString;
private final Object emptyArray;
Wrapper(Class<?> wtype,
String wtypeName,
Class<?> ptype,
String ptypeName,
char tchar,
Object emptyArray) {
this.wrapperType = wtype;
this.primitiveType = ptype;
this.basicTypeChar = tchar;
this.basicTypeString = String.valueOf(this.basicTypeChar);
this.emptyArray = emptyArray;
}
public Object zero() {
return switch (this) {
case BOOLEAN -> Boolean.FALSE;
case INT -> (Integer)0;
case DOUBLE -> DOUBLE_ZERO;
default -> null;
};
}
}
enum MyEnum {
DUMMY1,
DUMMY2;
static final boolean BOOLEAN = true;
static final byte BYTE = -128;
static final short SHORT = -12345;
static final char CHAR = 'c';
static final int INT = -123456;
static final long LONG = 0x1234567890L;
static final long LONG2 = -0x1234567890L;
static final float FLOAT = 567891.0f;
static final double DOUBLE = 12345678905678.890;
}
}

View File

@ -24,6 +24,8 @@
/*
* @test
* @bug 8343802
* @comment Only need to run this once, in tier1.
* @requires vm.flagless & vm.debug
* @summary Tests that HotSpot C++ files have sorted includes
* @build SortIncludes
* @run main TestIncludesAreSorted

View File

@ -24,6 +24,8 @@
/*
* @test
* @bug 8343802
* @comment Only need to run this once, in tier1.
* @requires vm.flagless & vm.debug
* @summary Test prevent NULL backsliding in hotspot code and tests
* @run main TestNoNULL
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 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
@ -37,7 +37,7 @@ import java.util.ArrayList;
import java.util.List;
import static java.lang.invoke.MethodHandles.lookup;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.*;
public class TestVHInvokerCaching {
@ -74,7 +74,7 @@ public class TestVHInvokerCaching {
MethodHandles.Lookup lookup = lookup();
for (Field field : Holder.class.getFields()) {
for (Field field : Holder.class.getDeclaredFields()) {
String fieldName = field.getName();
Class<?> fieldType = field.getType();
@ -82,6 +82,8 @@ public class TestVHInvokerCaching {
testHandles.add(lookup.findVarHandle(Holder.class, fieldName, fieldType));
}
assertFalse(testHandles.isEmpty());
return testHandles.stream().map(vh -> new Object[]{ vh }).toArray(Object[][]::new);
}
}

View File

@ -23,14 +23,6 @@
import jdk.httpclient.test.lib.http3.Http3TestServer;
import jdk.test.lib.net.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import javax.net.ssl.SSLContext;
import java.io.IOException;
@ -48,7 +40,6 @@ import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
@ -61,6 +52,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiPredicate;
@ -78,28 +70,35 @@ import static java.net.http.HttpClient.Version.HTTP_2;
import static java.net.http.HttpClient.Version.HTTP_3;
import static java.net.http.HttpOption.H3_DISCOVERY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestWatcher;
public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
SSLContext sslContext;
HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]
HttpTestServer httpsTestServer; // HTTPS/1.1
HttpTestServer http2TestServer; // HTTP/2 ( h2c )
HttpTestServer https2TestServer; // HTTP/2 ( h2 )
HttpTestServer http3TestServer; // HTTP/3 ( h3 )
String httpURI_fixed;
String httpURI_chunk;
String httpsURI_fixed;
String httpsURI_chunk;
String http2URI_fixed;
String http2URI_chunk;
String https2URI_fixed;
String https2URI_chunk;
String http3URI_fixed;
String http3URI_chunk;
String http3URI_head;
static SSLContext sslContext;
static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]
static HttpTestServer httpsTestServer; // HTTPS/1.1
static HttpTestServer http2TestServer; // HTTP/2 ( h2c )
static HttpTestServer https2TestServer; // HTTP/2 ( h2 )
static HttpTestServer http3TestServer; // HTTP/3 ( h3 )
static String httpURI_fixed;
static String httpURI_chunk;
static String httpsURI_fixed;
static String httpsURI_chunk;
static String http2URI_fixed;
static String http2URI_chunk;
static String https2URI_fixed;
static String https2URI_chunk;
static String http3URI_fixed;
static String http3URI_chunk;
static String http3URI_head;
static final int ITERATION_COUNT = 1;
// a shared executor helps reduce the amount of threads created by the test
@ -117,8 +116,34 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);
}
final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private volatile HttpClient sharedClient;
final static class TestStopper implements TestWatcher, BeforeEachCallback {
final AtomicReference<String> failed = new AtomicReference<>();
TestStopper() { }
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
if (stopAfterFirstFailure()) {
String msg = "Aborting due to: " + cause;
failed.compareAndSet(null, msg);
FAILURES.putIfAbsent(context.getDisplayName(), cause);
System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
}
}
@Override
public void beforeEach(ExtensionContext context) {
String msg = failed.get();
Assumptions.assumeTrue(msg == null, msg);
}
}
@RegisterExtension
static final TestStopper stopper = new TestStopper();
static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private static volatile HttpClient sharedClient;
static class TestExecutor implements Executor {
final AtomicLong tasks = new AtomicLong();
@ -144,21 +169,10 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
}
protected boolean stopAfterFirstFailure() {
protected static boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}
final AtomicReference<SkipException> skiptests = new AtomicReference<>();
void checkSkip() {
var skip = skiptests.get();
if (skip != null) throw skip;
}
static String name(ITestResult result) {
var params = result.getParameters();
return result.getName()
+ (params == null ? "()" : Arrays.toString(result.getParameters()));
}
static Version version(String uri) {
if (uri.contains("/http1/") || uri.contains("/https1/"))
return HTTP_1_1;
@ -169,7 +183,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
return null;
}
HttpRequest.Builder newRequestBuilder(String uri) {
static HttpRequest.Builder newRequestBuilder(String uri) {
var builder = HttpRequest.newBuilder(URI.create(uri));
if (version(uri) == HTTP_3) {
builder.version(HTTP_3);
@ -178,7 +192,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
return builder;
}
HttpResponse<String> headRequest(HttpClient client)
static HttpResponse<String> headRequest(HttpClient client)
throws IOException, InterruptedException
{
System.out.println("\n" + now() + "--- Sending HEAD request ----\n");
@ -187,26 +201,16 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
var request = newRequestBuilder(http3URI_head)
.HEAD().version(HTTP_2).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.version(), HTTP_2);
assertEquals(200, response.statusCode());
assertEquals(HTTP_2, response.version());
System.out.println("\n" + now() + "--- HEAD request succeeded ----\n");
System.err.println("\n" + now() + "--- HEAD request succeeded ----\n");
return response;
}
@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
if (skiptests.get() == null) {
SkipException skip = new SkipException("some tests failed");
skip.setStackTrace(new StackTraceElement[0]);
skiptests.compareAndSet(null, skip);
}
}
}
@AfterClass
static final void printFailedTests(ITestContext context) {
@AfterAll
static final void printFailedTests() {
out.println("\n=========================");
try {
// Exceptions should already have been added to FAILURES
@ -230,7 +234,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
}
private String[] uris() {
private static String[] uris() {
return new String[] {
http3URI_fixed,
http3URI_chunk,
@ -245,8 +249,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
};
}
@DataProvider(name = "sanity")
public Object[][] sanity() {
public static Object[][] sanity() {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2][];
//Object[][] result = new Object[uris.length][];
@ -277,7 +280,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
}
private Object[][] variants(List<Thrower> throwers, Set<Where> whereValues) {
private static Object[][] variants(List<Thrower> throwers, Set<Where> whereValues) {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2 * throwers.size()][];
//Object[][] result = new Object[(uris.length/2) * 2 * 2][];
@ -296,93 +299,65 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
return result;
}
@DataProvider(name = "subscribeProvider")
public Object[][] subscribeProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] subscribeProvider() {
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_SUBSCRIBE, Where.AFTER_SUBSCRIBE));
}
@DataProvider(name = "requestProvider")
public Object[][] requestProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] requestProvider() {
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_REQUEST, Where.AFTER_REQUEST));
}
@DataProvider(name = "nextRequestProvider")
public Object[][] nextRequestProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] nextRequestProvider() {
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_NEXT_REQUEST, Where.AFTER_NEXT_REQUEST));
}
@DataProvider(name = "beforeCancelProviderIO")
public Object[][] beforeCancelProviderIO(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] beforeCancelProviderIO() {
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}
@DataProvider(name = "afterCancelProviderIO")
public Object[][] afterCancelProviderIO(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] afterCancelProviderIO() {
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
}
@DataProvider(name = "beforeCancelProviderCustom")
public Object[][] beforeCancelProviderCustom(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] beforeCancelProviderCustom() {
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}
@DataProvider(name = "afterCancelProviderCustom")
public Object[][] afterCancelProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] afterCancelProviderCustom() {
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
}
private HttpClient makeNewClient() {
private static HttpClient makeNewClient() {
clientCount.incrementAndGet();
return TRACKER.track(newClientBuilderForH3()
return TRACKER.track(HttpServerAdapters.createClientBuilderForH3()
.proxy(HttpClient.Builder.NO_PROXY)
.executor(executor)
.sslContext(sslContext)
.build());
}
HttpClient newHttpClient(boolean share) {
static HttpClient newHttpClient(boolean share) {
if (!share) return makeNewClient();
HttpClient shared = sharedClient;
if (shared != null) return shared;
synchronized (this) {
synchronized (AbstractThrowingPublishers.class) {
shared = sharedClient;
if (shared == null) {
shared = sharedClient = makeNewClient();
@ -430,7 +405,7 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
CompletableFuture<HttpResponse<String>> response = client.sendAsync(req, handler);
String body = response.join().body();
assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining()));
assertEquals(Stream.of(BODY.split("\\|")).collect(Collectors.joining()), body);
if (!sameClient) {
// Wait for the client to be garbage collected.
// we use the ReferenceTracker API rather than HttpClient::close here,
@ -474,7 +449,6 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
boolean async, Set<Where> whereValues)
throws Exception
{
checkSkip();
out.printf("%n%s%s%n", now(), name);
try {
testThrowing(uri, sameClient, publishers, finisher, thrower, async, whereValues);
@ -778,8 +752,8 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
}
@BeforeTest
public void setup() throws Exception {
@BeforeAll
public static void setup() throws Exception {
System.out.println(now() + "setup");
System.err.println(now() + "setup");
@ -861,8 +835,8 @@ public abstract class AbstractThrowingPublishers implements HttpServerAdapters {
System.err.println(now() + "setup done");
}
@AfterTest
public void teardown() throws Exception {
@AfterAll
public static void teardown() throws Exception {
System.out.println(now() + "teardown");
System.err.println(now() + "teardown");

View File

@ -35,18 +35,10 @@
* ReferenceTracker AbstractThrowingPushPromises
* jdk.httpclient.test.lib.common.HttpServerAdapters
* <concrete-class-name>
* @run testng/othervm -Djdk.internal.httpclient.debug=true <concrete-class-name>
* @run junit/othervm -Djdk.internal.httpclient.debug=true <concrete-class-name>
*/
import jdk.test.lib.net.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
@ -100,21 +92,32 @@ import static java.net.http.HttpClient.Version.HTTP_3;
import static java.net.http.HttpOption.Http3DiscoveryMode.HTTP_3_URI_ONLY;
import static java.net.http.HttpOption.H3_DISCOVERY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestWatcher;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public abstract class AbstractThrowingPushPromises implements HttpServerAdapters {
SSLContext sslContext;
HttpTestServer http2TestServer; // HTTP/2 ( h2c )
HttpTestServer https2TestServer; // HTTP/2 ( h2 )
HttpTestServer http3TestServer; // HTTP/3 ( h3 )
String http2URI_fixed;
String http2URI_chunk;
String https2URI_fixed;
String https2URI_chunk;
String http3URI_fixed;
String http3URI_chunk;
static SSLContext sslContext;
static HttpTestServer http2TestServer; // HTTP/2 ( h2c )
static HttpTestServer https2TestServer; // HTTP/2 ( h2 )
static HttpTestServer http3TestServer; // HTTP/3 ( h3 )
static String http2URI_fixed;
static String http2URI_chunk;
static String https2URI_fixed;
static String https2URI_chunk;
static String http3URI_fixed;
static String http3URI_chunk;
static final int ITERATION_COUNT = 1;
// a shared executor helps reduce the amount of threads created by the test
@ -132,8 +135,34 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);
}
final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private volatile HttpClient sharedClient;
static final class TestStopper implements TestWatcher, BeforeEachCallback {
final AtomicReference<String> failed = new AtomicReference<>();
TestStopper() { }
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
if (stopAfterFirstFailure()) {
String msg = "Aborting due to: " + cause;
failed.compareAndSet(null, msg);
FAILURES.putIfAbsent(context.getDisplayName(), cause);
System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
}
}
@Override
public void beforeEach(ExtensionContext context) {
String msg = failed.get();
Assumptions.assumeTrue(msg == null, msg);
}
}
@RegisterExtension
static final TestStopper stopper = new TestStopper();
static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private static volatile HttpClient sharedClient;
static class TestExecutor implements Executor {
final AtomicLong tasks = new AtomicLong();
@ -159,34 +188,13 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
}
protected boolean stopAfterFirstFailure() {
protected static boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}
final AtomicReference<SkipException> skiptests = new AtomicReference<>();
void checkSkip() {
var skip = skiptests.get();
if (skip != null) throw skip;
}
static String name(ITestResult result) {
var params = result.getParameters();
return result.getName()
+ (params == null ? "()" : Arrays.toString(result.getParameters()));
}
@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
if (skiptests.get() == null) {
SkipException skip = new SkipException("some tests failed");
skip.setStackTrace(new StackTraceElement[0]);
skiptests.compareAndSet(null, skip);
}
}
}
@AfterClass
static final void printFailedTests(ITestContext context) {
@AfterAll
static final void printFailedTests() {
out.println("\n=========================");
try {
// Exceptions should already have been added to FAILURES
@ -211,7 +219,7 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
}
private String[] uris() {
private static String[] uris() {
return new String[] {
http3URI_fixed,
http3URI_chunk,
@ -222,8 +230,7 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
};
}
@DataProvider(name = "sanity")
public Object[][] sanity() {
public static Object[][] sanity() {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2][];
@ -252,7 +259,7 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
}
private Object[][] variants(List<Thrower> throwers) {
private static Object[][] variants(List<Thrower> throwers) {
String[] uris = uris();
// reduce traces by always using the same client if
// stopAfterFirstFailure is requested.
@ -272,27 +279,19 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
return result;
}
@DataProvider(name = "ioVariants")
public Object[][] ioVariants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] ioVariants() {
return variants(List.of(
new UncheckedIOExceptionThrower()));
}
@DataProvider(name = "customVariants")
public Object[][] customVariants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] customVariants() {
return variants(List.of(
new UncheckedCustomExceptionThrower()));
}
private HttpClient makeNewClient() {
private static HttpClient makeNewClient() {
clientCount.incrementAndGet();
return TRACKER.track(newClientBuilderForH3()
return TRACKER.track(HttpServerAdapters.createClientBuilderForH3()
.version(HTTP_3)
.proxy(HttpClient.Builder.NO_PROXY)
.executor(executor)
@ -300,11 +299,11 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
.build());
}
HttpClient newHttpClient(boolean share) {
static HttpClient newHttpClient(boolean share) {
if (!share) return makeNewClient();
HttpClient shared = sharedClient;
if (shared != null) return shared;
synchronized (this) {
synchronized (AbstractThrowingPushPromises.class) {
shared = sharedClient;
if (shared == null) {
shared = sharedClient = makeNewClient();
@ -313,15 +312,15 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
}
Http3DiscoveryMode config(String uri) {
static Http3DiscoveryMode config(String uri) {
return uri.contains("/http3/") ? HTTP_3_URI_ONLY : null;
}
Version version(String uri) {
static Version version(String uri) {
return uri.contains("/http3/") ? HTTP_3 : HTTP_2;
}
HttpRequest request(String uri) {
static HttpRequest request(String uri) {
var builder = HttpRequest.newBuilder(URI.create(uri))
.version(version(uri));
var config = config(uri);
@ -358,15 +357,15 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
HttpResponse<Stream<String>> response =
client.sendAsync(req, BodyHandlers.ofLines(), pushHandler).get();
String body = response.body().collect(Collectors.joining("|"));
assertEquals(URI.create(body).getPath(), URI.create(uri).getPath());
assertEquals(URI.create(uri).getPath(), URI.create(body).getPath());
for (HttpRequest promised : pushPromises.keySet()) {
out.printf("%s Received promise: %s%n\tresponse: %s%n",
now(), promised, pushPromises.get(promised).get());
String promisedBody = pushPromises.get(promised).get().body()
.collect(Collectors.joining("|"));
assertEquals(promisedBody, promised.uri().toASCIIString());
assertEquals(promised.uri().toASCIIString(), promisedBody);
}
assertEquals(pushPromises.size(), 3);
assertEquals(3, pushPromises.size());
if (!sameClient) {
// Wait for the client to be garbage collected.
// we use the ReferenceTracker API rather than HttpClient::close here,
@ -429,7 +428,6 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
Finisher finisher, Thrower thrower)
throws Exception
{
checkSkip();
out.printf("%n%s%s%n", now(), name);
try {
testThrowing(uri, sameClient, handlers, finisher, thrower);
@ -603,9 +601,9 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
default:
expectedCount = 3;
}
assertEquals(promises.size(), expectedCount,
assertEquals(expectedCount, promises.size(),
"bad promise count for " + reqURI + " with " + w);
assertEquals(result, List.of(reqURI.toASCIIString()));
assertEquals(List.of(reqURI.toASCIIString()), result);
return result;
}
@ -758,8 +756,8 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
}
@BeforeTest
public void setup() throws Exception {
@BeforeAll
public static void setup() throws Exception {
sslContext = new SimpleSSLContext().get();
if (sslContext == null)
throw new AssertionError("Unexpected null sslContext");
@ -792,8 +790,8 @@ public abstract class AbstractThrowingPushPromises implements HttpServerAdapters
http3TestServer.start();
}
@AfterTest
public void teardown() throws Exception {
@AfterAll
public static void teardown() throws Exception {
String sharedClientName =
sharedClient == null ? null : sharedClient.toString();
sharedClient = null;

View File

@ -23,14 +23,6 @@
import jdk.httpclient.test.lib.http3.Http3TestServer;
import jdk.test.lib.net.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
@ -49,7 +41,6 @@ import java.net.http.HttpResponse.BodyHandlers;
import java.net.http.HttpResponse.BodySubscriber;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@ -76,28 +67,37 @@ import static java.net.http.HttpClient.Version.HTTP_2;
import static java.net.http.HttpClient.Version.HTTP_3;
import static java.net.http.HttpOption.H3_DISCOVERY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestWatcher;
public abstract class AbstractThrowingSubscribers implements HttpServerAdapters {
SSLContext sslContext;
HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]
HttpTestServer httpsTestServer; // HTTPS/1.1
HttpTestServer http2TestServer; // HTTP/2 ( h2c )
HttpTestServer https2TestServer; // HTTP/2 ( h2 )
HttpTestServer http3TestServer; // HTTP/3 ( h3 )
String httpURI_fixed;
String httpURI_chunk;
String httpsURI_fixed;
String httpsURI_chunk;
String http2URI_fixed;
String http2URI_chunk;
String https2URI_fixed;
String https2URI_chunk;
String http3URI_fixed;
String http3URI_chunk;
String http3URI_head;
static SSLContext sslContext;
static HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]
static HttpTestServer httpsTestServer; // HTTPS/1.1
static HttpTestServer http2TestServer; // HTTP/2 ( h2c )
static HttpTestServer https2TestServer; // HTTP/2 ( h2 )
static HttpTestServer http3TestServer; // HTTP/3 ( h3 )
static String httpURI_fixed;
static String httpURI_chunk;
static String httpsURI_fixed;
static String httpsURI_chunk;
static String http2URI_fixed;
static String http2URI_chunk;
static String https2URI_fixed;
static String https2URI_chunk;
static String http3URI_fixed;
static String http3URI_chunk;
static String http3URI_head;
static final int ITERATION_COUNT = 1;
static final int REPEAT_RESPONSE = 3;
@ -116,8 +116,34 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);
}
final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private volatile HttpClient sharedClient;
static final class TestStopper implements TestWatcher, BeforeEachCallback {
final AtomicReference<String> failed = new AtomicReference<>();
TestStopper() { }
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
if (stopAfterFirstFailure()) {
String msg = "Aborting due to: " + cause;
failed.compareAndSet(null, msg);
FAILURES.putIfAbsent(context.getDisplayName(), cause);
System.out.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
System.err.printf("%nTEST FAILED: %s%s%n\tAborting due to %s%n%n",
now(), context.getDisplayName(), cause);
}
}
@Override
public void beforeEach(ExtensionContext context) {
String msg = failed.get();
Assumptions.assumeTrue(msg == null, msg);
}
}
@RegisterExtension
static final TestStopper stopper = new TestStopper();
static final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;
private static volatile HttpClient sharedClient;
static class TestExecutor implements Executor {
final AtomicLong tasks = new AtomicLong();
@ -143,21 +169,10 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
}
}
protected boolean stopAfterFirstFailure() {
protected static boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}
final AtomicReference<SkipException> skiptests = new AtomicReference<>();
void checkSkip() {
var skip = skiptests.get();
if (skip != null) throw skip;
}
static String name(ITestResult result) {
var params = result.getParameters();
return result.getName()
+ (params == null ? "()" : Arrays.toString(result.getParameters()));
}
static Version version(String uri) {
if (uri.contains("/http1/") || uri.contains("/https1/"))
return HTTP_1_1;
@ -168,7 +183,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return null;
}
HttpRequest.Builder newRequestBuilder(String uri) {
static HttpRequest.Builder newRequestBuilder(String uri) {
var builder = HttpRequest.newBuilder(URI.create(uri));
if (version(uri) == HTTP_3) {
builder.version(HTTP_3);
@ -177,7 +192,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return builder;
}
HttpResponse<String> headRequest(HttpClient client)
static HttpResponse<String> headRequest(HttpClient client)
throws IOException, InterruptedException
{
System.out.println("\n" + now() + "--- Sending HEAD request ----\n");
@ -186,26 +201,15 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
var request = newRequestBuilder(http3URI_head)
.HEAD().version(HTTP_2).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.version(), HTTP_2);
assertEquals(200, response.statusCode());
assertEquals(HTTP_2, response.version());
System.out.println("\n" + now() + "--- HEAD request succeeded ----\n");
System.err.println("\n" + now() + "--- HEAD request succeeded ----\n");
return response;
}
@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
if (skiptests.get() == null) {
SkipException skip = new SkipException("some tests failed");
skip.setStackTrace(new StackTraceElement[0]);
skiptests.compareAndSet(null, skip);
}
}
}
@AfterClass
static final void printFailedTests(ITestContext context) {
@AfterAll
static final void printFailedTests() {
out.println("\n=========================");
try {
// Exceptions should already have been added to FAILURES
@ -230,7 +234,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
}
}
private String[] uris() {
private static String[] uris() {
return new String[] {
http3URI_fixed,
http3URI_chunk,
@ -245,10 +249,9 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
};
}
static AtomicLong URICOUNT = new AtomicLong();
static final AtomicLong URICOUNT = new AtomicLong();
@DataProvider(name = "sanity")
public Object[][] sanity() {
public static Object[][] sanity() {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2][];
int i = 0;
@ -261,11 +264,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return result;
}
@DataProvider(name = "variants")
public Object[][] variants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
public static Object[][] variants() {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2 * 2][];
int i = 0;
@ -282,9 +281,9 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return result;
}
private HttpClient makeNewClient() {
private static HttpClient makeNewClient() {
clientCount.incrementAndGet();
HttpClient client = newClientBuilderForH3()
HttpClient client = HttpServerAdapters.createClientBuilderForH3()
.proxy(HttpClient.Builder.NO_PROXY)
.executor(executor)
.sslContext(sslContext)
@ -292,11 +291,11 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
return TRACKER.track(client);
}
HttpClient newHttpClient(boolean share) {
static HttpClient newHttpClient(boolean share) {
if (!share) return makeNewClient();
HttpClient shared = sharedClient;
if (shared != null) return shared;
synchronized (this) {
synchronized (AbstractThrowingSubscribers.class) {
shared = sharedClient;
if (shared == null) {
shared = sharedClient = makeNewClient();
@ -357,7 +356,7 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
HttpResponse<String> response = client.send(req, handler);
String body = response.body();
Stream.of(body.split("\n")).forEach(u ->
assertEquals(URI.create(u).getPath(), URI.create(uri2).getPath()));
assertEquals(URI.create(uri2).getPath(), URI.create(u).getPath()));
if (!sameClient) {
// Wait for the client to be garbage collected.
// we use the ReferenceTracker API rather than HttpClient::close here,
@ -469,7 +468,6 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
boolean async, EnumSet<Where> excludes)
throws Exception
{
checkSkip();
out.printf("%n%s%s%n", now(), name);
try {
testThrowing(uri, sameClient, handlers, finisher, thrower, async, excludes);
@ -551,7 +549,6 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
if (error != null) throw error;
System.out.println(now() + "operation finished normally: " + tracker.getName());
System.err.println(now() + "operation finished normally: " + tracker.getName());
}
}
}
@ -819,8 +816,8 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
}
@BeforeTest
public void setup() throws Exception {
@BeforeAll
public static void setup() throws Exception {
System.out.println(now() + "setup");
System.err.println(now() + "setup");
@ -902,8 +899,8 @@ public abstract class AbstractThrowingSubscribers implements HttpServerAdapters
System.err.println(now() + "setup done");
}
@AfterTest
public void teardown() throws Exception {
@AfterAll
public static void teardown() throws Exception {
System.out.println(now() + "teardown");
System.err.println(now() + "teardown");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersCustomAfterCancel
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersCustomAfterCancel
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersCustomAfterCancel extends AbstractThrowingPublishers {
@Test(dataProvider = "afterCancelProviderCustom")
@ParameterizedTest
@MethodSource("afterCancelProviderCustom")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersCustomBeforeCancel
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersCustomBeforeCancel
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersCustomBeforeCancel extends AbstractThrowingPublishers {
@Test(dataProvider = "beforeCancelProviderCustom")
@ParameterizedTest
@MethodSource("beforeCancelProviderCustom")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersIOAfterCancel
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersIOAfterCancel
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersIOAfterCancel extends AbstractThrowingPublishers {
@Test(dataProvider = "afterCancelProviderIO")
@ParameterizedTest
@MethodSource("afterCancelProviderIO")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersIOBeforeCancel
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersIOBeforeCancel
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersIOBeforeCancel extends AbstractThrowingPublishers {
@Test(dataProvider = "beforeCancelProviderIO")
@ParameterizedTest
@MethodSource("beforeCancelProviderIO")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersInNextRequest
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersInNextRequest
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersInNextRequest extends AbstractThrowingPublishers {
@Test(dataProvider = "nextRequestProvider")
@ParameterizedTest
@MethodSource("nextRequestProvider")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersInRequest
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersInRequest
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersInRequest extends AbstractThrowingPublishers {
@Test(dataProvider = "requestProvider")
@ParameterizedTest
@MethodSource("requestProvider")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,18 +29,20 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersInSubscribe
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersInSubscribe
*/
import org.testng.annotations.Test;
import java.util.Set;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersInSubscribe extends AbstractThrowingPublishers {
@Test(dataProvider = "subscribeProvider")
@ParameterizedTest
@MethodSource("subscribeProvider")
public void testThrowingAsString(String uri, boolean sameClient,
Thrower thrower, Set<Where> whereValues)
throws Exception

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,16 +29,18 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPublishers ThrowingPublishersSanity
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true
* @run junit/othervm -Djdk.internal.httpclient.debug=true
* -Djdk.httpclient.enableAllMethodRetry=true
* ThrowingPublishersSanity
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPublishersSanity extends AbstractThrowingPublishers {
@Test(dataProvider = "sanity")
@ParameterizedTest
@MethodSource("sanity")
public void testSanity(String uri, boolean sameClient)
throws Exception {
super.testSanityImpl(uri,sameClient);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsInputStreamCustom
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsInputStreamCustom
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsInputStreamCustom
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsInputStreamCustom extends AbstractThrowingPushPromises {
@Test(dataProvider = "customVariants")
@ParameterizedTest
@MethodSource("customVariants")
public void testThrowingAsInputStream(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsInputStreamImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsInputStreamIO
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsInputStreamIO
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsInputStreamIO
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsInputStreamIO extends AbstractThrowingPushPromises {
@Test(dataProvider = "ioVariants")
@ParameterizedTest
@MethodSource("ioVariants")
public void testThrowingAsInputStream(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsInputStreamImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsLinesCustom
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsLinesCustom
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsLinesCustom
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsLinesCustom extends AbstractThrowingPushPromises {
@Test(dataProvider = "customVariants")
@ParameterizedTest
@MethodSource("customVariants")
public void testThrowingAsLines(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsLinesImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsLinesIO
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsLinesIO
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsLinesIO
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsLinesIO extends AbstractThrowingPushPromises {
@Test(dataProvider = "ioVariants")
@ParameterizedTest
@MethodSource("ioVariants")
public void testThrowingAsLines(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsLinesImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsStringCustom
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsStringCustom
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsStringCustom
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsStringCustom extends AbstractThrowingPushPromises {
@Test(dataProvider = "customVariants")
@ParameterizedTest
@MethodSource("customVariants")
public void testThrowingAsString(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsStringImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesAsStringIO
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsStringIO
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesAsStringIO
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesAsStringIO extends AbstractThrowingPushPromises {
@Test(dataProvider = "ioVariants")
@ParameterizedTest
@MethodSource("ioVariants")
public void testThrowingAsString(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsStringImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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
@ -30,14 +30,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker AbstractThrowingPushPromises ThrowingPushPromisesSanity
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesSanity
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingPushPromisesSanity
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingPushPromisesSanity extends AbstractThrowingPushPromises {
@Test(dataProvider = "sanity")
@ParameterizedTest
@MethodSource("sanity")
public void testSanity(String uri, boolean sameClient)
throws Exception {
super.testSanityImpl(uri, sameClient);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,14 +29,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker ThrowingSubscribersAsInputStream AbstractThrowingSubscribers
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsInputStream
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsInputStream
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingSubscribersAsInputStream extends AbstractThrowingSubscribers {
@Test(dataProvider = "variants")
@ParameterizedTest
@MethodSource("variants")
public void testThrowingAsInputStream(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsInputStreamImpl(uri, sameClient, thrower);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, 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,14 +29,16 @@
* @build jdk.test.lib.net.SimpleSSLContext
* ReferenceTracker ThrowingSubscribersAsInputStreamAsync AbstractThrowingSubscribers
* jdk.httpclient.test.lib.common.HttpServerAdapters
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsInputStreamAsync
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsInputStreamAsync
*/
import org.testng.annotations.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingSubscribersAsInputStreamAsync extends AbstractThrowingSubscribers {
@Test(dataProvider = "variants")
@ParameterizedTest
@MethodSource("variants")
public void testThrowingAsInputStreamAsync(String uri, boolean sameClient, Thrower thrower)
throws Exception {
super.testThrowingAsInputStreamAsyncImpl(uri, sameClient, thrower);

View File

@ -35,18 +35,20 @@
* ReferenceTracker
* jdk.httpclient.test.lib.common.HttpServerAdapters
* jdk.test.lib.net.SimpleSSLContext
* @run testng/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsLimiting
* @run junit/othervm -Djdk.internal.httpclient.debug=true ThrowingSubscribersAsLimiting
*/
import org.testng.annotations.Test;
import java.net.http.HttpResponse;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class ThrowingSubscribersAsLimiting extends AbstractThrowingSubscribers {
@Test(dataProvider = "variants")
@ParameterizedTest
@MethodSource("variants")
public void test(String uri, boolean sameClient, Thrower thrower) throws Exception {
test(uri, sameClient, thrower, false);
}

Some files were not shown because too many files have changed in this diff Show More