diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index 79279be7acc1b..cba3dd919dafb 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -1835,8 +1835,10 @@ enum Nf { // Vector Unit-Stride Segment Load Instructions INSN(vlseg3e8_v, 0b0000111, 0b000, 0b00000, 0b00, 0b0, g3); + INSN(vlseg4e8_v, 0b0000111, 0b000, 0b00000, 0b00, 0b0, g4); // Vector Unit-Stride Segment Store Instructions + INSN(vsseg3e8_v, 0b0100111, 0b000, 0b00000, 0b00, 0b0, g3); INSN(vsseg4e8_v, 0b0100111, 0b000, 0b00000, 0b00, 0b0, g4); #undef INSN diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index f214c489557d6..8792dea7de5eb 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -5322,6 +5322,279 @@ class StubGenerator: public StubCodeGenerator { return (address) start; } + /** + * vector registers: + * input VectorRegister's: intputV1-V4, for m2 they could be v2, v4, v6, for m1 they could be v2, v4, v6, v8 + * index VectorRegister's: idxV1-V3, for m2 they could be v8, v10, v12, v14, for m1 they could be v10, v12, v14, v16 + * output VectorRegister's: outputV1-V4, for m2 they could be v16, v18, v20, v22, for m1 they could be v18, v20, v22 + * + * NOTE: each field will occupy a single vector register group + */ + void base64_vector_decode_round(Register src, Register dst, Register codec, + Register size, Register stepSrc, Register stepDst, Register failedIdx, Register minusOne, + VectorRegister inputV1, VectorRegister inputV2, VectorRegister inputV3, VectorRegister inputV4, + VectorRegister idxV1, VectorRegister idxV2, VectorRegister idxV3, VectorRegister idxV4, + VectorRegister outputV1, VectorRegister outputV2, VectorRegister outputV3, + Assembler::LMUL lmul) { + // set vector register type/len + __ vsetvli(x0, size, Assembler::e8, lmul, Assembler::ma, Assembler::ta); + + // segmented load src into v registers: mem(src) => vr(4) + __ vlseg4e8_v(inputV1, src); + + // src = src + register_group_len_bytes * 4 + __ add(src, src, stepSrc); + + // decoding + // 1. indexed load: vr(4) => vr(4) + __ vluxei8_v(idxV1, codec, inputV1); + __ vluxei8_v(idxV2, codec, inputV2); + __ vluxei8_v(idxV3, codec, inputV3); + __ vluxei8_v(idxV4, codec, inputV4); + + // 2. check wrong data + __ vor_vv(outputV1, idxV1, idxV2); + __ vor_vv(outputV2, idxV3, idxV4); + __ vor_vv(outputV1, outputV1, outputV2); + __ vmseq_vi(v0, outputV1, -1); + __ vfirst_m(failedIdx, v0); + Label NoFailure; + __ beq(failedIdx, minusOne, NoFailure); + __ vsetvli(x0, failedIdx, Assembler::e8, lmul, Assembler::mu, Assembler::tu); + __ slli(stepDst, failedIdx, 1); + __ add(stepDst, failedIdx, stepDst); + __ BIND(NoFailure); + + // 3. compute the decoded data: vr(4) => vr(3) + __ vsll_vi(idxV1, idxV1, 2); + __ vsrl_vi(outputV1, idxV2, 4); + __ vor_vv(outputV1, outputV1, idxV1); + + __ vsll_vi(idxV2, idxV2, 4); + __ vsrl_vi(outputV2, idxV3, 2); + __ vor_vv(outputV2, outputV2, idxV2); + + __ vsll_vi(idxV3, idxV3, 6); + __ vor_vv(outputV3, idxV4, idxV3); + + // segmented store encoded data in v registers back to dst: vr(3) => mem(dst) + __ vsseg3e8_v(outputV1, dst); + + // dst = dst + register_group_len_bytes * 3 + __ add(dst, dst, stepDst); + } + + /** + * int j.u.Base64.Decoder.decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL, boolean isMIME) + * + * Input arguments: + * c_rarg0 - src, source array + * c_rarg1 - sp, src start offset + * c_rarg2 - sl, src end offset + * c_rarg3 - dst, dest array + * c_rarg4 - dp, dst start offset + * c_rarg5 - isURL, Base64 or URL character set + * c_rarg6 - isMIME, Decoding MIME block + */ + address generate_base64_decodeBlock() { + + static const uint8_t fromBase64[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + }; + + static const uint8_t fromBase64URL[256] = { + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, + 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, + 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u, + 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u, + 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, + }; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "decodeBlock"); + address start = __ pc(); + __ enter(); + + Register src = c_rarg0; + Register soff = c_rarg1; + Register send = c_rarg2; + Register dst = c_rarg3; + Register doff = c_rarg4; + Register isURL = c_rarg5; + Register isMIME = c_rarg6; + + Register codec = c_rarg7; + Register dstBackup = x31; + Register length = x28; // t3, total length of src data in bytes + + Label ProcessData, Exit; + Label ProcessScalar, ScalarLoop; + + // passed in length (send - soff) is guaranteed to be > 4, + // and in this intrinsic we only process data of length in multiple of 4, + // it's not guaranteed to be multiple of 4 by java level, so do it explicitly + __ sub(length, send, soff); + __ andi(length, length, -4); + // real src/dst to process data + __ add(src, src, soff); + __ add(dst, dst, doff); + // backup of dst, used to calculate the return value at exit + __ mv(dstBackup, dst); + + // load the codec base address + __ la(codec, ExternalAddress((address) fromBase64)); + __ beqz(isURL, ProcessData); + __ la(codec, ExternalAddress((address) fromBase64URL)); + __ BIND(ProcessData); + + // vector version + if (UseRVV) { + // for MIME case, it has a default length limit of 76 which could be + // different(smaller) from (send - soff), so in MIME case, we go through + // the scalar code path directly. + __ bnez(isMIME, ScalarLoop); + + Label ProcessM1, ProcessM2; + + Register failedIdx = soff; + Register stepSrcM1 = send; + Register stepSrcM2 = doff; + Register stepDst = isURL; + Register size = x29; // t4 + Register minusOne = x30; // t5 + + __ mv(minusOne, -1); + __ mv(size, MaxVectorSize * 2); + __ mv(stepSrcM1, MaxVectorSize * 4); + __ slli(stepSrcM2, stepSrcM1, 1); + __ mv(stepDst, MaxVectorSize * 2 * 3); + + __ blt(length, stepSrcM2, ProcessM1); + + + // Assembler::m2 + __ BIND(ProcessM2); + base64_vector_decode_round(src, dst, codec, + size, stepSrcM2, stepDst, failedIdx, minusOne, + v2, v4, v6, v8, // inputs + v10, v12, v14, v16, // indexes + v18, v20, v22, // outputs + Assembler::m2); + __ sub(length, length, stepSrcM2); + + // error check + __ bne(failedIdx, minusOne, Exit); + + __ bge(length, stepSrcM2, ProcessM2); + + + // Assembler::m1 + __ BIND(ProcessM1); + __ blt(length, stepSrcM1, ProcessScalar); + + __ srli(size, size, 1); + __ srli(stepDst, stepDst, 1); + base64_vector_decode_round(src, dst, codec, + size, stepSrcM1, stepDst, failedIdx, minusOne, + v1, v2, v3, v4, // inputs + v5, v6, v7, v8, // indexes + v9, v10, v11, // outputs + Assembler::m1); + __ sub(length, length, stepSrcM1); + + // error check + __ bne(failedIdx, minusOne, Exit); + + __ BIND(ProcessScalar); + __ beqz(length, Exit); + } + + // scalar version + { + Register byte0 = soff, byte1 = send, byte2 = doff, byte3 = isURL; + Register combined32Bits = x29; // t5 + + // encoded: [byte0[5:0] : byte1[5:0] : byte2[5:0]] : byte3[5:0]] => + // plain: [byte0[5:0]+byte1[5:4] : byte1[3:0]+byte2[5:2] : byte2[1:0]+byte3[5:0]] + __ BIND(ScalarLoop); + + // load 4 bytes encoded src data + __ lbu(byte0, Address(src, 0)); + __ lbu(byte1, Address(src, 1)); + __ lbu(byte2, Address(src, 2)); + __ lbu(byte3, Address(src, 3)); + __ addi(src, src, 4); + + // get codec index and decode (ie. load from codec by index) + __ add(byte0, codec, byte0); + __ add(byte1, codec, byte1); + __ lb(byte0, Address(byte0, 0)); + __ lb(byte1, Address(byte1, 0)); + __ add(byte2, codec, byte2); + __ add(byte3, codec, byte3); + __ lb(byte2, Address(byte2, 0)); + __ lb(byte3, Address(byte3, 0)); + __ slliw(byte0, byte0, 18); + __ slliw(byte1, byte1, 12); + __ orr(byte0, byte0, byte1); + __ orr(byte0, byte0, byte3); + __ slliw(byte2, byte2, 6); + // For performance consideration, `combined32Bits` is constructed for 2 purposes at the same time, + // 1. error check below + // 2. decode below + __ orr(combined32Bits, byte0, byte2); + + // error check + __ bltz(combined32Bits, Exit); + + // store 3 bytes decoded data + __ sraiw(byte0, combined32Bits, 16); + __ sraiw(byte1, combined32Bits, 8); + __ sb(byte0, Address(dst, 0)); + __ sb(byte1, Address(dst, 1)); + __ sb(combined32Bits, Address(dst, 2)); + + __ sub(length, length, 4); + __ addi(dst, dst, 3); + // loop back + __ bnez(length, ScalarLoop); + } + + __ BIND(Exit); + __ sub(c_rarg0, dst, dstBackup); + + __ leave(); + __ ret(); + + return (address) start; + } + void adler32_process_bytes(Register buff, Register s1, Register s2, VectorRegister vtable, VectorRegister vzero, VectorRegister vbytes, VectorRegister vs1acc, VectorRegister vs2acc, Register temp0, Register temp1, Register temp2, Register temp3, @@ -5980,6 +6253,7 @@ static const int64_t right_3_bits = right_n_bits(3); if (UseBASE64Intrinsics) { StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock(); + StubRoutines::_base64_decodeBlock = generate_base64_decodeBlock(); } if (UseAdler32Intrinsics) { diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp index 6f1961758222f..42b40309d9869 100644 --- a/src/hotspot/share/gc/shared/gcLocker.cpp +++ b/src/hotspot/share/gc/shared/gcLocker.cpp @@ -33,11 +33,33 @@ #include "runtime/javaThread.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/threadSMR.hpp" +#include "utilities/ticks.hpp" volatile jint GCLocker::_jni_lock_count = 0; volatile bool GCLocker::_needs_gc = false; unsigned int GCLocker::_total_collections = 0; +// GCLockerTimingDebugLogger tracks specific timing information for GC lock waits. +class GCLockerTimingDebugLogger : public StackObj { + const char* _log_message; + Ticks _start; + +public: + GCLockerTimingDebugLogger(const char* log_message) : _log_message(log_message) { + assert(_log_message != nullptr, "GC locker debug message must be set."); + _start = Ticks::now(); + } + + ~GCLockerTimingDebugLogger() { + Log(gc, jni) log; + if (log.is_debug()) { + ResourceMark rm; // JavaThread::name() allocates to convert to UTF8 + const Tickspan elapsed_time = Ticks::now() - _start; + log.debug("%s Resumed after " UINT64_FORMAT "ms. Thread \"%s\".", _log_message, elapsed_time.milliseconds(), Thread::current()->name()); + } + } +}; + #ifdef ASSERT volatile jint GCLocker::_debug_jni_lock_count = 0; #endif @@ -110,11 +132,11 @@ void GCLocker::stall_until_clear() { if (needs_gc()) { GCLockerTracer::inc_stall_count(); log_debug_jni("Allocation failed. Thread stalled by JNI critical section."); - } - - // Wait for _needs_gc to be cleared - while (needs_gc()) { - ml.wait(); + GCLockerTimingDebugLogger logger("Thread stalled by JNI critical section."); + // Wait for _needs_gc to be cleared + while (needs_gc()) { + ml.wait(); + } } } @@ -127,16 +149,20 @@ void GCLocker::jni_lock(JavaThread* thread) { assert(!thread->in_critical(), "shouldn't currently be in a critical region"); MonitorLocker ml(JNICritical_lock); // Block entering threads if there's a pending GC request. - while (needs_gc()) { - // There's at least one thread that has not left the critical region (CR) - // completely. When that last thread (no new threads can enter CR due to the - // blocking) exits CR, it calls `jni_unlock`, which sets `_needs_gc` - // to false and wakes up all blocked threads. - // We would like to assert #threads in CR to be > 0, `_jni_lock_count > 0` - // in the code, but it's too strong; it's possible that the last thread - // has called `jni_unlock`, but not yet finished the call, e.g. initiating - // a GCCause::_gc_locker GC. - ml.wait(); + if (needs_gc()) { + log_debug_jni("Blocking thread as there is a pending GC request"); + GCLockerTimingDebugLogger logger("Thread blocked to enter critical region."); + while (needs_gc()) { + // There's at least one thread that has not left the critical region (CR) + // completely. When that last thread (no new threads can enter CR due to the + // blocking) exits CR, it calls `jni_unlock`, which sets `_needs_gc` + // to false and wakes up all blocked threads. + // We would like to assert #threads in CR to be > 0, `_jni_lock_count > 0` + // in the code, but it's too strong; it's possible that the last thread + // has called `jni_unlock`, but not yet finished the call, e.g. initiating + // a GCCause::_gc_locker GC. + ml.wait(); + } } thread->enter_critical(); _jni_lock_count++; @@ -148,6 +174,7 @@ void GCLocker::jni_unlock(JavaThread* thread) { MutexLocker mu(JNICritical_lock); _jni_lock_count--; decrement_debug_jni_lock_count(); + log_debug_jni("Thread exiting critical region."); thread->exit_critical(); if (needs_gc() && !is_active_internal()) { // We're the last thread out. Request a GC. @@ -161,7 +188,7 @@ void GCLocker::jni_unlock(JavaThread* thread) { { // Must give up the lock while at a safepoint MutexUnlocker munlock(JNICritical_lock); - log_debug_jni("Performing GC after exiting critical section."); + log_debug_jni("Last thread exiting. Performing GC after exiting critical section."); Universe::heap()->collect(GCCause::_gc_locker); } _needs_gc = false; diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp index ca119c1f8c35a..680e6bee1ee68 100644 --- a/src/hotspot/share/jfr/jni/jfrJniMethod.hpp +++ b/src/hotspot/share/jfr/jni/jfrJniMethod.hpp @@ -113,7 +113,7 @@ jobject JNICALL jfr_get_event_writer(JNIEnv* env, jclass jvm); jobject JNICALL jfr_new_event_writer(JNIEnv* env, jclass jvm); -jboolean JNICALL jfr_event_writer_flush(JNIEnv* env, jclass jvm, jobject writer, jint used_size, jint requested_size); +void JNICALL jfr_event_writer_flush(JNIEnv* env, jclass jvm, jobject writer, jint used_size, jint requested_size); jlong JNICALL jfr_commit(JNIEnv* env, jclass cls, jlong next_position); void JNICALL jfr_flush(JNIEnv* env, jclass jvm); diff --git a/src/hotspot/share/jfr/support/jfrSymbolTable.cpp b/src/hotspot/share/jfr/support/jfrSymbolTable.cpp index ffb54f4d0caee..e3c34e46a1a4a 100644 --- a/src/hotspot/share/jfr/support/jfrSymbolTable.cpp +++ b/src/hotspot/share/jfr/support/jfrSymbolTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -23,11 +23,10 @@ */ #include "precompiled.hpp" -#include "classfile/javaClasses.inline.hpp" #include "classfile/classLoaderData.hpp" +#include "classfile/javaClasses.hpp" #include "jfr/support/jfrSymbolTable.hpp" -#include "oops/instanceKlass.hpp" -#include "oops/oop.inline.hpp" +#include "oops/klass.hpp" #include "oops/symbol.hpp" #include "runtime/mutexLocker.hpp" @@ -200,7 +199,7 @@ traceid JfrSymbolTable::bootstrap_name(bool leakp) { traceid JfrSymbolTable::mark(const Symbol* sym, bool leakp /* false */) { assert(sym != nullptr, "invariant"); - return mark((uintptr_t)sym->identity_hash(), sym, leakp); + return mark(sym->identity_hash(), sym, leakp); } traceid JfrSymbolTable::mark(uintptr_t hash, const Symbol* sym, bool leakp) { @@ -236,59 +235,25 @@ traceid JfrSymbolTable::mark(uintptr_t hash, const char* str, bool leakp) { } /* -* hidden classes symbol is the external name + -* the address of its InstanceKlass slash appended: -* java.lang.invoke.LambdaForm$BMH/22626602 -* -* caller needs ResourceMark -*/ - -uintptr_t JfrSymbolTable::hidden_klass_name_hash(const InstanceKlass* ik) { - assert(ik != nullptr, "invariant"); - assert(ik->is_hidden(), "invariant"); - const oop mirror = ik->java_mirror_no_keepalive(); - assert(mirror != nullptr, "invariant"); - return (uintptr_t)mirror->identity_hash(); -} - -static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t hash) { - assert(ik != nullptr, "invariant"); - assert(ik->is_hidden(), "invariant"); - assert(hash != 0, "invariant"); - char* hidden_symbol = nullptr; - const oop mirror = ik->java_mirror_no_keepalive(); - assert(mirror != nullptr, "invariant"); - char hash_buf[40]; - os::snprintf_checked(hash_buf, sizeof(hash_buf), "/" UINTX_FORMAT, hash); - const size_t hash_len = strlen(hash_buf); - const size_t result_len = ik->name()->utf8_length(); - hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1); - ik->name()->as_klass_external_name(hidden_symbol, (int)result_len + 1); - assert(strlen(hidden_symbol) == result_len, "invariant"); - strcpy(hidden_symbol + result_len, hash_buf); - assert(strlen(hidden_symbol) == result_len + hash_len, "invariant"); - return hidden_symbol; -} - -bool JfrSymbolTable::is_hidden_klass(const Klass* k) { + * The hidden class symbol is the external name with the + * address of its Klass slash appended. + * + * "java.lang.invoke.LambdaForm$DMH/0x0000000037144c00" + * + * Caller needs ResourceMark. + */ +traceid JfrSymbolTable::mark_hidden_klass_name(const Klass* k, bool leakp) { assert(k != nullptr, "invariant"); - return k->is_instance_klass() && ((const InstanceKlass*)k)->is_hidden(); -} - -traceid JfrSymbolTable::mark_hidden_klass_name(const InstanceKlass* ik, bool leakp) { - assert(ik != nullptr, "invariant"); - assert(ik->is_hidden(), "invariant"); - const uintptr_t hash = hidden_klass_name_hash(ik); - const char* const hidden_symbol = create_hidden_klass_symbol(ik, hash); - return mark(hash, hidden_symbol, leakp); + assert(k->is_hidden(), "invariant"); + const uintptr_t hash = k->name()->identity_hash(); + return mark(hash, k->external_name(), leakp); } traceid JfrSymbolTable::mark(const Klass* k, bool leakp) { assert(k != nullptr, "invariant"); traceid symbol_id = 0; - if (is_hidden_klass(k)) { - assert(k->is_instance_klass(), "invariant"); - symbol_id = mark_hidden_klass_name((const InstanceKlass*)k, leakp); + if (k->is_hidden()) { + symbol_id = mark_hidden_klass_name(k, leakp); } else { Symbol* const sym = k->name(); if (sym != nullptr) { diff --git a/src/hotspot/share/jfr/support/jfrSymbolTable.hpp b/src/hotspot/share/jfr/support/jfrSymbolTable.hpp index a1951e52cc376..8cefa287c8ade 100644 --- a/src/hotspot/share/jfr/support/jfrSymbolTable.hpp +++ b/src/hotspot/share/jfr/support/jfrSymbolTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -100,16 +100,13 @@ class JfrSymbolTable : public JfrCHeapObj { traceid mark(const Symbol* sym, bool leakp = false); traceid mark(const char* str, bool leakp = false); traceid mark(uintptr_t hash, const char* str, bool leakp); + traceid mark_hidden_klass_name(const Klass* k, bool leakp); traceid bootstrap_name(bool leakp); bool has_entries() const { return has_symbol_entries() || has_string_entries(); } bool has_symbol_entries() const { return _symbol_list != nullptr; } bool has_string_entries() const { return _string_list != nullptr; } - traceid mark_hidden_klass_name(const InstanceKlass* k, bool leakp); - bool is_hidden_klass(const Klass* k); - uintptr_t hidden_klass_name_hash(const InstanceKlass* ik); - // hashtable(s) callbacks void on_link(const SymbolEntry* entry); bool on_equals(uintptr_t hash, const SymbolEntry* entry); diff --git a/src/hotspot/share/memory/classLoaderMetaspace.cpp b/src/hotspot/share/memory/classLoaderMetaspace.cpp index 3315c15e67749..08bf42da8e3d9 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.cpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -130,9 +130,10 @@ MetaWord* ClassLoaderMetaspace::expand_and_allocate(size_t word_size, Metaspace: // Prematurely returns a metaspace allocation to the _block_freelists // because it is not needed anymore. -void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { +void ClassLoaderMetaspace::deallocate(MetaWord* ptr, size_t word_size) { MutexLocker fcl(lock(), Mutex::_no_safepoint_check_flag); - if (Metaspace::using_class_space() && is_class) { + const bool is_class = Metaspace::using_class_space() && Metaspace::is_in_class_space(ptr); + if (is_class) { class_space_arena()->deallocate(ptr, word_size); } else { non_class_space_arena()->deallocate(ptr, word_size); diff --git a/src/hotspot/share/memory/classLoaderMetaspace.hpp b/src/hotspot/share/memory/classLoaderMetaspace.hpp index 525c63dde3e4b..176b8c5082acf 100644 --- a/src/hotspot/share/memory/classLoaderMetaspace.hpp +++ b/src/hotspot/share/memory/classLoaderMetaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, 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 @@ -92,7 +92,7 @@ class ClassLoaderMetaspace : public CHeapObj { // Prematurely returns a metaspace allocation to the _block_freelists // because it is not needed anymore. - void deallocate(MetaWord* ptr, size_t word_size, bool is_class); + void deallocate(MetaWord* ptr, size_t word_size); // Update statistics. This walks all in-use chunks. void add_to_statistics(metaspace::ClmsStats* out) const; diff --git a/src/hotspot/share/memory/metadataFactory.hpp b/src/hotspot/share/memory/metadataFactory.hpp index 14cac8dc191a7..f5935c588d79b 100644 --- a/src/hotspot/share/memory/metadataFactory.hpp +++ b/src/hotspot/share/memory/metadataFactory.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2024, 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 @@ -54,7 +54,7 @@ class MetadataFactory : AllStatic { assert(loader_data != nullptr, "shouldn't pass null"); assert(!data->is_shared(), "cannot deallocate array in shared spaces"); int size = data->size(); - loader_data->metaspace_non_null()->deallocate((MetaWord*)data, size, false); + loader_data->metaspace_non_null()->deallocate((MetaWord*)data, size); } } @@ -68,7 +68,6 @@ class MetadataFactory : AllStatic { assert(!md->on_stack(), "can't deallocate things on stack"); assert(!md->is_shared(), "cannot deallocate if in shared spaces"); md->deallocate_contents(loader_data); - bool is_klass = md->is_klass(); // Call the destructor. This is currently used for MethodData which has a member // that needs to be destructed to release resources. Most Metadata derived classes have noop // destructors and/or cleanup using deallocate_contents. @@ -76,7 +75,7 @@ class MetadataFactory : AllStatic { // or volatile so we can call the destructor of the type T points to. using U = std::remove_cv_t; md->~U(); - loader_data->metaspace_non_null()->deallocate((MetaWord*)md, size, is_klass); + loader_data->metaspace_non_null()->deallocate((MetaWord*)md, size); } } }; diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index ad51f5ab7b6db..9b3b36f8be02e 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -538,6 +538,8 @@ void MetaspaceGC::compute_new_size() { ////// Metaspace methods ///// const MetaspaceTracer* Metaspace::_tracer = nullptr; +const void* Metaspace::_class_space_start = nullptr; +const void* Metaspace::_class_space_end = nullptr; bool Metaspace::initialized() { return metaspace::MetaspaceContext::context_nonclass() != nullptr @@ -570,6 +572,8 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { "wrong alignment"); MetaspaceContext::initialize_class_space_context(rs); + _class_space_start = rs.base(); + _class_space_end = rs.end(); } // Returns true if class space has been setup (initialize_class_space). @@ -979,17 +983,15 @@ void Metaspace::purge(bool classes_unloaded) { MetaspaceCriticalAllocation::process(); } -bool Metaspace::contains(const void* ptr) { - if (MetaspaceShared::is_in_shared_metaspace(ptr)) { - return true; - } - return contains_non_shared(ptr); -} -bool Metaspace::contains_non_shared(const void* ptr) { - if (using_class_space() && VirtualSpaceList::vslist_class()->contains((MetaWord*)ptr)) { - return true; - } +// Returns true if pointer points into one of the metaspace regions, or +// into the class space. +bool Metaspace::is_in_shared_metaspace(const void* ptr) { + return MetaspaceShared::is_in_shared_metaspace(ptr); +} +// Returns true if pointer points into one of the non-class-space metaspace regions. +bool Metaspace::is_in_nonclass_metaspace(const void* ptr) { return VirtualSpaceList::vslist_nonclass()->contains((MetaWord*)ptr); } + diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp index a90ff77564763..6de901b25d12b 100644 --- a/src/hotspot/share/memory/metaspace.hpp +++ b/src/hotspot/share/memory/metaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2021 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -64,6 +64,10 @@ class Metaspace : public AllStatic { static const MetaspaceTracer* _tracer; + // For quick pointer testing: extent of class space; nullptr if no class space. + static const void* _class_space_start; + static const void* _class_space_end; + static bool _initialized; public: @@ -113,8 +117,32 @@ class Metaspace : public AllStatic { static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size, MetaspaceObj::Type type); - static bool contains(const void* ptr); - static bool contains_non_shared(const void* ptr); + // Returns true if the pointer points into class space, non-class metaspace, or the + // metadata portion of the CDS archive. + static bool contains(const void* ptr) { + return is_in_shared_metaspace(ptr) || // in cds + is_in_class_space(ptr) || // in class space + is_in_nonclass_metaspace(ptr); // in one of the non-class regions? + } + + // Returns true if the pointer points into class space or into non-class metaspace + static bool contains_non_shared(const void* ptr) { + return is_in_class_space(ptr) || // in class space + is_in_nonclass_metaspace(ptr); // in one of the non-class regions? + } + + // Returns true if pointer points into the CDS klass region. + static bool is_in_shared_metaspace(const void* ptr); + + // Returns true if pointer points into one of the non-class-space metaspace regions. + static bool is_in_nonclass_metaspace(const void* ptr); + + // Returns true if pointer points into class space, false if it doesn't or if + // there is no class space. Class space is a contiguous region, which is why + // two address comparisons are enough. + static inline bool is_in_class_space(const void* ptr) { + return ptr < _class_space_end && ptr >= _class_space_start; + } // Free empty virtualspaces static void purge(bool classes_unloaded); diff --git a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java index b130ca8e031e5..2cf0a5e4443cb 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/MemoryLayout.java @@ -574,7 +574,9 @@ public sealed interface MemoryLayout *

* For any given dynamic argument {@code x_i}, it must be that {@code 0 <= x_i < size_i}, * where {@code size_i} is the size of the open path element associated with {@code x_i}. - * Otherwise, the returned method handle throws {@link IndexOutOfBoundsException}. + * Otherwise, the returned method handle throws {@link IndexOutOfBoundsException}. Moreover, + * the value of {@code b} must be such that the computation for {@code offset} does not overflow, + * or the returned method handle throws {@link ArithmeticException}. * * @apiNote The returned method handle can be used to compute a layout offset, * similarly to {@link #byteOffset(PathElement...)}, but more flexibly, as diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java index 82f6b358a0752..8f171fd8bfbc9 100644 --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java @@ -475,7 +475,7 @@ * MemorySegment ptr = null; * try (Arena arena = Arena.ofConfined()) { * MemorySegment z = segment.get(ValueLayout.ADDRESS, ...); // size = 0, scope = always alive - * ptr = z.reinterpret(16, arena, null); // size = 4, scope = arena.scope() + * ptr = z.reinterpret(16, arena, null); // size = 16, scope = arena.scope() * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // ok * } * int x = ptr.getAtIndex(ValueLayout.JAVA_INT, 3); // throws IllegalStateException diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java index 0cb5eb88079e4..35464d6ec91d0 100644 --- a/src/java.base/share/classes/java/text/CompactNumberFormat.java +++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java @@ -652,16 +652,16 @@ private StringBuf format(double number, StringBuf result, double val = getNumberValue(number, divisor); if (checkIncrement(val, compactDataIndex, divisor)) { divisor = (Long) divisors.get(++compactDataIndex); - val = getNumberValue(number, divisor); } + roundedNumber = roundedNumber / divisor; + decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits()); + val = decimalFormat.getDigitList().getDouble(); String prefix = getAffix(false, true, isNegative, compactDataIndex, val); String suffix = getAffix(false, false, isNegative, compactDataIndex, val); if (!prefix.isEmpty() || !suffix.isEmpty()) { appendPrefix(result, prefix, delegate); if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) { - roundedNumber = roundedNumber / divisor; - decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits()); decimalFormat.subformatNumber(result, delegate, isNegative, false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), getMaximumFractionDigits(), getMinimumFractionDigits()); @@ -734,31 +734,28 @@ private StringBuf format(long number, StringBuf result, FieldDelegate delegate) double val = getNumberValue(number, divisor); if (checkIncrement(val, compactDataIndex, divisor)) { divisor = (Long) divisors.get(++compactDataIndex); - val = getNumberValue(number, divisor); } + var noFraction = number % divisor == 0; + if (noFraction) { + number = number / divisor; + decimalFormat.setDigitList(number, isNegative, 0); + } else { + // To avoid truncation of fractional part store + // the value in double and follow double path instead of + // long path + double dNumber = (double) number / divisor; + decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits()); + } + val = decimalFormat.getDigitList().getDouble(); String prefix = getAffix(false, true, isNegative, compactDataIndex, val); String suffix = getAffix(false, false, isNegative, compactDataIndex, val); if (!prefix.isEmpty() || !suffix.isEmpty()) { appendPrefix(result, prefix, delegate); if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) { - if ((number % divisor == 0)) { - number = number / divisor; - decimalFormat.setDigitList(number, isNegative, 0); - decimalFormat.subformatNumber(result, delegate, - isNegative, true, getMaximumIntegerDigits(), - getMinimumIntegerDigits(), getMaximumFractionDigits(), - getMinimumFractionDigits()); - } else { - // To avoid truncation of fractional part store - // the value in double and follow double path instead of - // long path - double dNumber = (double) number / divisor; - decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits()); - decimalFormat.subformatNumber(result, delegate, - isNegative, false, getMaximumIntegerDigits(), - getMinimumIntegerDigits(), getMaximumFractionDigits(), - getMinimumFractionDigits()); - } + decimalFormat.subformatNumber(result, delegate, + isNegative, noFraction, getMaximumIntegerDigits(), + getMinimumIntegerDigits(), getMaximumFractionDigits(), + getMinimumFractionDigits()); appendSuffix(result, suffix, delegate); } } else { @@ -833,15 +830,15 @@ private StringBuf format(BigDecimal number, StringBuf result, double val = getNumberValue(number.doubleValue(), divisor.doubleValue()); if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) { divisor = divisors.get(++compactDataIndex); - val = getNumberValue(number.doubleValue(), divisor.doubleValue()); } + number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode()); + decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits()); + val = decimalFormat.getDigitList().getDouble(); String prefix = getAffix(false, true, isNegative, compactDataIndex, val); String suffix = getAffix(false, false, isNegative, compactDataIndex, val); if (!prefix.isEmpty() || !suffix.isEmpty()) { appendPrefix(result, prefix, delegate); if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) { - number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode()); - decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits()); decimalFormat.subformatNumber(result, delegate, isNegative, false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), getMaximumFractionDigits(), getMinimumFractionDigits()); @@ -904,34 +901,30 @@ private StringBuf format(BigInteger number, StringBuf result, double val = getNumberValue(number.doubleValue(), divisor.doubleValue()); if (checkIncrement(val, compactDataIndex, divisor.doubleValue())) { divisor = divisors.get(++compactDataIndex); - val = getNumberValue(number.doubleValue(), divisor.doubleValue()); } + var noFraction = number.mod(new BigInteger(divisor.toString())) + .compareTo(BigInteger.ZERO) == 0; + if (noFraction) { + number = number.divide(new BigInteger(divisor.toString())); + decimalFormat.setDigitList(number, isNegative, 0); + } else { + // To avoid truncation of fractional part store the value in + // BigDecimal and follow BigDecimal path instead of + // BigInteger path + BigDecimal nDecimal = new BigDecimal(number) + .divide(new BigDecimal(divisor.toString()), getRoundingMode()); + decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits()); + } + val = decimalFormat.getDigitList().getDouble(); String prefix = getAffix(false, true, isNegative, compactDataIndex, val); String suffix = getAffix(false, false, isNegative, compactDataIndex, val); if (!prefix.isEmpty() || !suffix.isEmpty()) { appendPrefix(result, prefix, delegate); if (!placeHolderPatterns.get(compactDataIndex).get(val).isEmpty()) { - if (number.mod(new BigInteger(divisor.toString())) - .compareTo(BigInteger.ZERO) == 0) { - number = number.divide(new BigInteger(divisor.toString())); - - decimalFormat.setDigitList(number, isNegative, 0); - decimalFormat.subformatNumber(result, delegate, - isNegative, true, getMaximumIntegerDigits(), - getMinimumIntegerDigits(), getMaximumFractionDigits(), - getMinimumFractionDigits()); - } else { - // To avoid truncation of fractional part store the value in - // BigDecimal and follow BigDecimal path instead of - // BigInteger path - BigDecimal nDecimal = new BigDecimal(number) - .divide(new BigDecimal(divisor.toString()), getRoundingMode()); - decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits()); - decimalFormat.subformatNumber(result, delegate, - isNegative, false, getMaximumIntegerDigits(), - getMinimumIntegerDigits(), getMaximumFractionDigits(), - getMinimumFractionDigits()); - } + decimalFormat.subformatNumber(result, delegate, + isNegative, noFraction, getMaximumIntegerDigits(), + getMinimumIntegerDigits(), getMaximumFractionDigits(), + getMinimumFractionDigits()); appendSuffix(result, suffix, delegate); } } else { diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index c04abbd042f57..b23c5f360bf05 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -1799,6 +1799,14 @@ void setDigitList(Number number, boolean isNegative, int maxDigits) { } } + /** + * {@return the {@code DigitList} used by this {@code DecimalFormat} instance} + * Declared as package-private, intended for {@code CompactNumberFormat}. + */ + DigitList getDigitList() { + return digitList; + } + // ======== End fast-path formatting logic for double ========================= /** diff --git a/src/java.base/share/classes/java/time/LocalDateTime.java b/src/java.base/share/classes/java/time/LocalDateTime.java index d14afb7d78f9a..cea9123258c6b 100644 --- a/src/java.base/share/classes/java/time/LocalDateTime.java +++ b/src/java.base/share/classes/java/time/LocalDateTime.java @@ -1966,10 +1966,17 @@ public int hashCode() { @Override public String toString() { var buf = new StringBuilder(29); + formatTo(buf); + return buf.toString(); + } + + /** + * Prints the toString result to the given buf, avoiding extra string allocations. + */ + void formatTo(StringBuilder buf) { date.formatTo(buf); buf.append('T'); time.formatTo(buf); - return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/OffsetDateTime.java b/src/java.base/share/classes/java/time/OffsetDateTime.java index bd75fffb24e55..fd8355e36cda5 100644 --- a/src/java.base/share/classes/java/time/OffsetDateTime.java +++ b/src/java.base/share/classes/java/time/OffsetDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -1923,7 +1923,10 @@ public int hashCode() { */ @Override public String toString() { - return dateTime.toString() + offset.toString(); + var offsetStr = offset.toString(); + var buf = new StringBuilder(29 + offsetStr.length()); + dateTime.formatTo(buf); + return buf.append(offsetStr).toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/OffsetTime.java b/src/java.base/share/classes/java/time/OffsetTime.java index b9fe5e533d80e..1f6feb4180fc5 100644 --- a/src/java.base/share/classes/java/time/OffsetTime.java +++ b/src/java.base/share/classes/java/time/OffsetTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -1398,7 +1398,10 @@ public int hashCode() { */ @Override public String toString() { - return time.toString() + offset.toString(); + var offsetStr = offset.toString(); + var buf = new StringBuilder(18 + offsetStr.length()); + time.formatTo(buf); + return buf.append(offsetStr).toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/ZonedDateTime.java b/src/java.base/share/classes/java/time/ZonedDateTime.java index 555392cf513ce..b1426efc914a4 100644 --- a/src/java.base/share/classes/java/time/ZonedDateTime.java +++ b/src/java.base/share/classes/java/time/ZonedDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -2214,11 +2214,20 @@ public int hashCode() { */ @Override // override for Javadoc public String toString() { - String str = dateTime.toString() + offset.toString(); + var offsetStr = offset.toString(); + var zoneStr = (String) null; + int length = 29 + offsetStr.length(); if (offset != zone) { - str += '[' + zone.toString() + ']'; + zoneStr = zone.toString(); + length += zoneStr.length() + 2; } - return str; + var buf = new StringBuilder(length); + dateTime.formatTo(buf); + buf.append(offsetStr); + if (zoneStr != null) { + buf.append('[').append(zoneStr).append(']'); + } + return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java index 956a5c75875ab..dc995589472b1 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java @@ -66,7 +66,7 @@ public class LayoutPath { private static final MethodHandle MH_SLICE_LAYOUT; private static final MethodHandle MH_CHECK_ENCL_LAYOUT; private static final MethodHandle MH_SEGMENT_RESIZE; - private static final MethodHandle MH_ADD; + private static final MethodHandle MH_ADD_EXACT; static { try { @@ -81,7 +81,7 @@ public class LayoutPath { MethodType.methodType(void.class, MemorySegment.class, long.class, MemoryLayout.class)); MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment", MethodType.methodType(MemorySegment.class, MemorySegment.class)); - MH_ADD = lookup.findStatic(Long.class, "sum", + MH_ADD_EXACT = lookup.findStatic(Math.class, "addExact", MethodType.methodType(long.class, long.class, long.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); @@ -244,15 +244,18 @@ private static long addScaledOffset(long base, long index, long stride, long bou } public MethodHandle offsetHandle() { - MethodHandle mh = MethodHandles.insertArguments(MH_ADD, 0, offset); + MethodHandle mh = MH_ADD_EXACT; for (int i = strides.length - 1; i >= 0; i--) { MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i], bounds[i]); - // (J, ...) -> J to (J, J, ...) -> J - // i.e. new coord is prefixed. Last coord will correspond to innermost layout - mh = MethodHandles.collectArguments(mh, 0, collector); - } - - return mh; + // (J, J, ...) -> J to (J, J, J, ...) -> J + // 1. the leading argument is the base offset (externally provided). + // 2. index arguments are added. The last index correspond to the innermost layout. + // 3. overflow can only occur at the outermost layer, due to the final addition with the base offset. + // This is because the layout API ensures (by construction) that all offsets generated from layout paths + // are always < Long.MAX_VALUE. + mh = MethodHandles.collectArguments(mh, 1, collector); + } + return MethodHandles.insertArguments(mh, 1, offset); } private MemoryLayout rootLayout() { diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index dbd76e07d4f46..297c77bc106c7 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -59,7 +59,6 @@ import java.util.StringTokenizer; import java.util.jar.JarFile; import java.util.zip.CRC32; -import java.util.zip.ZipEntry; import java.util.jar.JarEntry; import java.util.jar.Manifest; import java.util.jar.Attributes; @@ -439,27 +438,38 @@ private synchronized Loader getLoader(int index) { continue; } // Otherwise, create a new Loader for the URL. - Loader loader; + Loader loader = null; + final URL[] loaderClassPathURLs; try { loader = getLoader(url); // If the loader defines a local class path then add the // URLs as the next URLs to be opened. - URL[] urls = loader.getClassPath(); - if (urls != null) { - push(urls); - } + loaderClassPathURLs = loader.getClassPath(); } catch (IOException e) { - // Silently ignore for now... + // log the error and close the unusable loader (if any) + if (DEBUG) { + System.err.println("Failed to construct a loader or construct its" + + " local classpath for " + url + ", cause:" + e); + } + if (loader != null) { + closeQuietly(loader); + } continue; } catch (SecurityException se) { - // Always silently ignore. The context, if there is one, that - // this URLClassPath was given during construction will never - // have permission to access the URL. + // log the error and close the unusable loader (if any). + // The context, if there is one, that this URLClassPath was + // given during construction will never have permission to access the URL. if (DEBUG) { System.err.println("Failed to access " + url + ", " + se ); } + if (loader != null) { + closeQuietly(loader); + } continue; } + if (loaderClassPathURLs != null) { + push(loaderClassPathURLs); + } // Finally, add the Loader to the search path. loaders.add(loader); lmap.put(urlNoFragString, loader); @@ -467,6 +477,17 @@ private synchronized Loader getLoader(int index) { return loaders.get(index); } + // closes the given loader and ignores any IOException that may occur during close + private static void closeQuietly(final Loader loader) { + try { + loader.close(); + } catch (IOException ioe) { + if (DEBUG) { + System.err.println("ignoring exception " + ioe + " while closing loader " + loader); + } + } + } + /* * Returns the Loader for the specified base URL. */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 681c0e5195838..a2414da455955 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -1058,7 +1058,7 @@ public Object createValue(UIDefaults table) { "KP_RIGHT", "selectNextColumn", "shift RIGHT", "selectNextColumnExtendSelection", "shift KP_RIGHT", "selectNextColumnExtendSelection", - "ctrl shift RIGHT", "selectNextColumnExtendSelection", + "ctrl shift RIGHT", "selectLastColumnExtendSelection", "ctrl shift KP_RIGHT", "selectNextColumnExtendSelection", "ctrl RIGHT", "selectNextColumnChangeLead", "ctrl KP_RIGHT", "selectNextColumnChangeLead", @@ -1066,7 +1066,7 @@ public Object createValue(UIDefaults table) { "KP_LEFT", "selectPreviousColumn", "shift LEFT", "selectPreviousColumnExtendSelection", "shift KP_LEFT", "selectPreviousColumnExtendSelection", - "ctrl shift LEFT", "selectPreviousColumnExtendSelection", + "ctrl shift LEFT", "selectFirstColumnExtendSelection", "ctrl shift KP_LEFT", "selectPreviousColumnExtendSelection", "ctrl LEFT", "selectPreviousColumnChangeLead", "ctrl KP_LEFT", "selectPreviousColumnChangeLead", diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index ecff358ce22c6..6141ce46fe8fc 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -478,7 +478,8 @@ doclet.usage.author.description=\ Include @author paragraphs doclet.usage.docfilessubdirs.description=\ - Recursively copy doc-file subdirectories + Enables deep copying of 'doc-files' directories. Subdirectories and all\n\ + contents are recursively copied to the destination doclet.usage.splitindex.description=\ Split index into one file per letter @@ -515,7 +516,7 @@ doclet.usage.html5.description=\ doclet.usage.footer.parameters=\ doclet.usage.footer.description=\ - Include footer text for each page + This option is no longer supported and reports a warning doclet.usage.top.parameters=\ @@ -553,7 +554,7 @@ doclet.usage.link-platform-properties.description=\ doclet.usage.excludedocfilessubdir.parameters=\ ,,... doclet.usage.excludedocfilessubdir.description=\ - Exclude any doc-files subdirectories with given name.\n\ + Exclude any 'doc-files' subdirectories with given name.\n\ ':' can also be used anywhere in the argument as a separator. doclet.usage.group.parameters=\ @@ -614,7 +615,7 @@ doclet.usage.nooverview.description=\ Do not generate overview pages doclet.usage.serialwarn.description=\ - Generate warning about @serial tag + Reports compile-time warnings for missing '@serial' tags doclet.usage.since.parameters=\ (,)* @@ -629,7 +630,7 @@ doclet.usage.since-label.description=\ doclet.usage.tag.parameters=\ ::

doclet.usage.tag.description=\ - Specify single argument custom tags + Specifies a custom tag with a single argument doclet.usage.taglet.description=\ The fully qualified name of Taglet to register @@ -654,7 +655,8 @@ doclet.usage.javafx.description=\ doclet.usage.helpfile.parameters=\ doclet.usage.helpfile.description=\ - Include file that help link links to + Specifies a file containing the text that will be displayed when the\n\ + help link in the navigation bar is clicked doclet.usage.linksource.description=\ Generate source in HTML @@ -691,7 +693,8 @@ doclet.usage.override-methods.description=\ The default is 'detail'. doclet.usage.allow-script-in-comments.description=\ - Allow JavaScript in options and comments + Allow JavaScript in documentation comments, and options\n\ + whose value is html-code doclet.usage.xdocrootparent.parameters=\ diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOptions.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOptions.java index 17bf628114a67..39d9f2834e183 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOptions.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOptions.java @@ -678,7 +678,7 @@ interface ShowHelper { /** * Show command-line help for the extended options, as requested by - * the {@code --help-extended} option and its aliases. + * the {@code --help-extra} option and its aliases. */ void Xusage(); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties index 7db420e46d587..8178380972509 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties @@ -67,7 +67,7 @@ main.opt.private.desc=\ main.opt.show.members.arg=\ main.opt.show.members.desc=\ - Specifies which members (fields, methods, etc.) will be\n\ + Specifies which members (fields, methods, or constructors) will be\n\ documented, where value can be one of "public", "protected",\n\ "package" or "private". The default is "protected", which will\n\ show public and protected members, "public" will show only\n\ @@ -87,7 +87,7 @@ main.opt.show.types.desc=\ main.opt.show.packages.arg=\ main.opt.show.packages.desc=\ - Specifies which module's packages will be documented. Possible\n\ + Specifies which module packages will be documented. Possible\n\ values are "exported" or "all" packages. main.opt.show.module.contents.arg=\ @@ -97,7 +97,7 @@ main.opt.show.module.contents.desc=\ declarations. Possible values are "api" or "all". main.opt.expand.requires.arg=\ - + (transitive|all) main.opt.expand.requires.desc=\ Instructs the tool to expand the set of modules to be\n\ documented. By default, only the modules given explicitly on\n\ diff --git a/test/hotspot/gtest/metaspace/test_metaspace_misc.cpp b/test/hotspot/gtest/metaspace/test_metaspace_misc.cpp index 6a2282960e6f6..4d0a3e0774f50 100644 --- a/test/hotspot/gtest/metaspace/test_metaspace_misc.cpp +++ b/test/hotspot/gtest/metaspace/test_metaspace_misc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -67,7 +67,7 @@ TEST_VM(metaspace, misc_max_alloc_size) { ASSERT_NOT_NULL(p); } // And also, successfully deallocate it. - cld->metaspace_non_null()->deallocate(p, sz, in_class_space); + cld->metaspace_non_null()->deallocate(p, sz); } } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index bbf594f3bc70b..697fe82187ca3 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -375,7 +375,7 @@ java/awt/Modal/MultipleDialogs/MultipleDialogs3Test.java 8198665 macosx-all java/awt/Modal/MultipleDialogs/MultipleDialogs4Test.java 8198665 macosx-all java/awt/Modal/MultipleDialogs/MultipleDialogs5Test.java 8198665 macosx-all java/awt/Mouse/EnterExitEvents/DragWindowOutOfFrameTest.java 8177326 macosx-all -java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java 8005021,8332158 macosx-all,linux-x64 +java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java 8005021 macosx-all java/awt/Mouse/EnterExitEvents/FullscreenEnterEventTest.java 8051455 macosx-all java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersUnitTest_Standard.java 7124407,8302787 macosx-all,windows-all java/awt/Mouse/RemovedComponentMouseListener/RemovedComponentMouseListener.java 8157170 macosx-all diff --git a/test/jdk/java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java b/test/jdk/java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java index 51e5dd353a98d..2918a6a5e0a89 100644 --- a/test/jdk/java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java +++ b/test/jdk/java/awt/Mouse/EnterExitEvents/ResizingFrameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,12 +27,19 @@ * @bug 7154048 * @summary Programmatically resized window does not receive mouse entered/exited events * @author alexandr.scherbatiy area=awt.event + * @library /test/lib + * @build jdk.test.lib.Platform * @run main ResizingFrameTest */ -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import jdk.test.lib.Platform; public class ResizingFrameTest { @@ -41,6 +48,9 @@ public class ResizingFrameTest { private static JFrame frame; public static void main(String[] args) throws Exception { + if (Platform.isOnWayland()) { + return; + } Robot robot = new Robot(); robot.setAutoDelay(50); diff --git a/test/jdk/java/foreign/TestLayoutPaths.java b/test/jdk/java/foreign/TestLayoutPaths.java index 414eb4117ced7..6729fbf823682 100644 --- a/test/jdk/java/foreign/TestLayoutPaths.java +++ b/test/jdk/java/foreign/TestLayoutPaths.java @@ -332,6 +332,14 @@ public void testOffsetHandleOOBIndex(MemoryLayout layout, PathElement[] pathElem } } + @Test(dataProvider = "testLayouts", expectedExceptions = ArithmeticException.class) + public void testOffsetHandleOverflow(MemoryLayout layout, PathElement[] pathElements, long[] indexes, + long expectedByteOffset) throws Throwable { + MethodHandle byteOffsetHandle = layout.byteOffsetHandle(pathElements); + byteOffsetHandle = byteOffsetHandle.asSpreader(long[].class, indexes.length); + byteOffsetHandle.invoke(Long.MAX_VALUE, indexes); + } + @Test(dataProvider = "testLayouts") public void testVarHandleBadSegment(MemoryLayout layout, PathElement[] pathElements, long[] indexes, long expectedByteOffset) throws Throwable { diff --git a/test/jdk/java/net/URLClassLoader/JarLoaderCloseTest.java b/test/jdk/java/net/URLClassLoader/JarLoaderCloseTest.java new file mode 100644 index 0000000000000..469a682e0392a --- /dev/null +++ b/test/jdk/java/net/URLClassLoader/JarLoaderCloseTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/* + * @test + * @bug 8338445 + * @summary verify that the jdk.internal.loader.URLClassPath closes the JarFile + * instances that it no longer uses for loading + * @library /test/lib + * @build jdk.test.lib.util.JarUtils + * @comment This test expects MalformedURLException for some specific URLs. + * We use othervm to prevent interference from other tests which + * might have installed custom URLStreamHandler(s) + * @run junit/othervm JarLoaderCloseTest + */ +public class JarLoaderCloseTest { + + private static final String RESOURCE_NAME = "foo-bar.txt"; + private static final String RESOURCE_CONTENT = "Hello world"; + private static final Path TEST_SCRATCH_DIR = Path.of("."); + + @BeforeAll + static void beforeAll() throws Exception { + // create a file which will be added to the JAR file that gets tested + Files.writeString(TEST_SCRATCH_DIR.resolve(RESOURCE_NAME), RESOURCE_CONTENT); + } + + /* + * Creates a JAR file with a manifest which has a Class-Path entry value with malformed URLs. + * Then uses a URLClassLoader backed by the JAR file in its classpath, loads some resource, + * closes the URLClassLoader and then expects that the underlying JAR file can be deleted + * from the filesystem. + */ + @ParameterizedTest + @ValueSource(strings = { + "C:\\foo\\bar\\hello/world.jar lib2.jar", + "C:/hello/world/foo.jar", + "lib4.jar C:\\bar\\foo\\world/hello.jar" + }) + public void testMalformedClassPathEntry(final String classPathValue) throws Exception { + final Manifest manifest = createManifestWithClassPath(classPathValue); + final Path jar = Files.createTempFile(TEST_SCRATCH_DIR, "8338445", ".jar"); + // create the JAR file with the given manifest and an arbitrary file + JarUtils.createJarFile(jar, manifest, TEST_SCRATCH_DIR, Path.of(RESOURCE_NAME)); + System.out.println("created jar at " + jar + " with manifest:"); + manifest.write(System.out); + final URL[] urlClassPath = new URL[]{jar.toUri().toURL()}; + // Create a URLClassLoader backed by the JAR file and load a non-existent resource just to + // exercise the URLClassPath code of loading the jar and parsing the Class-Path entry. + // Then close the classloader. After the classloader is closed + // issue a delete on the underlying JAR file on the filesystem. The delete is expected + // to succeed. + try (final URLClassLoader cl = new URLClassLoader(urlClassPath)) { + try (final InputStream is = cl.getResourceAsStream("non-existent.txt")) { + assertNull(is, "unexpectedly found a resource in classpath " + + Arrays.toString(urlClassPath)); + } + } + // now delete the JAR file and verify the delete worked + Files.delete(jar); + assertFalse(Files.exists(jar), jar + " exists even after being deleted"); + } + + /* + * Creates a JAR file with a manifest which has a Class-Path entry value with URLs + * that are parsable but point to files that don't exist on the filesystem. + * Then uses a URLClassLoader backed by the JAR file in its classpath, loads some resource, + * closes the URLClassLoader and then expects that the underlying JAR file can be deleted + * from the filesystem. + */ + @ParameterizedTest + @ValueSource(strings = { + "/home/me/hello/world.jar lib9.jar", + "lib10.jar" + }) + public void testParsableClassPathEntry(final String classPathValue) throws Exception { + final Manifest manifest = createManifestWithClassPath(classPathValue); + final Path jar = Files.createTempFile(TEST_SCRATCH_DIR, "8338445", ".jar"); + // create the JAR file with the given manifest and an arbitrary file + JarUtils.createJarFile(jar, manifest, TEST_SCRATCH_DIR, Path.of(RESOURCE_NAME)); + System.out.println("created jar at " + jar + " with manifest:"); + manifest.write(System.out); + final URL[] urlClassPath = new URL[]{jar.toUri().toURL()}; + // Create a URLClassLoader backed by the JAR file and load a resource + // and verify the resource contents. + // Then close the classloader. After the classloader is closed + // issue a delete on the underlying JAR file on the filesystem. The delete is expected + // to succeed. + try (final URLClassLoader cl = new URLClassLoader(urlClassPath)) { + try (final InputStream is = cl.getResourceAsStream(RESOURCE_NAME)) { + assertNotNull(is, RESOURCE_NAME + " not located by classloader in classpath " + + Arrays.toString(urlClassPath)); + final String content = new String(is.readAllBytes(), US_ASCII); + assertEquals(RESOURCE_CONTENT, content, "unexpected content in " + RESOURCE_NAME); + } + } + // now delete the JAR file and verify the delete worked + Files.delete(jar); + assertFalse(Files.exists(jar), jar + " exists even after being deleted"); + } + + private static Manifest createManifestWithClassPath(final String classPathValue) { + final Manifest manifest = new Manifest(); + final Attributes mainAttributes = manifest.getMainAttributes(); + mainAttributes.putValue("Manifest-Version", "1.0"); + mainAttributes.putValue("Class-Path", classPathValue); + return manifest; + } +} diff --git a/test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java b/test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java index 40da19adfe7b4..16de224777951 100644 --- a/test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java +++ b/test/jdk/java/text/Format/CompactNumberFormat/TestCompactNumber.java @@ -22,7 +22,7 @@ */ /* * @test - * @bug 8177552 8217721 8222756 8295372 8306116 8319990 + * @bug 8177552 8217721 8222756 8295372 8306116 8319990 8338690 * @summary Checks the functioning of compact number format * @modules jdk.localedata * @run testng/othervm TestCompactNumber @@ -60,6 +60,9 @@ public class TestCompactNumber { private static final NumberFormat FORMAT_IT_SHORT = NumberFormat .getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.SHORT); + private static final NumberFormat FORMAT_IT_LONG = NumberFormat + .getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG); + private static final NumberFormat FORMAT_CA_LONG = NumberFormat .getCompactNumberInstance(Locale.of("ca"), NumberFormat.Style.LONG); @@ -89,6 +92,13 @@ public class TestCompactNumber { .getCompactNumberInstance(Locale.ITALIAN, NumberFormat.Style.LONG); private static final NumberFormat FORMAT_PT_LONG_FD4 = NumberFormat .getCompactNumberInstance(Locale.of("pt"), NumberFormat.Style.LONG); + + private static final NumberFormat FORMAT_PL_LONG = NumberFormat + .getCompactNumberInstance(Locale.of("pl"), NumberFormat.Style.LONG); + + private static final NumberFormat FORMAT_FR_LONG = NumberFormat + .getCompactNumberInstance(Locale.FRENCH, NumberFormat.Style.LONG); + static { FORMAT_ES_LONG_FD1.setMaximumFractionDigits(1); FORMAT_DE_LONG_FD2.setMaximumFractionDigits(2); @@ -359,6 +369,12 @@ Object[][] compactFormatData() { {FORMAT_DE_LONG_FD2, 1_234_500, "1,23 Millionen"}, {FORMAT_IT_LONG_FD3, 1_234_500, "1,234 milioni"}, {FORMAT_PT_LONG_FD4, 1_234_500, "1,2345 milh\u00f5es"}, + + // 8338690 + {FORMAT_PL_LONG, 5_000, "5 tysi\u0119cy"}, + {FORMAT_PL_LONG, 4_949, "5 tysi\u0119cy"}, + {FORMAT_FR_LONG, 1_949, "2 mille"}, + {FORMAT_IT_LONG, 1_949, "2 mila"}, }; } @@ -466,6 +482,10 @@ Object[][] compactParseData() { {FORMAT_DE_LONG_FD2, "1,23 Millionen", 1_230_000L, Long.class}, {FORMAT_IT_LONG_FD3, "1,234 milioni", 1_234_000L, Long.class}, {FORMAT_PT_LONG_FD4, "1,2345 milh\u00f5es", 1_234_500L, Long.class}, + // 8338690 + {FORMAT_PL_LONG, "5 tysi\u0119cy", 5_000L, Long.class}, + {FORMAT_FR_LONG, "2 mille", 2_000L, Long.class}, + {FORMAT_IT_LONG, "2 mila", 2_000L, Long.class}, }; } @@ -514,6 +534,10 @@ Object[][] invalidParseData() { {FORMAT_SL_LONG, "5 milijon", 5L}, {FORMAT_SL_LONG, "5 milijona", 5L}, {FORMAT_SL_LONG, "5 milijone", 5L}, + // 8338690 + {FORMAT_PL_LONG, "5 tysiÄ…ce", 5L}, + {FORMAT_FR_LONG, "2 millier", 2L}, + {FORMAT_IT_LONG, "2 mille", 2L}, }; } diff --git a/test/jdk/javax/swing/plaf/gtk/JTableCtrlShiftRightLeftKeyTest.java b/test/jdk/javax/swing/plaf/gtk/JTableCtrlShiftRightLeftKeyTest.java new file mode 100644 index 0000000000000..1df2ed4861632 --- /dev/null +++ b/test/jdk/javax/swing/plaf/gtk/JTableCtrlShiftRightLeftKeyTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.JFrame; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +/* + * @test + * @bug 8338041 + * @key headful + * @summary Verify that Ctrl Shift RIGHT/LEFT key extends columns till + * Last/First Columns in JTable + * @requires (os.family == "linux") + * @run main JTableCtrlShiftRightLeftKeyTest + */ + +public class JTableCtrlShiftRightLeftKeyTest { + private static JFrame frame; + private static JTable table; + private static volatile Point tableLoc; + private static volatile Rectangle cellRect; + private static volatile int[] selectedColumnAfterKeyPress; + private static Robot robot; + private static final int SELECTED_COLUMN = 2; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); + robot = new Robot(); + robot.setAutoDelay(50); + try { + SwingUtilities.invokeAndWait(JTableCtrlShiftRightLeftKeyTest::createAndShowUI); + robot.waitForIdle(); + robot.delay(1000); + + SwingUtilities.invokeAndWait(() -> { + tableLoc = table.getLocationOnScreen(); + cellRect = table.getCellRect(0, SELECTED_COLUMN, true); + }); + + robot.mouseMove(tableLoc.x + cellRect.x + cellRect.width / 2, + tableLoc.y + cellRect.y + cellRect.height / 2); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(100); + + testCtrlShift(KeyEvent.VK_RIGHT, SELECTED_COLUMN, + table.getColumnCount() - 1, "RIGHT"); + + robot.waitForIdle(); + robot.delay(100); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.waitForIdle(); + robot.delay(100); + + testCtrlShift(KeyEvent.VK_LEFT, 0, + SELECTED_COLUMN, "LEFT"); + robot.waitForIdle(); + robot.delay(100); + System.out.println("Test Passed!"); + + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } + + private static void testCtrlShift(int keySelected, int startCellCheck, + int endCellCheck, String key) throws Exception { + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.keyPress(keySelected); + robot.keyRelease(keySelected); + robot.keyRelease(KeyEvent.VK_SHIFT); + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.waitForIdle(); + robot.delay(100); + + SwingUtilities.invokeAndWait(() -> { + selectedColumnAfterKeyPress = table.getSelectedColumns(); + }); + + if (selectedColumnAfterKeyPress[0] != startCellCheck || + selectedColumnAfterKeyPress[selectedColumnAfterKeyPress.length - 1] != + endCellCheck) { + System.out.println("Selected Columns: "); + for (int columnsSelected : selectedColumnAfterKeyPress) { + System.out.println(columnsSelected); + } + String failureMsg = "Test Failure. Failed to select cells for Ctrl" + + " Shift " + key + " selection"; + throw new RuntimeException(failureMsg); + } + } + + private static void createAndShowUI() { + frame = new JFrame("Test Ctrl Shift RIGHT/LEFT Key Press"); + table = new JTable(2, 5); + table.setColumnSelectionAllowed(true); + frame.getContentPane().add(table); + + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setVisible(true); + } +}