mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-06 14:10:36 +00:00
Merge branch 'openjdk:master' into JDK-8373118
This commit is contained in:
commit
c3e137a6bc
@ -458,12 +458,10 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
warning("Failed mmap to file. (%s)", os::strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
if (base != nullptr && addr != base) {
|
||||
if (!os::release_memory(addr, size)) {
|
||||
warning("Could not release memory on unsuccessful file mapping");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The requested address should be the same as the returned address when using MAP_FIXED
|
||||
// as per POSIX.
|
||||
assert(base == nullptr || addr == base, "base should equal addr when using MAP_FIXED");
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2021 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2026 SAP SE. 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
|
||||
@ -946,7 +946,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
|
||||
if (result == -1 ) break;
|
||||
if (!os::write(fd, &zero_int, 1)) {
|
||||
if (errno == ENOSPC) {
|
||||
warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename);
|
||||
warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
|
||||
}
|
||||
result = OS_ERR;
|
||||
break;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2026, 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
|
||||
@ -686,7 +686,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
|
||||
// the check before we do the actual allocation. The reason for doing it
|
||||
// before the allocation is that we avoid having to keep track of the newly
|
||||
// allocated memory while we do a GC.
|
||||
if (policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
// Only try that if we can actually perform a GC.
|
||||
if (is_init_completed() && policy()->need_to_start_conc_mark("concurrent humongous allocation",
|
||||
word_size)) {
|
||||
try_collect(word_size, GCCause::_g1_humongous_allocation, collection_counters(this));
|
||||
}
|
||||
|
||||
@ -700,7 +700,7 @@ void OopStorage::Block::release_entries(uintx releasing, OopStorage* owner) {
|
||||
// then someone else has made such a claim and the deferred update has not
|
||||
// yet been processed and will include our change, so we don't need to do
|
||||
// anything further.
|
||||
if (_deferred_updates_next.compare_exchange(nullptr, this) == nullptr) {
|
||||
if (_deferred_updates_next.compare_set(nullptr, this)) {
|
||||
// Successfully claimed. Push, with self-loop for end-of-list.
|
||||
Block* head = owner->_deferred_updates.load_relaxed();
|
||||
while (true) {
|
||||
|
||||
@ -56,7 +56,7 @@ void PretouchTask::work(uint worker_id) {
|
||||
char* cur_end = cur_start + MIN2(_chunk_size, pointer_delta(_end_addr, cur_start, 1));
|
||||
if (cur_start >= cur_end) {
|
||||
break;
|
||||
} else if (cur_start == _cur_addr.compare_exchange(cur_start, cur_end)) {
|
||||
} else if (_cur_addr.compare_set(cur_start, cur_end)) {
|
||||
os::pretouch_memory(cur_start, cur_end, _page_size);
|
||||
} // Else attempt to claim chunk failed, so try again.
|
||||
}
|
||||
|
||||
@ -183,8 +183,8 @@ protected:
|
||||
_age.store_relaxed(new_age);
|
||||
}
|
||||
|
||||
Age cmpxchg_age(Age old_age, Age new_age) {
|
||||
return _age.compare_exchange(old_age, new_age);
|
||||
bool par_set_age(Age old_age, Age new_age) {
|
||||
return _age.compare_set(old_age, new_age);
|
||||
}
|
||||
|
||||
idx_t age_top_relaxed() const {
|
||||
@ -345,7 +345,7 @@ protected:
|
||||
|
||||
using TaskQueueSuper<N, MT>::age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::set_age_relaxed;
|
||||
using TaskQueueSuper<N, MT>::cmpxchg_age;
|
||||
using TaskQueueSuper<N, MT>::par_set_age;
|
||||
using TaskQueueSuper<N, MT>::age_top_relaxed;
|
||||
|
||||
using TaskQueueSuper<N, MT>::increment_index;
|
||||
|
||||
@ -170,8 +170,7 @@ bool GenericTaskQueue<E, MT, N>::pop_local_slow(uint localBot, Age oldAge) {
|
||||
if (localBot == oldAge.top()) {
|
||||
// No competing pop_global has yet incremented "top"; we'll try to
|
||||
// install new_age, thus claiming the element.
|
||||
Age tempAge = cmpxchg_age(oldAge, newAge);
|
||||
if (tempAge == oldAge) {
|
||||
if (par_set_age(oldAge, newAge)) {
|
||||
// We win.
|
||||
assert_not_underflow(localBot, age_top_relaxed());
|
||||
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
|
||||
@ -283,12 +282,12 @@ typename GenericTaskQueue<E, MT, N>::PopResult GenericTaskQueue<E, MT, N>::pop_g
|
||||
idx_t new_top = increment_index(oldAge.top());
|
||||
idx_t new_tag = oldAge.tag() + ((new_top == 0) ? 1 : 0);
|
||||
Age newAge(new_top, new_tag);
|
||||
Age resAge = cmpxchg_age(oldAge, newAge);
|
||||
bool result = par_set_age(oldAge, newAge);
|
||||
|
||||
// Note that using "bottom" here might fail, since a pop_local might
|
||||
// have decremented it.
|
||||
assert_not_underflow(localBot, newAge.top());
|
||||
return resAge == oldAge ? PopResult::Success : PopResult::Contended;
|
||||
return result ? PopResult::Success : PopResult::Contended;
|
||||
}
|
||||
|
||||
inline int randomParkAndMiller(int *seed0) {
|
||||
|
||||
@ -1112,8 +1112,6 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !ti->is_con() ) return nullptr;
|
||||
jint con = ti->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// First, special check for modulo 2^k-1
|
||||
if( con >= 0 && con < max_jint && is_power_of_2(con+1) ) {
|
||||
uint k = exact_log2(con+1); // Extract k
|
||||
@ -1129,7 +1127,9 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndINode(x,divisor) );
|
||||
@ -1185,6 +1185,7 @@ Node *ModINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivI to MulL
|
||||
@ -1407,8 +1408,6 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( !tl->is_con() ) return nullptr;
|
||||
jlong con = tl->get_con();
|
||||
|
||||
Node *hook = new Node(1);
|
||||
|
||||
// Expand mod
|
||||
if(con >= 0 && con < max_jlong && is_power_of_2(con + 1)) {
|
||||
uint k = log2i_exact(con + 1); // Extract k
|
||||
@ -1426,13 +1425,15 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
Node *x = in(1); // Value being mod'd
|
||||
Node *divisor = in(2); // Also is mask
|
||||
|
||||
hook->init_req(0, x); // Add a use to x to prevent him from dying
|
||||
// Add a use to x to prevent it from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, x);
|
||||
// Generate code to reduce X rapidly to nearly 2^k-1.
|
||||
for( int i = 0; i < trip_count; i++ ) {
|
||||
Node *xl = phase->transform( new AndLNode(x,divisor) );
|
||||
Node *xh = phase->transform( new RShiftLNode(x,phase->intcon(k)) ); // Must be signed
|
||||
x = phase->transform( new AddLNode(xh,xl) );
|
||||
hook->set_req(0, x); // Add a use to x to prevent him from dying
|
||||
hook->set_req(0, x); // Add a use to x to prevent it from dying
|
||||
}
|
||||
|
||||
// Generate sign-fixup code. Was original value positive?
|
||||
@ -1482,6 +1483,8 @@ Node *ModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Save in(1) so that it cannot be changed or deleted
|
||||
// Add a use to x to prevent him from dying
|
||||
Node* hook = new Node(1);
|
||||
hook->init_req(0, in(1));
|
||||
|
||||
// Divide using the transform from DivL to MulL
|
||||
|
||||
@ -75,6 +75,7 @@
|
||||
// v.release_store(x) -> void
|
||||
// v.release_store_fence(x) -> void
|
||||
// v.compare_exchange(x, y [, o]) -> T
|
||||
// v.compare_set(x, y [, o]) -> bool
|
||||
// v.exchange(x [, o]) -> T
|
||||
//
|
||||
// (2) All atomic types are default constructible.
|
||||
@ -267,6 +268,11 @@ public:
|
||||
return AtomicAccess::cmpxchg(value_ptr(), compare_value, new_value, order);
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return compare_exchange(compare_value, new_value, order) == compare_value;
|
||||
}
|
||||
|
||||
T exchange(T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return AtomicAccess::xchg(this->value_ptr(), new_value, order);
|
||||
@ -479,6 +485,13 @@ public:
|
||||
order));
|
||||
}
|
||||
|
||||
bool compare_set(T compare_value, T new_value,
|
||||
atomic_memory_order order = memory_order_conservative) {
|
||||
return _value.compare_set(decay(compare_value),
|
||||
decay(new_value),
|
||||
order);
|
||||
}
|
||||
|
||||
T exchange(T new_value, atomic_memory_order order = memory_order_conservative) {
|
||||
return recover(_value.exchange(decay(new_value), order));
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
if (is_locked()) {
|
||||
return false;
|
||||
}
|
||||
if (_first.compare_exchange(expect, node) == expect) {
|
||||
if (_first.compare_set(expect, node)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -172,7 +172,7 @@ inline bool ConcurrentHashTable<CONFIG, MT>::
|
||||
}
|
||||
// We will expect a clean first pointer.
|
||||
Node* tmp = first();
|
||||
if (_first.compare_exchange(tmp, set_state(tmp, STATE_LOCK_BIT)) == tmp) {
|
||||
if (_first.compare_set(tmp, set_state(tmp, STATE_LOCK_BIT))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -181,7 +181,7 @@ void GenericWaitBarrier::Cell::disarm(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(0, waiters);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Successfully disarmed.
|
||||
break;
|
||||
}
|
||||
@ -218,7 +218,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters + 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success! Proceed to wait.
|
||||
break;
|
||||
}
|
||||
@ -247,7 +247,7 @@ void GenericWaitBarrier::Cell::wait(int32_t expected_tag) {
|
||||
tag, waiters);
|
||||
|
||||
int64_t new_state = encode(tag, waiters - 1);
|
||||
if (_state.compare_exchange(state, new_state) == state) {
|
||||
if (_state.compare_set(state, new_state)) {
|
||||
// Success!
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2045,19 +2045,26 @@ public final class String
|
||||
return encode(Charset.defaultCharset(), coder(), value);
|
||||
}
|
||||
|
||||
boolean bytesCompatible(Charset charset) {
|
||||
boolean bytesCompatible(Charset charset, int srcIndex, int numChars) {
|
||||
if (isLatin1()) {
|
||||
if (charset == ISO_8859_1.INSTANCE) {
|
||||
return true; // ok, same encoding
|
||||
} else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) {
|
||||
return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible
|
||||
return !StringCoding.hasNegatives(value, srcIndex, numChars); // ok, if ASCII-compatible
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset) {
|
||||
MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length);
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
if (!isLatin1()) {
|
||||
// This method is intended to be used together with bytesCompatible, which currently only supports
|
||||
// latin1 strings. In the future, bytesCompatible could be updated to handle more cases, like
|
||||
// UTF-16 strings (when the platform and charset endianness match, and the String doesn’t contain
|
||||
// unpaired surrogates). If that happens, copyToSegmentRaw should also be updated.
|
||||
throw new IllegalStateException("This string does not support copyToSegmentRaw");
|
||||
}
|
||||
MemorySegment.copy(value, srcIndex, segment, ValueLayout.JAVA_BYTE, offset, srcLength);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -2331,13 +2331,13 @@ public final class System {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
string.copyToSegmentRaw(segment, offset);
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
string.copyToSegmentRaw(segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bytesCompatible(String string, Charset charset) {
|
||||
return string.bytesCompatible(charset);
|
||||
public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return string.bytesCompatible(charset, srcIndex, numChars);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -31,6 +31,8 @@ import java.lang.classfile.Instruction;
|
||||
import java.lang.classfile.Opcode;
|
||||
|
||||
import jdk.internal.classfile.impl.AbstractInstruction;
|
||||
import jdk.internal.classfile.impl.BytecodeHelpers;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
|
||||
/**
|
||||
* Models a local variable increment instruction in the {@code code} array of a
|
||||
@ -82,6 +84,37 @@ public sealed interface IncrementInstruction extends Instruction
|
||||
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
|
||||
*/
|
||||
static IncrementInstruction of(int slot, int constant) {
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);
|
||||
var opcode = BytecodeHelpers.validateAndIsWideIinc(slot, constant) ? Opcode.IINC_W: Opcode.IINC;
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(opcode, slot, constant);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return an increment instruction}
|
||||
* <p>
|
||||
* {@code slot} must be {@link java.lang.classfile##u1 u1} and
|
||||
* {@code constant} must be within {@code [-128, 127]} for
|
||||
* {@link Opcode#IINC iinc}, or {@code slot} must be
|
||||
* {@link java.lang.classfile##u2 u2} and {@code constant} must be
|
||||
* within {@code [-32768, 32767]} for {@link Opcode#IINC_W wide iinc}.
|
||||
*
|
||||
* @apiNote
|
||||
* The explicit {@code op} argument allows creating {@code wide} or
|
||||
* regular increment instructions when {@code slot} and
|
||||
* {@code constant} can be encoded with more optimized
|
||||
* increment instructions.
|
||||
*
|
||||
* @param op the opcode for the specific type of increment instruction,
|
||||
* which must be of kind {@link Opcode.Kind#INCREMENT}
|
||||
* @param slot the local variable slot to increment
|
||||
* @param constant the increment constant
|
||||
* @throws IllegalArgumentException if the opcode kind is not
|
||||
* {@link Opcode.Kind#INCREMENT} or {@code slot} or
|
||||
* {@code constant} is out of range
|
||||
* @since 27
|
||||
*/
|
||||
static IncrementInstruction of(Opcode op, int slot, int constant) {
|
||||
Util.checkKind(op, Opcode.Kind.INCREMENT);
|
||||
BytecodeHelpers.validateIncrement(op, slot, constant);
|
||||
return new AbstractInstruction.UnboundIncrementInstruction(op, slot, constant);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1296,12 +1296,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* Getting a string from a segment with a known byte offset and
|
||||
* known byte length can be done like so:
|
||||
* {@snippet lang=java :
|
||||
* byte[] bytes = new byte[length];
|
||||
* MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, length);
|
||||
* return new String(bytes, charset);
|
||||
* }
|
||||
* known byte length can be done using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
@ -1328,6 +1323,40 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
*/
|
||||
String getString(long offset, Charset charset);
|
||||
|
||||
/**
|
||||
* Reads a string from this segment at the given offset, using the provided length
|
||||
* and charset.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the string contains any {@code '\0'} characters, they will be read as well.
|
||||
* This differs from {@link #getString(long, Charset)}, which will only read up
|
||||
* to the first {@code '\0'}, resulting in truncation for string data that contains
|
||||
* the {@code '\0'} character.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur
|
||||
* @param charset the charset used to {@linkplain Charset#newDecoder() decode} the
|
||||
* string bytes
|
||||
* @param byteLength length, in bytes, of the region of memory to read and decode into
|
||||
* a string
|
||||
* @return a Java string constructed from the bytes read from the given starting
|
||||
* address up to the given length
|
||||
* @throws IllegalArgumentException if the size of the string is greater than the
|
||||
* largest string supported by the platform
|
||||
* @throws IndexOutOfBoundsException if {@code offset < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code offset > byteSize() - byteLength}
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* this segment is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isAccessibleBy(T) == false}
|
||||
* @throws IllegalArgumentException if {@code byteLength < 0}
|
||||
*/
|
||||
String getString(long offset, Charset charset, long byteLength);
|
||||
|
||||
/**
|
||||
* Writes the given string into this segment at the given offset, converting it to
|
||||
* a null-terminated byte sequence using the {@linkplain StandardCharsets#UTF_8 UTF-8}
|
||||
@ -1366,7 +1395,8 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param offset offset in bytes (relative to this segment address) at which this
|
||||
* access operation will occur, the final address of this write
|
||||
@ -2606,6 +2636,50 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
||||
elementCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the byte sequence of the given string encoded using the provided charset
|
||||
* to the destination segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement string. The {@link
|
||||
* java.nio.charset.CharsetDecoder} class should be used when more control
|
||||
* over the decoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link #getString(long, Charset, long)}.
|
||||
*
|
||||
* @param src the Java string to be written into the destination segment
|
||||
* @param dstEncoding the charset used to {@linkplain Charset#newEncoder() encode}
|
||||
* the string bytes.
|
||||
* @param srcIndex the starting character index of the source string
|
||||
* @param dst the destination segment
|
||||
* @param dstOffset the starting offset, in bytes, of the destination segment
|
||||
* @param numChars the number of characters to be copied
|
||||
* @throws IllegalStateException if the {@linkplain #scope() scope} associated with
|
||||
* {@code dst} is not {@linkplain Scope#isAlive() alive}
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code dst.isAccessibleBy(T) == false}
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex}, {@code numChars}, or {@code dstOffset}
|
||||
* are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > src.length() - numChars}
|
||||
* @throws IllegalArgumentException if {@code dst} is {@linkplain #isReadOnly() read-only}
|
||||
* @throws IndexOutOfBoundsException if {@code dstOffset > dstSegment.byteSize() - B} where {@code B} is the size,
|
||||
* in bytes, of the substring of {@code src} encoded using the given charset
|
||||
* @return the number of copied bytes.
|
||||
*/
|
||||
@ForceInline
|
||||
static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, src.length());
|
||||
|
||||
return AbstractMemorySegmentImpl.copy(src, dstEncoding, srcIndex, dst, dstOffset, numChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the relative offset, in bytes, of the first mismatch between the
|
||||
* source and the destination segments. More specifically, the bytes at offset
|
||||
|
||||
@ -111,7 +111,8 @@ public interface SegmentAllocator {
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again.
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be converted into a C string
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
@ -137,10 +138,10 @@ public interface SegmentAllocator {
|
||||
int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize();
|
||||
MemorySegment segment;
|
||||
int length;
|
||||
if (StringSupport.bytesCompatible(str, charset)) {
|
||||
if (StringSupport.bytesCompatible(str, charset, 0, str.length())) {
|
||||
length = str.length();
|
||||
segment = allocateNoInit((long) length + termCharSize);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, 0, str.length());
|
||||
} else {
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
length = bytes.length;
|
||||
@ -153,6 +154,53 @@ public interface SegmentAllocator {
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a Java string using the provided charset and stores the resulting
|
||||
* byte array into a memory segment.
|
||||
* <p>
|
||||
* This method always replaces malformed-input and unmappable-character
|
||||
* sequences with this charset's default replacement byte array. The
|
||||
* {@link java.nio.charset.CharsetEncoder} class should be used when more
|
||||
* control over the encoding process is required.
|
||||
* <p>
|
||||
* If the given string contains any {@code '\0'} characters, they will be
|
||||
* copied as well. This means that, depending on the method used to read
|
||||
* the string, such as {@link MemorySegment#getString(long)}, the string
|
||||
* will appear truncated when read again. The string can be read without
|
||||
* truncation using {@link MemorySegment#getString(long, Charset, long)}.
|
||||
*
|
||||
* @param str the Java string to be encoded
|
||||
* @param charset the charset used to {@linkplain Charset#newEncoder() encode} the
|
||||
* string bytes
|
||||
* @param srcIndex the starting index of the source string
|
||||
* @param numChars the number of characters to be copied
|
||||
* @return a new native segment containing the encoded string
|
||||
* @throws IndexOutOfBoundsException if either {@code srcIndex} or {@code numChars} are {@code < 0}
|
||||
* @throws IndexOutOfBoundsException if {@code srcIndex > str.length() - numChars}
|
||||
*
|
||||
* @implSpec The default implementation for this method copies the contents of the
|
||||
* provided Java string into a new memory segment obtained by calling
|
||||
* {@code this.allocate(B)}, where {@code B} is the size, in bytes, of
|
||||
* the string encoded using the provided charset
|
||||
* (e.g. {@code str.getBytes(charset).length});
|
||||
*/
|
||||
@ForceInline
|
||||
default MemorySegment allocateFrom(String str, Charset charset, int srcIndex, int numChars) {
|
||||
Objects.requireNonNull(charset);
|
||||
Objects.requireNonNull(str);
|
||||
Objects.checkFromIndexSize(srcIndex, numChars, str.length());
|
||||
MemorySegment segment;
|
||||
if (StringSupport.bytesCompatible(str, charset, srcIndex, numChars)) {
|
||||
segment = allocateNoInit(numChars);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0, srcIndex, numChars);
|
||||
} else {
|
||||
byte[] bytes = str.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
segment = allocateNoInit(bytes.length);
|
||||
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new memory segment initialized with the provided byte value}
|
||||
* <p>
|
||||
|
||||
@ -634,10 +634,10 @@ public interface JavaLangAccess {
|
||||
/**
|
||||
* Copy the string bytes to an existing segment, avoiding intermediate copies.
|
||||
*/
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset);
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength);
|
||||
|
||||
/**
|
||||
* Are the string bytes compatible with the given charset?
|
||||
*/
|
||||
boolean bytesCompatible(String string, Charset charset);
|
||||
boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars);
|
||||
}
|
||||
|
||||
@ -832,10 +832,8 @@ public abstract sealed class AbstractInstruction
|
||||
final int slot;
|
||||
final int constant;
|
||||
|
||||
public UnboundIncrementInstruction(int slot, int constant) {
|
||||
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
|
||||
? Opcode.IINC_W
|
||||
: Opcode.IINC);
|
||||
public UnboundIncrementInstruction(Opcode op, int slot, int constant) {
|
||||
super(op);
|
||||
this.slot = slot;
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
@ -450,6 +450,13 @@ public class BytecodeHelpers {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void validateIncrement(Opcode opcode, int slot, int constant) {
|
||||
if (validateAndIsWideIinc(slot, constant) && opcode != Opcode.IINC_W) {
|
||||
throw new IllegalArgumentException(
|
||||
"IINC: operands require wide encoding for %s".formatted(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateRet(Opcode opcode, int slot) {
|
||||
if (opcode == Opcode.RET && (slot & ~0xFF) == 0 ||
|
||||
opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0)
|
||||
|
||||
@ -551,6 +551,13 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
unsafeGetOffset() == that.unsafeGetOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(long offset, Charset charset, long byteLength) {
|
||||
Utils.checkNonNegativeArgument(byteLength, "byteLength");
|
||||
Objects.requireNonNull(charset);
|
||||
return StringSupport.read(this, offset, charset, byteLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
@ -702,6 +709,16 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static long copy(String src, Charset dstEncoding, int srcIndex, MemorySegment dst, long dstOffset, int numChars) {
|
||||
Objects.requireNonNull(src);
|
||||
Objects.requireNonNull(dstEncoding);
|
||||
Objects.requireNonNull(dst);
|
||||
|
||||
AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dst;
|
||||
return StringSupport.copyBytes(src, destImpl, dstEncoding, dstOffset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
// accessors
|
||||
|
||||
@ForceInline
|
||||
|
||||
@ -30,11 +30,14 @@ import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
import jdk.internal.util.Architecture;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.util.Preconditions;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
|
||||
@ -58,6 +61,27 @@ public final class StringSupport {
|
||||
};
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String read(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
return readBytes(segment, offset, charset, length);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static String readBytes(AbstractMemorySegmentImpl segment, long offset, Charset charset, long length) {
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Required length exceeds implementation limit");
|
||||
}
|
||||
final int lengthBytes = (int) length;
|
||||
final byte[] bytes = new byte[lengthBytes];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, lengthBytes);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static void write(AbstractMemorySegmentImpl segment, long offset, Charset charset, String string) {
|
||||
switch (CharsetKind.of(charset)) {
|
||||
@ -70,14 +94,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readByte(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
final int len = strlenByte(segment, offset, segment.byteSize());
|
||||
final byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -89,14 +106,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readShort(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenShort(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -108,14 +118,7 @@ public final class StringSupport {
|
||||
@ForceInline
|
||||
private static String readInt(AbstractMemorySegmentImpl segment, long offset, Charset charset) {
|
||||
int len = strlenInt(segment, offset, segment.byteSize());
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
return readBytes(segment, offset, charset, len);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@ -345,22 +348,26 @@ public final class StringSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean bytesCompatible(String string, Charset charset) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset);
|
||||
public static boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset, srcIndex, numChars);
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) {
|
||||
if (bytesCompatible(string, charset)) {
|
||||
copyToSegmentRaw(string, segment, offset);
|
||||
return string.length();
|
||||
return copyBytes(string, segment, charset, offset, 0, string.length());
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset, int srcIndex, int numChars) {
|
||||
if (bytesCompatible(string, charset, srcIndex, numChars)) {
|
||||
copyToSegmentRaw(string, segment, offset, srcIndex, numChars);
|
||||
return numChars;
|
||||
} else {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
byte[] bytes = string.substring(srcIndex, srcIndex + numChars).getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
return bytes.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset);
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset, int srcIndex, int srcLength) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset, srcIndex, srcLength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +162,35 @@ TEST_VM(AtomicIntegerTest, cmpxchg_int64) {
|
||||
Support().test();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct AtomicIntegerCmpsetTestSupport {
|
||||
Atomic<T> _test_value;
|
||||
|
||||
AtomicIntegerCmpsetTestSupport() : _test_value{} {}
|
||||
|
||||
void test() {
|
||||
T zero = 0;
|
||||
T five = 5;
|
||||
T ten = 10;
|
||||
_test_value.store_relaxed(zero);
|
||||
EXPECT_FALSE(_test_value.compare_set(five, ten));
|
||||
EXPECT_EQ(zero, _test_value.load_relaxed());
|
||||
EXPECT_TRUE(_test_value.compare_set(zero, ten));
|
||||
EXPECT_EQ(ten, _test_value.load_relaxed());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_VM(AtomicIntegerTest, cmpset_int32) {
|
||||
using Support = AtomicIntegerCmpsetTestSupport<int32_t>;
|
||||
Support().test();
|
||||
}
|
||||
|
||||
TEST_VM(AtomicIntegerTest, cmpset_int64) {
|
||||
// Check if 64-bit atomics are available on the machine.
|
||||
using Support = AtomicIntegerCmpsetTestSupport<int64_t>;
|
||||
Support().test();
|
||||
}
|
||||
|
||||
struct AtomicXchgAndCmpxchg1ByteStressSupport {
|
||||
char _default_val;
|
||||
int _base;
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.c2.igvn;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8372302
|
||||
* @summary ModINode::Ideal and ModLNode::Ideal use an intermediate "hook" node
|
||||
* to keep stuff alive between phase->transform(...) calls. In some cases,
|
||||
* this node is not properly deleted before returning, causing failure
|
||||
* in the verification because the node count has changed. This test
|
||||
* ensures that the intermediate node gets destroyed before returning.
|
||||
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
|
||||
* -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,${test.main.class}::test*
|
||||
* -XX:VerifyIterativeGVN=1110
|
||||
* ${test.main.class}
|
||||
* @run main ${test.main.class}
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestModIdealCreatesUselessNode {
|
||||
static int test0(int x) {
|
||||
return x % Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
static long test1(long x) {
|
||||
return x % Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test0(0);
|
||||
test1(0L);
|
||||
}
|
||||
}
|
||||
@ -274,6 +274,7 @@ public final class Operations {
|
||||
ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")"));
|
||||
|
||||
// TODO: Math and other classes.
|
||||
// Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset.
|
||||
|
||||
// Make sure the list is not modifiable.
|
||||
return List.copyOf(ops);
|
||||
@ -294,7 +295,8 @@ public final class Operations {
|
||||
ops.add(Expression.make(INTS, "Float16.compare(", FLOAT16, ",", FLOAT16, ")"));
|
||||
addComparisonOperations(ops, "Float16.compare", FLOAT16);
|
||||
ops.add(Expression.make(INTS, "(", FLOAT16, ").compareTo(", FLOAT16, ")"));
|
||||
ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")"));
|
||||
// Note: There are NaN encodings with bit set or unset.
|
||||
ops.add(Expression.make(FLOAT16, "Float16.copySign(", FLOAT16, ",", FLOAT16, ")", WITH_NONDETERMINISTIC_RESULT));
|
||||
ops.add(Expression.make(FLOAT16, "Float16.divide(", FLOAT16, ",", FLOAT16, ")"));
|
||||
ops.add(Expression.make(BOOLEANS, "", FLOAT16, ".equals(", FLOAT16, ")"));
|
||||
// Note: there are multiple NaN values with different bit representations.
|
||||
|
||||
152
test/jdk/build/CheckFiles.java
Normal file
152
test/jdk/build/CheckFiles.java
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2026 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2026, 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.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import jdk.test.lib.Platform;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Check for unwanted file (types/extensions) in the jdk image
|
||||
* @library /test/lib
|
||||
* @requires !vm.debug
|
||||
* @run main CheckFiles
|
||||
*/
|
||||
public class CheckFiles {
|
||||
|
||||
// Set this property on command line to scan an alternate dir or file:
|
||||
// JTREG=JAVA_OPTIONS=-Djdk.test.build.CheckFiles.dir=/path/to/dir
|
||||
public static final String DIR_PROPERTY = "jdk.test.build.CheckFiles.dir";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String jdkPathString = System.getProperty("test.jdk");
|
||||
Path jdkHome = Paths.get(jdkPathString);
|
||||
|
||||
Path mainDirToScan = jdkHome;
|
||||
String overrideDir = System.getProperty(DIR_PROPERTY);
|
||||
if (overrideDir != null) {
|
||||
mainDirToScan = Paths.get(overrideDir);
|
||||
}
|
||||
|
||||
System.out.println("Main directory to scan:" + mainDirToScan);
|
||||
Path binDir = mainDirToScan.resolve("bin");
|
||||
Path libDir = mainDirToScan.resolve("lib");
|
||||
|
||||
System.out.println("Bin directory to scan:" + binDir);
|
||||
ArrayList<String> allowedEndingsBinDir = new ArrayList<>();
|
||||
// UNIX - no extensions are allowed; Windows : .dll, .exe, .pdb, .jsa
|
||||
if (Platform.isWindows()) {
|
||||
allowedEndingsBinDir.add(".dll");
|
||||
allowedEndingsBinDir.add(".exe");
|
||||
allowedEndingsBinDir.add(".pdb");
|
||||
allowedEndingsBinDir.add(".jsa");
|
||||
}
|
||||
boolean binDirRes = scanFiles(binDir, allowedEndingsBinDir);
|
||||
|
||||
System.out.println("Lib directory to scan:" + libDir);
|
||||
ArrayList<String> allowedEndingsLibDir = new ArrayList<>();
|
||||
allowedEndingsLibDir.add(".jfc"); // jfr config files
|
||||
allowedEndingsLibDir.add("cacerts");
|
||||
allowedEndingsLibDir.add("blocked.certs");
|
||||
allowedEndingsLibDir.add("public_suffix_list.dat");
|
||||
allowedEndingsLibDir.add("classlist");
|
||||
allowedEndingsLibDir.add("fontconfig.bfc");
|
||||
allowedEndingsLibDir.add("fontconfig.properties.src");
|
||||
allowedEndingsLibDir.add("ct.sym");
|
||||
allowedEndingsLibDir.add("jrt-fs.jar");
|
||||
allowedEndingsLibDir.add("jvm.cfg");
|
||||
allowedEndingsLibDir.add("modules");
|
||||
allowedEndingsLibDir.add("psfontj2d.properties");
|
||||
allowedEndingsLibDir.add("psfont.properties.ja");
|
||||
allowedEndingsLibDir.add("src.zip");
|
||||
allowedEndingsLibDir.add("tzdb.dat");
|
||||
if (Platform.isWindows()) {
|
||||
allowedEndingsLibDir.add(".lib");
|
||||
allowedEndingsLibDir.add("tzmappings");
|
||||
} else {
|
||||
allowedEndingsLibDir.add("jexec");
|
||||
allowedEndingsLibDir.add("jspawnhelper");
|
||||
allowedEndingsLibDir.add(".jsa");
|
||||
if (Platform.isOSX()) {
|
||||
allowedEndingsLibDir.add("shaders.metallib");
|
||||
allowedEndingsLibDir.add(".dylib");
|
||||
} else {
|
||||
allowedEndingsLibDir.add(".so");
|
||||
}
|
||||
if (Platform.isAix()) {
|
||||
allowedEndingsLibDir.add("tzmappings");
|
||||
}
|
||||
}
|
||||
boolean libDirRes = scanFiles(libDir, allowedEndingsLibDir);
|
||||
|
||||
if (!binDirRes) {
|
||||
throw new Error("bin dir scan failed");
|
||||
}
|
||||
|
||||
if (!libDirRes) {
|
||||
throw new Error("lib dir scan failed");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean scanFiles(Path root, ArrayList<String> allowedEndings) throws IOException {
|
||||
AtomicBoolean badFileFound = new AtomicBoolean(false);
|
||||
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String fullFileName = file.toString();
|
||||
String fileName = file.getFileName().toString();
|
||||
System.out.println(" visiting file:" + fullFileName);
|
||||
checkFile(fileName, allowedEndings);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
|
||||
private void checkFile(String name, ArrayList<String> allowedEndings) {
|
||||
if (allowedEndings.isEmpty()) { // no file extensions allowed
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot > 0) {
|
||||
System.out.println(" --> ERROR this file is not allowed:" + name);
|
||||
badFileFound.set(true);
|
||||
}
|
||||
} else {
|
||||
boolean allowed = allowedEndings.stream().anyMatch(name::endsWith);
|
||||
if (! allowed) {
|
||||
System.out.println(" --> ERROR this file is not allowed:" + name);
|
||||
badFileFound.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return !badFileFound.get();
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
@ -102,6 +103,140 @@ public class TestStringEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testStringsLength(String testString) {
|
||||
if (!testString.isEmpty()) {
|
||||
for (Charset charset : Charset.availableCharsets().values()) {
|
||||
if (charset.canEncode()) {
|
||||
for (Arena arena : arenas()) {
|
||||
try (arena) {
|
||||
MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length());
|
||||
long length = text.byteSize();
|
||||
assertEquals(length, testString.getBytes(charset).length);
|
||||
String roundTrip = text.getString(0, charset, length);
|
||||
if (charset.newEncoder().canEncode(testString)) {
|
||||
assertEquals(roundTrip, testString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testStringsCopy(String testString) {
|
||||
if (!testString.isEmpty()) {
|
||||
for (Charset charset : Charset.availableCharsets().values()) {
|
||||
if (charset.canEncode()) {
|
||||
for (Arena arena : arenas()) {
|
||||
try (arena) {
|
||||
byte[] bytes = testString.getBytes(charset);
|
||||
MemorySegment text = arena.allocate(JAVA_BYTE, bytes.length);
|
||||
MemorySegment.copy(testString, charset, 0, text, 0, testString.length());
|
||||
String roundTrip = text.getString(0, charset, bytes.length);
|
||||
if (charset.newEncoder().canEncode(testString)) {
|
||||
assertEquals(roundTrip, testString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringsLengthNegative() {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
var segment = arena.allocateFrom("abc");
|
||||
assertThrows(IllegalArgumentException.class, () -> segment.getString(1, StandardCharsets.UTF_8, -1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyThrows() {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
String testString = "abc";
|
||||
String testString_notBytesCompatible = "snowman \u26C4";
|
||||
MemorySegment text = arena.allocate(JAVA_BYTE, 3);
|
||||
MemorySegment text_notBytesCompatible = arena.allocate(JAVA_BYTE,
|
||||
testString_notBytesCompatible.getBytes(StandardCharsets.UTF_8).length);
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length());
|
||||
MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, 0,
|
||||
text_notBytesCompatible, 0,
|
||||
testString_notBytesCompatible.length());
|
||||
// srcIndex < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, -1, text, 0, testString.length()));
|
||||
// dstOffset < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, -1, testString.length()));
|
||||
// numChars < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, -1));
|
||||
// srcIndex + numChars > length
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 1, text, 0, testString.length()));
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 0, testString.length() + 1));
|
||||
// dstOffset > byteSize() - B
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, 0, text, 1, testString.length()));
|
||||
// srcIndex + numChars overflows
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3));
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
MemorySegment.copy(testString_notBytesCompatible, StandardCharsets.UTF_8, Integer.MAX_VALUE, text, 0, Integer.MAX_VALUE + 3));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllocateFromThrows() {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
String testString = "abc";
|
||||
String testString_notBytesCompatible = "snowman \u26C4";
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length());
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 2, 1);
|
||||
// srcIndex < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, -1, testString.length()));
|
||||
// numChars < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, -1));
|
||||
// srcIndex + numChars > length
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length() + 1));
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 1, testString.length()));
|
||||
// srcIndex + numChars overflows
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
arena.allocateFrom(testString, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE));
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> arena.allocateFrom(
|
||||
testString_notBytesCompatible, StandardCharsets.UTF_8, 3, Integer.MAX_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStringThrows() {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
String testString = "abc";
|
||||
MemorySegment text = arena.allocateFrom(testString, StandardCharsets.UTF_8, 0, testString.length());
|
||||
text.getString(0, StandardCharsets.UTF_8, 3);
|
||||
// unsupported string size
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
text.getString(0, StandardCharsets.UTF_8, Integer.MAX_VALUE + 1L));
|
||||
// offset < 0
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
text.getString(-1, StandardCharsets.UTF_8, 3));
|
||||
// offset > byteSize() - length
|
||||
assertThrows(IndexOutOfBoundsException.class, () ->
|
||||
text.getString(1, StandardCharsets.UTF_8, 3));
|
||||
// length < 0
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
text.getString(0, StandardCharsets.UTF_8, -1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testStringsHeap(String testString) {
|
||||
for (Charset charset : singleByteCharsets()) {
|
||||
@ -221,6 +356,74 @@ public class TestStringEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testSubstringGetString(String testString) {
|
||||
if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) {
|
||||
return;
|
||||
}
|
||||
for (var charset : singleByteCharsets()) {
|
||||
for (var arena: arenas()) {
|
||||
try (arena) {
|
||||
MemorySegment text = arena.allocateFrom(testString, charset, 0, testString.length());
|
||||
for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) {
|
||||
for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) {
|
||||
// this test assumes single-byte charsets
|
||||
String roundTrip = text.getString(srcIndex, charset, numChars);
|
||||
String substring = testString.substring(srcIndex, srcIndex + numChars);
|
||||
assertEquals(roundTrip, substring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testSubstringAllocate(String testString) {
|
||||
if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) {
|
||||
return;
|
||||
}
|
||||
for (var charset : singleByteCharsets()) {
|
||||
for (var arena: arenas()) {
|
||||
try (arena) {
|
||||
for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) {
|
||||
for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) {
|
||||
MemorySegment text = arena.allocateFrom(testString, charset, srcIndex, numChars);
|
||||
String substring = testString.substring(srcIndex, srcIndex + numChars);
|
||||
assertEquals(text.byteSize(), substring.getBytes(charset).length);
|
||||
String roundTrip = text.getString(0, charset, text.byteSize());
|
||||
assertEquals(roundTrip, substring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "strings")
|
||||
public void testSubstringCopy(String testString) {
|
||||
if (testString.length() < 3 || !containsOnlyRegularCharacters(testString)) {
|
||||
return;
|
||||
}
|
||||
for (var charset : singleByteCharsets()) {
|
||||
for (var arena: arenas()) {
|
||||
try (arena) {
|
||||
for (int srcIndex = 0; srcIndex <= testString.length(); srcIndex++) {
|
||||
for (int numChars = 0; numChars <= testString.length() - srcIndex; numChars++) {
|
||||
String substring = testString.substring(srcIndex, srcIndex + numChars);
|
||||
long length = substring.getBytes(charset).length;
|
||||
MemorySegment text = arena.allocate(JAVA_BYTE, length);
|
||||
long copied = MemorySegment.copy(testString, charset, srcIndex, text, 0, numChars);
|
||||
String roundTrip = text.getString(0, charset, length);
|
||||
assertEquals(roundTrip, substring);
|
||||
assertEquals(copied, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final MemoryLayout CHAR_POINTER = ADDRESS
|
||||
.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE));
|
||||
private static final Linker LINKER = Linker.nativeLinker();
|
||||
@ -402,7 +605,7 @@ public class TestStringEncoding {
|
||||
{""},
|
||||
{"X"},
|
||||
{"12345"},
|
||||
{"yen \u00A5"},
|
||||
{"section \u00A7"},
|
||||
{"snowman \u26C4"},
|
||||
{"rainbow \uD83C\uDF08"},
|
||||
{"0"},
|
||||
|
||||
@ -195,6 +195,7 @@ class InstructionValidationTest {
|
||||
ensureFailFast(i, cob -> cob.iinc(i, 1));
|
||||
}
|
||||
check(fails, () -> IncrementInstruction.of(i, 1));
|
||||
check(fails, () -> IncrementInstruction.of(IINC_W, i, 1));
|
||||
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i));
|
||||
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i));
|
||||
check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel));
|
||||
@ -208,6 +209,7 @@ class InstructionValidationTest {
|
||||
check(fails, () -> LoadInstruction.of(u1Op, i));
|
||||
for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE))
|
||||
check(fails, () -> StoreInstruction.of(u1Op, i));
|
||||
check(fails, () -> IncrementInstruction.of(IINC, i, 1));
|
||||
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i));
|
||||
}
|
||||
|
||||
@ -250,6 +252,13 @@ class InstructionValidationTest {
|
||||
IncrementInstruction.of(0, 2);
|
||||
IncrementInstruction.of(0, Short.MAX_VALUE);
|
||||
IncrementInstruction.of(0, Short.MIN_VALUE);
|
||||
IncrementInstruction.of(IINC, 0, 2);
|
||||
IncrementInstruction.of(IINC, 0, Byte.MIN_VALUE);
|
||||
IncrementInstruction.of(IINC, 0, Byte.MAX_VALUE);
|
||||
IncrementInstruction.of(IINC_W, 0, 2);
|
||||
IncrementInstruction.of(IINC_W, 0, Short.MIN_VALUE);
|
||||
IncrementInstruction.of(IINC_W, 0, Short.MAX_VALUE);
|
||||
|
||||
for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) {
|
||||
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, i));
|
||||
TestUtil.runCodeHandler(cob -> {
|
||||
@ -257,6 +266,12 @@ class InstructionValidationTest {
|
||||
cob.return_();
|
||||
});
|
||||
}
|
||||
for (int i : new int[] {Byte.MIN_VALUE - 1, Byte.MAX_VALUE + 1}) {
|
||||
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC, 0, i));
|
||||
}
|
||||
for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) {
|
||||
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(IINC_W, 0, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
@ -34,8 +34,9 @@ import jdk.jfr.Configuration;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.consumer.EventStream;
|
||||
import jdk.jfr.consumer.RecordedClassLoader;
|
||||
import jdk.jfr.consumer.RecordingStream;
|
||||
import jdk.test.lib.jfr.TestClassLoader;
|
||||
|
||||
/**
|
||||
* @test
|
||||
@ -52,28 +53,17 @@ public class TestBackToBackSensitive {
|
||||
static class FillEvent extends Event {
|
||||
String message;
|
||||
}
|
||||
public static Object OBJECT;
|
||||
|
||||
public static void main(String... arg) throws Exception {
|
||||
Set<Instant> threadDumps = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
Set<Instant> classLoaderStatistics = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
Set<Instant> physicalMemory = Collections.synchronizedSet(new LinkedHashSet<>());
|
||||
|
||||
TestClassLoader loader = new TestClassLoader();
|
||||
Class<?> clazz = loader.loadClass(TestBackToBackSensitive.class.getName());
|
||||
String classLoaderName = loader.getClass().getName();
|
||||
OBJECT = clazz.getDeclaredConstructor().newInstance();
|
||||
Configuration configuration = Configuration.getConfiguration("default");
|
||||
try (RecordingStream r1 = new RecordingStream(configuration)) {
|
||||
r1.setMaxSize(Long.MAX_VALUE);
|
||||
r1.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime()));
|
||||
r1.onEvent("jdk.ClassLoaderStatistics", e -> {
|
||||
RecordedClassLoader cl = e.getValue("classLoader");
|
||||
if (cl != null) {
|
||||
if (cl.getType().getName().contains("PlatformClassLoader")) {
|
||||
classLoaderStatistics.add(e.getStartTime());
|
||||
System.out.println("Class loader" + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
r1.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime()));
|
||||
try (Recording r1 = new Recording(configuration)) {
|
||||
// Start chunk 1
|
||||
r1.startAsync();
|
||||
r1.start();
|
||||
try (Recording r2 = new Recording()) {
|
||||
// Start chunk 2
|
||||
r2.start();
|
||||
@ -86,6 +76,25 @@ public class TestBackToBackSensitive {
|
||||
f.commit();
|
||||
}
|
||||
r1.stop();
|
||||
Path file = Path.of("file.jfr");
|
||||
r1.dump(file);
|
||||
Set<Instant> threadDumps = new LinkedHashSet<>();
|
||||
Set<Instant> classLoaderStatistics = new LinkedHashSet<>();
|
||||
Set<Instant> physicalMemory = new LinkedHashSet<>();
|
||||
try (EventStream es = EventStream.openFile(file)) {
|
||||
es.onEvent("jdk.ThreadDump", e -> threadDumps.add(e.getStartTime()));
|
||||
es.onEvent("jdk.ClassLoaderStatistics", e -> {
|
||||
RecordedClassLoader cl = e.getValue("classLoader");
|
||||
if (cl != null) {
|
||||
if (cl.getType().getName().equals(classLoaderName)) {
|
||||
classLoaderStatistics.add(e.getStartTime());
|
||||
System.out.println("Class loader" + e);
|
||||
}
|
||||
}
|
||||
});
|
||||
es.onEvent("jdk.PhysicalMemory", e -> physicalMemory.add(e.getStartTime()));
|
||||
es.start();
|
||||
}
|
||||
long chunkFiles = filesInRepository();
|
||||
System.out.println("Number of chunk files: " + chunkFiles);
|
||||
// When jdk.PhysicalMemory is expected to be emitted:
|
||||
@ -93,15 +102,15 @@ public class TestBackToBackSensitive {
|
||||
// Chunk 2: begin, end
|
||||
// Chunk 3: begin, end
|
||||
// Chunk 4: begin, end
|
||||
assertCount(r1, "jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles);
|
||||
assertCount("jdk.PhysicalMemory", physicalMemory, 2 * chunkFiles);
|
||||
// When jdk.ClassLoaderStatistics and jdk.ThreadThreadDump are expected to be
|
||||
// emitted:
|
||||
// Chunk 1: begin, end
|
||||
// Chunk 2: begin, end
|
||||
// Chunk 3: end
|
||||
// Chunk 4: end
|
||||
assertCount(r1, "jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2));
|
||||
assertCount(r1, "jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2));
|
||||
assertCount("jdk.ThreadDump", threadDumps, 2 + 2 + (chunkFiles - 2));
|
||||
assertCount("jdk.ClassLoaderStatistics", classLoaderStatistics, 2 + 2 + (chunkFiles - 2));
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,15 +119,13 @@ public class TestBackToBackSensitive {
|
||||
return Files.list(repository).filter(p -> p.toString().endsWith(".jfr")).count();
|
||||
}
|
||||
|
||||
private static void assertCount(RecordingStream stream, String eventName, Set<Instant> timestamps, long expected) throws Exception {
|
||||
private static void assertCount(String eventName, Set<Instant> timestamps, long expected) throws Exception {
|
||||
System.out.println("Timestamps for " + eventName + ":");
|
||||
for (Instant timestamp : timestamps) {
|
||||
System.out.println(timestamp);
|
||||
}
|
||||
int count = timestamps.size();
|
||||
if (count != expected) {
|
||||
System.out.println("Dumping failure file.");
|
||||
stream.dump(Path.of("failure.jfr"));
|
||||
throw new Exception("Expected " + expected + " timestamps for event " + eventName + ", but got " + count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 org.openjdk.bench.java.lang.foreign;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Fork(value = 3)
|
||||
public class FromJavaStringTest {
|
||||
|
||||
private String str;
|
||||
private MemorySegment strSegment;
|
||||
private int lengthBytes;
|
||||
|
||||
@Param({"5", "20", "100", "200", "451"})
|
||||
int size;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
var arena = Arena.ofAuto();
|
||||
while (LOREM.length() < size) {
|
||||
LOREM += LOREM;
|
||||
}
|
||||
str = LOREM.substring(0, size);
|
||||
strSegment = arena.allocateFrom(str);
|
||||
lengthBytes = str.getBytes(UTF_8).length;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void segment_setString() {
|
||||
strSegment.setString(0, str, UTF_8);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void segment_copyStringRaw() {
|
||||
MemorySegment.copy(str, UTF_8, 0, strSegment, 0, str.length());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void segment_copyStringBytes() {
|
||||
byte[] bytes = str.getBytes(UTF_8);
|
||||
MemorySegment.copy(bytes, 0, strSegment, JAVA_BYTE, 0, bytes.length);
|
||||
}
|
||||
|
||||
static String LOREM =
|
||||
"""
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
|
||||
ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
||||
mollit anim id est laborum.
|
||||
""";
|
||||
}
|
||||
@ -22,6 +22,9 @@
|
||||
*/
|
||||
package org.openjdk.bench.java.lang.foreign;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
@ -47,6 +50,7 @@ import java.util.concurrent.TimeUnit;
|
||||
public class ToJavaStringTest {
|
||||
|
||||
private MemorySegment strSegment;
|
||||
private int length;
|
||||
|
||||
@Param({"5", "20", "100", "200", "451"})
|
||||
int size;
|
||||
@ -61,19 +65,33 @@ public class ToJavaStringTest {
|
||||
while (LOREM.length() < size) {
|
||||
LOREM += LOREM;
|
||||
}
|
||||
strSegment = arena.allocateFrom(LOREM.substring(0, size));
|
||||
var s = LOREM.substring(0, size);
|
||||
strSegment = arena.allocateFrom(s);
|
||||
length = s.getBytes(UTF_8).length;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String panama_readString() {
|
||||
public String segment_getString() {
|
||||
return strSegment.getString(0);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String segment_getStringLength() {
|
||||
return strSegment.getString(0, UTF_8, length);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String jni_readString() {
|
||||
return readString(strSegment.address());
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public String segment_copyStringBytes() {
|
||||
byte[] bytes = new byte[length];
|
||||
MemorySegment.copy(strSegment, JAVA_BYTE, 0, bytes, 0, length);
|
||||
return new String(bytes, UTF_8);
|
||||
}
|
||||
|
||||
static native String readString(long addr);
|
||||
|
||||
static String LOREM = """
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user