Merge
This commit is contained in:
commit
d9ca438d06
src
java.base/share/classes
java
jdk/internal
foreign
AbstractMemorySegmentImpl.javaConfinedSession.javaHeapMemorySegmentImpl.javaMappedMemorySegmentImpl.javaMemoryAddressImpl.javaMemorySessionImpl.javaNativeMemorySegmentImpl.javaScoped.javaSharedSession.java
abi
misc
sun/nio/ch
jdk.javadoc/share/classes/jdk/javadoc/internal
doclets
formats/html
toolkit
doclint
test
hotspot/jtreg/serviceability/jvmti/events/FramePop/framepop02
jdk/java/foreign
langtools/jdk/javadoc/doclet
InheritDocForUserTags
testInheritDocWithinInappropriateTag
testRelativeLinks
testSimpleTagInherit
testTaglets
@ -25,6 +25,7 @@
|
||||
*/
|
||||
package java.lang.foreign;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -50,7 +51,16 @@ import jdk.internal.reflect.Reflection;
|
||||
* <p>
|
||||
* As such, this interface only supports reading {@code int}, {@code double},
|
||||
* and any other type that fits into a {@code long}.
|
||||
*
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
* It is possible for clients to access elements outside the spatial bounds of a variable argument list.
|
||||
* Variable argument list implementations will try to detect out-of-bounds reads on a best-effort basis.
|
||||
* <p>
|
||||
* Whether this detection succeeds depends on the factory method used to create the variable argument list:
|
||||
* <ul>
|
||||
* <li>Variable argument lists created <em>safely</em>, using {@link #make(Consumer, MemorySession)} are capable of detecting out-of-bounds reads;</li>
|
||||
* <li>Variable argument lists created <em>unsafely</em>, using {@link #ofAddress(MemoryAddress, MemorySession)} are not capable of detecting out-of-bounds reads</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class is not thread safe, and all accesses should occur within a single thread
|
||||
* (regardless of the memory session associated with the variable arity list).
|
||||
*
|
||||
@ -74,6 +84,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
int nextVarg(ValueLayout.OfInt layout);
|
||||
|
||||
@ -87,6 +98,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
long nextVarg(ValueLayout.OfLong layout);
|
||||
|
||||
@ -100,6 +112,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
double nextVarg(ValueLayout.OfDouble layout);
|
||||
|
||||
@ -113,6 +126,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemoryAddress nextVarg(ValueLayout.OfAddress layout);
|
||||
|
||||
@ -135,6 +149,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
|
||||
|
||||
@ -146,6 +161,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
void skip(MemoryLayout... layouts);
|
||||
|
||||
@ -185,6 +201,8 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can not detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param address a memory address pointing to an existing variable argument list.
|
||||
* @param session the memory session to be associated with the returned variable argument list.
|
||||
* @return a new variable argument list backed by the memory region at {@code address}.
|
||||
@ -214,6 +232,8 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
||||
* Note that when there are no elements added to the created va list,
|
||||
* this method will return the same as {@link #empty()}.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
|
||||
* of the underlying variable argument list.
|
||||
* @param session the memory session to be associated with the new variable arity list.
|
||||
|
@ -37,7 +37,6 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.MemorySession;
|
||||
import java.util.Objects;
|
||||
import java.util.Spliterator;
|
||||
|
||||
@ -771,11 +770,7 @@ public abstract sealed class Buffer
|
||||
final void checkSession() {
|
||||
MemorySessionImpl session = session();
|
||||
if (session != null) {
|
||||
try {
|
||||
session.checkValidState();
|
||||
} catch (ScopedMemoryAccess.ScopedAccessError e) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
}
|
||||
session.checkValidState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,11 +313,7 @@ class Direct$Type$Buffer$RW$$BO$
|
||||
if (session.ownerThread() == null && session.isCloseable()) {
|
||||
throw new UnsupportedOperationException("ByteBuffer derived from closeable shared sessions not supported");
|
||||
}
|
||||
try {
|
||||
session.checkValidState();
|
||||
} catch (ScopedAccessError e) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
}
|
||||
session.checkValidState();
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
@ -70,40 +70,33 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
|
||||
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
|
||||
|
||||
static final int READ_ONLY = 1;
|
||||
static final long NONCE = new Random().nextLong();
|
||||
|
||||
static final int DEFAULT_MODES = 0;
|
||||
|
||||
static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
|
||||
final long length;
|
||||
final int mask;
|
||||
final boolean readOnly;
|
||||
final MemorySession session;
|
||||
|
||||
@ForceInline
|
||||
AbstractMemorySegmentImpl(long length, int mask, MemorySession session) {
|
||||
AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySession session) {
|
||||
this.length = length;
|
||||
this.mask = mask;
|
||||
this.readOnly = readOnly;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
abstract long min();
|
||||
|
||||
abstract Object base();
|
||||
|
||||
abstract AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session);
|
||||
abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session);
|
||||
|
||||
abstract ByteBuffer makeByteBuffer();
|
||||
|
||||
@Override
|
||||
public AbstractMemorySegmentImpl asReadOnly() {
|
||||
return dup(0, length, mask | READ_ONLY, session);
|
||||
return dup(0, length, true, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return isSet(READ_ONLY);
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -119,7 +112,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
}
|
||||
|
||||
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
|
||||
return dup(offset, newSize, mask, session);
|
||||
return dup(offset, newSize, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -147,7 +140,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
@Override
|
||||
public final MemorySegment fill(byte value){
|
||||
checkAccess(0, length, false);
|
||||
SCOPED_MEMORY_ACCESS.setMemory(sessionImpl(), base(), min(), length, value);
|
||||
SCOPED_MEMORY_ACCESS.setMemory(sessionImpl(), unsafeGetBase(), unsafeGetOffset(), length, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -176,8 +169,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
return 0;
|
||||
}
|
||||
i = vectorizedMismatchLargeForBytes(sessionImpl(), that.sessionImpl(),
|
||||
this.base(), this.min(),
|
||||
that.base(), that.min(),
|
||||
this.unsafeGetBase(), this.unsafeGetOffset(),
|
||||
that.unsafeGetBase(), that.unsafeGetOffset(),
|
||||
length);
|
||||
if (i >= 0) {
|
||||
return i;
|
||||
@ -235,7 +228,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
public final ByteBuffer asByteBuffer() {
|
||||
checkArraySize("ByteBuffer", 1);
|
||||
ByteBuffer _bb = makeByteBuffer();
|
||||
if (isSet(READ_ONLY)) {
|
||||
if (readOnly) {
|
||||
//session is IMMUTABLE - obtain a RO byte buffer
|
||||
_bb = _bb.asReadOnlyBuffer();
|
||||
}
|
||||
@ -260,9 +253,9 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
@Override
|
||||
public final Optional<MemorySegment> asOverlappingSlice(MemorySegment other) {
|
||||
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
|
||||
if (base() == that.base()) { // both either native or heap
|
||||
final long thisStart = this.min();
|
||||
final long thatStart = that.min();
|
||||
if (unsafeGetBase() == that.unsafeGetBase()) { // both either native or heap
|
||||
final long thisStart = this.unsafeGetOffset();
|
||||
final long thatStart = that.unsafeGetOffset();
|
||||
final long thisEnd = thisStart + this.byteSize();
|
||||
final long thatEnd = thatStart + that.byteSize();
|
||||
|
||||
@ -278,8 +271,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
@Override
|
||||
public final long segmentOffset(MemorySegment other) {
|
||||
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other);
|
||||
if (base() == that.base()) {
|
||||
return that.min() - this.min();
|
||||
if (unsafeGetBase() == that.unsafeGetBase()) {
|
||||
return that.unsafeGetOffset() - this.unsafeGetOffset();
|
||||
}
|
||||
throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa).");
|
||||
}
|
||||
@ -347,31 +340,24 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
return arr;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public void checkAccess(long offset, long length, boolean readOnly) {
|
||||
if (!readOnly && isSet(READ_ONLY)) {
|
||||
if (!readOnly && this.readOnly) {
|
||||
throw new UnsupportedOperationException("Attempt to write a read-only segment");
|
||||
}
|
||||
checkBounds(offset, length);
|
||||
}
|
||||
|
||||
public void checkValidState() {
|
||||
sessionImpl().checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
}
|
||||
|
||||
public long unsafeGetOffset() {
|
||||
return min();
|
||||
}
|
||||
public abstract long unsafeGetOffset();
|
||||
|
||||
public Object unsafeGetBase() {
|
||||
return base();
|
||||
}
|
||||
public abstract Object unsafeGetBase();
|
||||
|
||||
// Helper methods
|
||||
|
||||
private boolean isSet(int mask) {
|
||||
return (this.mask & mask) != 0;
|
||||
}
|
||||
|
||||
public abstract long maxAlignMask();
|
||||
|
||||
@ForceInline
|
||||
@ -407,25 +393,19 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
return outOfBoundException(offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySession session() {
|
||||
return session;
|
||||
}
|
||||
|
||||
private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
|
||||
return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; offset = %d; length = %d",
|
||||
return new IndexOutOfBoundsException(String.format("Out of bound access on segment %s; new offset = %d; new length = %d",
|
||||
this, offset, length));
|
||||
}
|
||||
|
||||
protected int id() {
|
||||
//compute a stable and random id for this memory segment
|
||||
return Math.abs(Objects.hash(base(), min(), NONCE));
|
||||
return Math.abs(Objects.hash(unsafeGetBase(), unsafeGetOffset(), NONCE));
|
||||
}
|
||||
|
||||
static class SegmentSplitter implements Spliterator<MemorySegment> {
|
||||
@ -541,42 +521,37 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
|
||||
int size = limit - pos;
|
||||
|
||||
AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
|
||||
final MemorySessionImpl bufferSession;
|
||||
int modes;
|
||||
final MemorySession bufferSession;
|
||||
if (bufferSegment != null) {
|
||||
bufferSession = bufferSegment.sessionImpl();
|
||||
modes = bufferSegment.mask;
|
||||
bufferSession = bufferSegment.session;
|
||||
} else {
|
||||
bufferSession = MemorySessionImpl.heapSession(bb);
|
||||
modes = DEFAULT_MODES;
|
||||
}
|
||||
if (bb.isReadOnly()) {
|
||||
modes |= READ_ONLY;
|
||||
}
|
||||
boolean readOnly = bb.isReadOnly();
|
||||
int scaleFactor = getScaleFactor(bb);
|
||||
if (base != null) {
|
||||
if (base instanceof byte[]) {
|
||||
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof short[]) {
|
||||
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof char[]) {
|
||||
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof int[]) {
|
||||
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof float[]) {
|
||||
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof long[]) {
|
||||
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else if (base instanceof double[]) {
|
||||
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, modes);
|
||||
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
} else {
|
||||
throw new AssertionError("Cannot get here");
|
||||
}
|
||||
} else if (unmapper == null) {
|
||||
return new NativeMemorySegmentImpl(bbAddress + (pos << scaleFactor), size << scaleFactor, modes, bufferSession);
|
||||
return new NativeMemorySegmentImpl(bbAddress + (pos << scaleFactor), size << scaleFactor, readOnly, bufferSession);
|
||||
} else {
|
||||
// we can ignore scale factor here, a mapped buffer is always a byte buffer, so scaleFactor == 0.
|
||||
return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferSession);
|
||||
return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, readOnly, bufferSession);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,17 +55,12 @@ final class ConfinedSession extends MemorySessionImpl {
|
||||
super(owner, new ConfinedResourceList(), cleaner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return state != CLOSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ForceInline
|
||||
public void acquire0() {
|
||||
checkValidStateSlow();
|
||||
checkValidState();
|
||||
if (state == MAX_FORKS) {
|
||||
throw new IllegalStateException("Session keep alive limit exceeded");
|
||||
throw tooManyAcquires();
|
||||
}
|
||||
state++;
|
||||
}
|
||||
@ -85,11 +80,11 @@ final class ConfinedSession extends MemorySessionImpl {
|
||||
}
|
||||
|
||||
void justClose() {
|
||||
checkValidStateSlow();
|
||||
checkValidState();
|
||||
if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
|
||||
state = CLOSED;
|
||||
} else {
|
||||
throw new IllegalStateException("Session is acquired by " + state + " clients");
|
||||
throw alreadyAcquired(state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +98,7 @@ final class ConfinedSession extends MemorySessionImpl {
|
||||
cleanup.next = fst;
|
||||
fst = cleanup;
|
||||
} else {
|
||||
throw new IllegalStateException("Already closed!");
|
||||
throw alreadyClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +109,7 @@ final class ConfinedSession extends MemorySessionImpl {
|
||||
fst = ResourceCleanup.CLOSED_LIST;
|
||||
cleanup(prev);
|
||||
} else {
|
||||
throw new IllegalStateException("Attempt to cleanup an already closed resource list");
|
||||
throw alreadyClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||
* a base object (typically an array). To enhance performances, the access to the base object needs to feature
|
||||
* sharp type information, as well as sharp null-check information. For this reason, many concrete subclasses
|
||||
* of {@link HeapMemorySegmentImpl} are defined (e.g. {@link OfFloat}, so that each subclass can override the
|
||||
* {@link HeapMemorySegmentImpl#base()} method so that it returns an array of the correct (sharp) type. Note that
|
||||
* {@link HeapMemorySegmentImpl#unsafeGetBase()} method so that it returns an array of the correct (sharp) type. Note that
|
||||
* the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses
|
||||
* accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as
|
||||
* using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations,
|
||||
@ -60,54 +60,51 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
final Object base;
|
||||
|
||||
@ForceInline
|
||||
HeapMemorySegmentImpl(long offset, Object base, long length, int mask) {
|
||||
super(length, mask, MemorySessionImpl.GLOBAL);
|
||||
HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) {
|
||||
super(length, readOnly, MemorySessionImpl.GLOBAL);
|
||||
this.offset = offset;
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract Object base();
|
||||
|
||||
@Override
|
||||
long min() {
|
||||
public long unsafeGetOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract HeapMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session);
|
||||
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session);
|
||||
|
||||
@Override
|
||||
ByteBuffer makeByteBuffer() {
|
||||
if (!(base() instanceof byte[])) {
|
||||
if (!(base instanceof byte[])) {
|
||||
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
|
||||
}
|
||||
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
return nioAccess.newHeapByteBuffer((byte[]) base(), (int)min() - BYTE_ARR_BASE, (int) byteSize(), null);
|
||||
return nioAccess.newHeapByteBuffer((byte[])base, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null);
|
||||
}
|
||||
|
||||
// factories
|
||||
|
||||
public static class OfByte extends HeapMemorySegmentImpl {
|
||||
|
||||
OfByte(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfByte(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfByte dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfByte(this.offset + offset, base, size, mask);
|
||||
OfByte dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfByte(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] base() {
|
||||
public byte[] unsafeGetBase() {
|
||||
return (byte[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(byte[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
|
||||
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -118,24 +115,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfChar extends HeapMemorySegmentImpl {
|
||||
|
||||
OfChar(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfChar(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfChar dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfChar(this.offset + offset, base, size, mask);
|
||||
OfChar dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfChar(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
char[] base() {
|
||||
public char[] unsafeGetBase() {
|
||||
return (char[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(char[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
|
||||
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,24 +143,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfShort extends HeapMemorySegmentImpl {
|
||||
|
||||
OfShort(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfShort(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfShort dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfShort(this.offset + offset, base, size, mask);
|
||||
OfShort dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfShort(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
short[] base() {
|
||||
public short[] unsafeGetBase() {
|
||||
return (short[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(short[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
|
||||
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -174,24 +171,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfInt extends HeapMemorySegmentImpl {
|
||||
|
||||
OfInt(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfInt(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfInt dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfInt(this.offset + offset, base, size, mask);
|
||||
OfInt dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfInt(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
int[] base() {
|
||||
public int[] unsafeGetBase() {
|
||||
return (int[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(int[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
|
||||
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -202,24 +199,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfLong extends HeapMemorySegmentImpl {
|
||||
|
||||
OfLong(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfLong(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfLong dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfLong(this.offset + offset, base, size, mask);
|
||||
OfLong dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfLong(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] base() {
|
||||
public long[] unsafeGetBase() {
|
||||
return (long[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(long[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
|
||||
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -230,24 +227,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfFloat extends HeapMemorySegmentImpl {
|
||||
|
||||
OfFloat(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfFloat(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfFloat dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfFloat(this.offset + offset, base, size, mask);
|
||||
OfFloat dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfFloat(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
float[] base() {
|
||||
public float[] unsafeGetBase() {
|
||||
return (float[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(float[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
|
||||
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -258,24 +255,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static class OfDouble extends HeapMemorySegmentImpl {
|
||||
|
||||
OfDouble(long offset, Object base, long length, int mask) {
|
||||
super(offset, base, length, mask);
|
||||
OfDouble(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfDouble dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new OfDouble(this.offset + offset, base, size, mask);
|
||||
OfDouble dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new OfDouble(this.offset + offset, base, size, readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
double[] base() {
|
||||
public double[] unsafeGetBase() {
|
||||
return (double[])Objects.requireNonNull(base);
|
||||
}
|
||||
|
||||
public static MemorySegment fromArray(double[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
|
||||
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, DEFAULT_MODES);
|
||||
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,8 +43,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
|
||||
static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
|
||||
|
||||
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, MemorySession session) {
|
||||
super(min, length, mask, session);
|
||||
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySession session) {
|
||||
super(min, length, readOnly, session);
|
||||
this.unmapper = unmapper;
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
MappedMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new MappedMemorySegmentImpl(min + offset, unmapper, size, mask, session);
|
||||
MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, session);
|
||||
}
|
||||
|
||||
// mapped segment methods
|
||||
@ -95,8 +95,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
|
||||
public static class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl {
|
||||
|
||||
public EmptyMappedMemorySegmentImpl(int modes, MemorySession session) {
|
||||
super(0, null, 0, modes, session);
|
||||
public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) {
|
||||
super(0, null, 0, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +95,7 @@ public final class MemoryAddressImpl implements MemoryAddress, Scoped {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
public MemorySessionImpl session() {
|
||||
return MemorySessionImpl.GLOBAL;
|
||||
}
|
||||
|
||||
|
@ -43,16 +43,16 @@ import sun.nio.ch.DirectBuffer;
|
||||
* This class manages the temporal bounds associated with a memory segment as well
|
||||
* as thread confinement. A session has a liveness bit, which is updated when the session is closed
|
||||
* (this operation is triggered by {@link MemorySession#close()}). This bit is consulted prior
|
||||
* to memory access (see {@link #checkValidState()}).
|
||||
* to memory access (see {@link #checkValidStateRaw()}).
|
||||
* There are two kinds of memory session: confined memory session and shared memory session.
|
||||
* A confined memory session has an associated owner thread that confines some operations to
|
||||
* associated owner thread such as {@link #close()} or {@link #checkValidState()}.
|
||||
* associated owner thread such as {@link #close()} or {@link #checkValidStateRaw()}.
|
||||
* Shared sessions do not feature an owner thread - meaning their operations can be called, in a racy
|
||||
* manner, by multiple threads. To guarantee temporal safety in the presence of concurrent thread,
|
||||
* shared sessions use a more sophisticated synchronization mechanism, which guarantees that no concurrent
|
||||
* access is possible when a session is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
|
||||
*/
|
||||
public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySession, SegmentAllocator {
|
||||
public abstract non-sealed class MemorySessionImpl implements MemorySession, SegmentAllocator {
|
||||
final ResourceList resourceList;
|
||||
final Cleaner.Cleanable cleanable;
|
||||
final Thread owner;
|
||||
@ -78,8 +78,7 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
@Override
|
||||
public void addCloseAction(Runnable runnable) {
|
||||
Objects.requireNonNull(runnable);
|
||||
addInternal(runnable instanceof ResourceList.ResourceCleanup cleanup ?
|
||||
cleanup : ResourceList.ResourceCleanup.ofRunnable(runnable));
|
||||
addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +101,7 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
}
|
||||
|
||||
void addInternal(ResourceList.ResourceCleanup resource) {
|
||||
checkValidStateSlow();
|
||||
checkValidState();
|
||||
// Note: from here on we no longer check the session state. Two cases are possible: either the resource cleanup
|
||||
// is added to the list when the session is still open, in which case everything works ok; or the resource
|
||||
// cleanup is added while the session is being closed. In this latter case, what matters is whether we have already
|
||||
@ -141,13 +140,13 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
public abstract void acquire0();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public final boolean equals(Object o) {
|
||||
return (o instanceof MemorySession other) &&
|
||||
toSessionImpl(other) == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public final int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
@ -174,7 +173,9 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
* Returns true, if this session is still open. This method may be called in any thread.
|
||||
* @return {@code true} if this session is not closed yet.
|
||||
*/
|
||||
public abstract boolean isAlive();
|
||||
public boolean isAlive() {
|
||||
return state >= OPEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySession asNonCloseable() {
|
||||
@ -182,30 +183,27 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
new NonCloseableView(this) : this;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static MemorySessionImpl toSessionImpl(MemorySession session) {
|
||||
return ((Scoped)session).sessionImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return this;
|
||||
return session instanceof MemorySessionImpl sessionImpl ?
|
||||
sessionImpl : ((NonCloseableView)session).session;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a faster version of {@link #checkValidStateSlow()}, which is called upon memory access, and which
|
||||
* This is a faster version of {@link #checkValidState()}, which is called upon memory access, and which
|
||||
* relies on invariants associated with the memory session implementations (volatile access
|
||||
* to the closed state bit is replaced with plain access). This method should be monomorphic,
|
||||
* to avoid virtual calls in the memory access hot path. This method is not intended as general purpose method
|
||||
* and should only be used in the memory access handle hot path; for liveness checks triggered by other API methods,
|
||||
* please use {@link #checkValidStateSlow()}.
|
||||
* please use {@link #checkValidState()}.
|
||||
*/
|
||||
@ForceInline
|
||||
public final void checkValidState() {
|
||||
public void checkValidStateRaw() {
|
||||
if (owner != null && owner != Thread.currentThread()) {
|
||||
throw new WrongThreadException("Attempted access outside owning thread");
|
||||
throw WRONG_THREAD;
|
||||
}
|
||||
if (state < OPEN) {
|
||||
throw ScopedMemoryAccess.ScopedAccessError.INSTANCE;
|
||||
throw ALREADY_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,11 +212,11 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
* @throws IllegalStateException if this session is already closed or if this is
|
||||
* a confined session and this method is called outside of the owner thread.
|
||||
*/
|
||||
public final void checkValidStateSlow() {
|
||||
if (owner != null && Thread.currentThread() != owner) {
|
||||
throw new WrongThreadException("Attempted access outside owning thread");
|
||||
} else if (!isAlive()) {
|
||||
throw new IllegalStateException("Already closed");
|
||||
public void checkValidState() {
|
||||
try {
|
||||
checkValidStateRaw();
|
||||
} catch (ScopedMemoryAccess.ScopedAccessError error) {
|
||||
throw error.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,14 +287,9 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void justClose() {
|
||||
throw new UnsupportedOperationException();
|
||||
throw nonCloseable();
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,19 +328,9 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySession asNonCloseable() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void justClose() {
|
||||
throw new UnsupportedOperationException();
|
||||
throw nonCloseable();
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,17 +341,13 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
* a strong reference to the original session, so even if the original session is dropped by the client
|
||||
* it would still be reachable by the GC, which is important if the session is implicitly closed.
|
||||
*/
|
||||
public final static class NonCloseableView implements MemorySession, Scoped {
|
||||
public final static class NonCloseableView implements MemorySession {
|
||||
final MemorySessionImpl session;
|
||||
|
||||
public NonCloseableView(MemorySessionImpl session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return session.isAlive();
|
||||
@ -461,6 +440,31 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// helper functions to centralize error handling
|
||||
|
||||
static IllegalStateException tooManyAcquires() {
|
||||
return new IllegalStateException("Session acquire limit exceeded");
|
||||
}
|
||||
|
||||
static IllegalStateException alreadyAcquired(int acquires) {
|
||||
return new IllegalStateException(String.format("Session is acquired by %d clients", acquires));
|
||||
}
|
||||
|
||||
static IllegalStateException alreadyClosed() {
|
||||
return new IllegalStateException("Already closed");
|
||||
}
|
||||
|
||||
static WrongThreadException wrongThread() {
|
||||
return new WrongThreadException("Attempted access outside owning thread");
|
||||
}
|
||||
|
||||
static UnsupportedOperationException nonCloseable() {
|
||||
return new UnsupportedOperationException("Attempted to close a non-closeable session");
|
||||
}
|
||||
|
||||
static final ScopedMemoryAccess.ScopedAccessError ALREADY_CLOSED = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::alreadyClosed);
|
||||
|
||||
static final ScopedMemoryAccess.ScopedAccessError WRONG_THREAD = new ScopedMemoryAccess.ScopedAccessError(MemorySessionImpl::wrongThread);
|
||||
}
|
||||
|
@ -41,14 +41,14 @@ import sun.security.action.GetBooleanAction;
|
||||
*/
|
||||
public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static final MemorySegment EVERYTHING = new NativeMemorySegmentImpl(0, Long.MAX_VALUE, 0, MemorySessionImpl.GLOBAL) {
|
||||
public static final MemorySegment EVERYTHING = new NativeMemorySegmentImpl(0, Long.MAX_VALUE, false, MemorySessionImpl.GLOBAL) {
|
||||
@Override
|
||||
void checkBounds(long offset, long length) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
NativeMemorySegmentImpl dup(long offset, long size, int mask, MemorySession scope) {
|
||||
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
};
|
||||
@ -64,8 +64,8 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
final long min;
|
||||
|
||||
@ForceInline
|
||||
NativeMemorySegmentImpl(long min, long length, int mask, MemorySession session) {
|
||||
super(length, mask, session);
|
||||
NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySession session) {
|
||||
super(length, readOnly, session);
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
@ -77,13 +77,13 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
NativeMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session) {
|
||||
return new NativeMemorySegmentImpl(min + offset, size, mask, session);
|
||||
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
|
||||
return new NativeMemorySegmentImpl(min + offset, size, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
ByteBuffer makeByteBuffer() {
|
||||
return nioAccess.newDirectByteBuffer(min(), (int) this.length, null,
|
||||
return nioAccess.newDirectByteBuffer(min, (int) this.length, null,
|
||||
session == MemorySessionImpl.GLOBAL ? null : this);
|
||||
}
|
||||
|
||||
@ -93,12 +93,12 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
long min() {
|
||||
public long unsafeGetOffset() {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
Object base() {
|
||||
public Object unsafeGetBase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes, MemorySession session) {
|
||||
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
|
||||
sessionImpl.checkValidStateSlow();
|
||||
sessionImpl.checkValidState();
|
||||
if (VM.isDirectMemoryPageAligned()) {
|
||||
alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize());
|
||||
}
|
||||
@ -127,7 +127,7 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
}
|
||||
long alignedBuf = Utils.alignUp(buf, alignmentBytes);
|
||||
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
|
||||
DEFAULT_MODES, session);
|
||||
false, session);
|
||||
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
@ -143,9 +143,8 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
}
|
||||
|
||||
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, MemorySession session) {
|
||||
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
|
||||
sessionImpl.checkValidStateSlow();
|
||||
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, DEFAULT_MODES, session);
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidState();
|
||||
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, false, session);
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,14 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.MemorySession;
|
||||
|
||||
public interface Scoped {
|
||||
MemorySessionImpl sessionImpl();
|
||||
@ForceInline
|
||||
default MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session());
|
||||
}
|
||||
MemorySession session();
|
||||
}
|
||||
|
@ -56,10 +56,10 @@ class SharedSession extends MemorySessionImpl {
|
||||
value = (int) STATE.getVolatile(this);
|
||||
if (value < OPEN) {
|
||||
//segment is not open!
|
||||
throw new IllegalStateException("Already closed");
|
||||
throw alreadyClosed();
|
||||
} else if (value == MAX_FORKS) {
|
||||
//overflow
|
||||
throw new IllegalStateException("Session acquire limit exceeded");
|
||||
throw tooManyAcquires();
|
||||
}
|
||||
} while (!STATE.compareAndSet(this, value, value + 1));
|
||||
}
|
||||
@ -72,7 +72,7 @@ class SharedSession extends MemorySessionImpl {
|
||||
value = (int) STATE.getVolatile(this);
|
||||
if (value <= OPEN) {
|
||||
//cannot get here - we can't close segment twice
|
||||
throw new IllegalStateException("Already closed");
|
||||
throw alreadyClosed();
|
||||
}
|
||||
} while (!STATE.compareAndSet(this, value, value - 1));
|
||||
}
|
||||
@ -80,22 +80,17 @@ class SharedSession extends MemorySessionImpl {
|
||||
void justClose() {
|
||||
int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSING);
|
||||
if (prevState < 0) {
|
||||
throw new IllegalStateException("Already closed");
|
||||
throw alreadyClosed();
|
||||
} else if (prevState != OPEN) {
|
||||
throw new IllegalStateException("Session is acquired by " + prevState + " clients");
|
||||
throw alreadyAcquired(prevState);
|
||||
}
|
||||
boolean success = SCOPED_MEMORY_ACCESS.closeScope(this);
|
||||
STATE.setVolatile(this, success ? CLOSED : OPEN);
|
||||
if (!success) {
|
||||
throw new IllegalStateException("Session is acquired by 1 client");
|
||||
throw alreadyAcquired(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return (int) STATE.getVolatile(this) != CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* A shared resource list; this implementation has to handle add vs. add races, as well as add vs. cleanup races.
|
||||
*/
|
||||
@ -117,7 +112,7 @@ class SharedSession extends MemorySessionImpl {
|
||||
ResourceCleanup prev = (ResourceCleanup) FST.getVolatile(this);
|
||||
if (prev == ResourceCleanup.CLOSED_LIST) {
|
||||
// too late
|
||||
throw new IllegalStateException("Already closed");
|
||||
throw alreadyClosed();
|
||||
}
|
||||
cleanup.next = prev;
|
||||
if (FST.compareAndSet(this, prev, cleanup)) {
|
||||
@ -144,7 +139,7 @@ class SharedSession extends MemorySessionImpl {
|
||||
}
|
||||
cleanup(prev);
|
||||
} else {
|
||||
throw new IllegalStateException("Attempt to cleanup an already closed resource list");
|
||||
throw alreadyClosed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ import java.lang.ref.Reference;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
@ -382,13 +383,15 @@ public class SharedUtils {
|
||||
return firstPos != -1 && argIndex >= firstPos;
|
||||
}
|
||||
|
||||
public static NoSuchElementException newVaListNSEE(MemoryLayout layout) {
|
||||
return new NoSuchElementException("No such element: " + layout);
|
||||
}
|
||||
|
||||
public static class SimpleVaArg {
|
||||
public final Class<?> carrier;
|
||||
public final MemoryLayout layout;
|
||||
public final Object value;
|
||||
|
||||
public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
this.carrier = carrier;
|
||||
public SimpleVaArg(MemoryLayout layout, Object value) {
|
||||
this.layout = layout;
|
||||
this.value = value;
|
||||
}
|
||||
@ -445,11 +448,6 @@ public class SharedUtils {
|
||||
return MemorySessionImpl.GLOBAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.GLOBAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
return this;
|
||||
|
@ -52,8 +52,6 @@ import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMEN
|
||||
public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
|
||||
// See AAPCS Appendix B "Variable Argument Lists" for definition of
|
||||
// va_list on AArch64.
|
||||
//
|
||||
@ -73,6 +71,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
AArch64.C_INT.withName("__vr_offs")
|
||||
).withName("__va_list");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG
|
||||
= MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG
|
||||
@ -101,22 +101,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
= new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment stack;
|
||||
private final MemorySegment gpRegsArea;
|
||||
private final long gpLimit;
|
||||
private final MemorySegment fpRegsArea;
|
||||
private final long fpLimit;
|
||||
|
||||
private LinuxAArch64VaList(MemorySegment segment, MemorySegment gpRegsArea, MemorySegment fpRegsArea) {
|
||||
private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack,
|
||||
MemorySegment gpRegsArea, long gpLimit, MemorySegment fpRegsArea, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.stack = stack;
|
||||
this.gpRegsArea = gpRegsArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpRegsArea = fpRegsArea;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static LinuxAArch64VaList readFromSegment(MemorySegment segment) {
|
||||
MemorySegment stack = MemorySegment.ofAddress(stackPtr(segment),
|
||||
Long.MAX_VALUE, segment.session()); // size unknown
|
||||
|
||||
MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET),
|
||||
MAX_GP_OFFSET, segment.session());
|
||||
|
||||
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET),
|
||||
MAX_FP_OFFSET, segment.session());
|
||||
return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea);
|
||||
return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
@ -165,12 +175,17 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
return offs;
|
||||
}
|
||||
|
||||
private MemoryAddress stackPtr() {
|
||||
private static MemoryAddress stackPtr(MemorySegment segment) {
|
||||
return (MemoryAddress) VH_stack.get(segment);
|
||||
}
|
||||
|
||||
private void stackPtr(MemoryAddress ptr) {
|
||||
VH_stack.set(segment, ptr);
|
||||
private MemoryAddress stackPtr() {
|
||||
return stackPtr(segment);
|
||||
}
|
||||
|
||||
private void setStack(MemorySegment newStack) {
|
||||
stack = newStack;
|
||||
VH_stack.set(segment, stack.address());
|
||||
}
|
||||
|
||||
private void consumeGPSlots(int num) {
|
||||
@ -199,54 +214,62 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
return fpRegsArea.byteSize() + vrOffs();
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteAlignment() > 8) {
|
||||
stackPtr(Utils.alignUp(stackPtr(), 16));
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = stack.address().toRawLongValue();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setStack(stack.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8));
|
||||
setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read( layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
@ -254,7 +277,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
@ -263,6 +286,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
} else {
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
checkGPElement(layout, numSlots(layout));
|
||||
// Struct is passed packed in integer registers.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
long offset = 0;
|
||||
@ -275,6 +299,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
yield value;
|
||||
}
|
||||
case STRUCT_HFA -> {
|
||||
checkFPElement(layout, numSlots(layout));
|
||||
// Struct is passed with each element in a separate floating
|
||||
// point register.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
@ -290,6 +315,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
yield value;
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkGPElement(layout, 1);
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
VarHandle ptrReader = AArch64.C_POINTER.varHandle();
|
||||
MemoryAddress ptr = (MemoryAddress) ptrReader.get(
|
||||
@ -302,12 +328,14 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
checkGPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
checkFPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
|
||||
consumeFPSlots(1);
|
||||
@ -317,22 +345,46 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkGPElement(MemoryLayout layout, long slots) {
|
||||
if ((grOffs() + MAX_GP_OFFSET) + (slots * GP_SLOT_SIZE) > gpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFPElement(MemoryLayout layout, long slots) {
|
||||
if ((vrOffs() + MAX_FP_OFFSET) + (slots * FP_SLOT_SIZE) > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
if (preAlignOffset(layout) + layout.byteSize() > stack.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
|
||||
consumeFPSlots(numSlots(layout));
|
||||
long slots = numSlots(layout);
|
||||
checkFPElement(layout, slots);
|
||||
consumeFPSlots((int) slots);
|
||||
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
|
||||
checkGPElement(layout, 1);
|
||||
consumeGPSlots(1);
|
||||
} else {
|
||||
consumeGPSlots(numSlots(layout));
|
||||
long slots = numSlots(layout);
|
||||
checkGPElement(layout, slots);
|
||||
consumeGPSlots((int) slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -350,16 +402,11 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
return segment.session();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
|
||||
copy.copyFrom(segment);
|
||||
return new LinuxAArch64VaList(copy, gpRegsArea, fpRegsArea);
|
||||
return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -367,8 +414,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
return segment.address();
|
||||
}
|
||||
|
||||
private static int numSlots(MemoryLayout layout) {
|
||||
return (int) Utils.alignUp(layout.byteSize(), 8) / 8;
|
||||
private static long numSlots(MemoryLayout layout) {
|
||||
return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE;
|
||||
}
|
||||
|
||||
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset,
|
||||
@ -410,35 +457,35 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
|
||||
stackArgs.add(new SimpleVaArg(carrier, layout, value));
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
@ -500,30 +547,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream()
|
||||
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), 8), Long::sum);
|
||||
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
stackArgsPtr = stackArgsSegment.address();
|
||||
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), 8);
|
||||
stackArgsSegment = Utils.alignUp(stackArgsSegment, alignedSize);
|
||||
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE);
|
||||
writeCursor = Utils.alignUp(writeCursor, alignedSize);
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(stackArgsSegment, arg.value);
|
||||
stackArgsSegment = stackArgsSegment.asSlice(alignedSize);
|
||||
writer.set(writeCursor, arg.value);
|
||||
writeCursor = writeCursor.asSlice(alignedSize);
|
||||
}
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session);
|
||||
}
|
||||
|
||||
VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()).address());
|
||||
VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize()).address());
|
||||
VH_stack.set(vaListSegment, stackArgsPtr);
|
||||
VH_stack.set(vaListSegment, stackArgsSegment.address());
|
||||
VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET);
|
||||
VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET);
|
||||
|
||||
assert gpRegs.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
assert fpRegs.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
return new LinuxAArch64VaList(vaListSegment, gpRegs, fpRegs);
|
||||
return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ import static jdk.internal.foreign.abi.SharedUtils.alignUp;
|
||||
* char* instead of the structure defined in the AAPCS.
|
||||
*/
|
||||
public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
public static final Class<?> CARRIER = MemoryAddress.class;
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
@ -66,41 +65,42 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
@ -109,14 +109,17 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
checkElement(layout, size);
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
segment = segment.asSlice(size);
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
@ -124,17 +127,29 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static long sizeOf(MemoryLayout layout) {
|
||||
return switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
segment = segment.asSlice(switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
});
|
||||
long size = sizeOf(layout);
|
||||
checkElement(layout, size);
|
||||
segment = segment.asSlice(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout, long size) {
|
||||
if (segment.byteSize() < size) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,14 +167,9 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
return new MacOsAArch64VaList(segment, session);
|
||||
}
|
||||
|
||||
@ -174,40 +184,40 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(MemorySession session) {
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow();
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidState();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
@ -217,22 +227,18 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
|
||||
// Each argument may occupy up to four slots
|
||||
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4);
|
||||
|
||||
List<MemorySegment> attachedSegments = new ArrayList<>();
|
||||
attachedSegments.add(segment);
|
||||
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
|
||||
MemorySegment segment = allocator.allocate(allocationSize);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.carrier == MemorySegment.class) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = allocator.allocate(arg.layout);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
attachedSegments.add(copy);
|
||||
VH_address.set(cursor, copy.address());
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
|
||||
public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
|
||||
// struct typedef __va_list_tag __va_list_tag {
|
||||
// unsigned int gp_offset; /* 0 4 */
|
||||
// unsigned int fp_offset; /* 4 4 */
|
||||
@ -66,6 +64,8 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
SysV.C_POINTER.withName("reg_save_area")
|
||||
).withName("__va_list_tag");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
@ -112,16 +112,25 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment overflowArgArea;
|
||||
private final MemorySegment regSaveArea;
|
||||
private final long gpLimit;
|
||||
private final long fpLimit;
|
||||
|
||||
private SysVVaList(MemorySegment segment, MemorySegment regSaveArea) {
|
||||
private SysVVaList(MemorySegment segment,
|
||||
MemorySegment overflowArgArea,
|
||||
MemorySegment regSaveArea, long gpLimit, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.overflowArgArea = overflowArgArea;
|
||||
this.regSaveArea = regSaveArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static SysVVaList readFromSegment(MemorySegment segment) {
|
||||
MemorySegment regSaveArea = getRegSaveArea(segment);
|
||||
return new SysVVaList(segment, regSaveArea);
|
||||
MemorySegment overflowArgArea = getArgOverflowArea(segment);
|
||||
return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
@ -157,72 +166,78 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
VH_fp_offset.set(segment, i);
|
||||
}
|
||||
|
||||
private MemoryAddress stackPtr() {
|
||||
return (MemoryAddress) VH_overflow_arg_area.get(segment);
|
||||
}
|
||||
|
||||
private void stackPtr(MemoryAddress ptr) {
|
||||
VH_overflow_arg_area.set(segment, ptr);
|
||||
}
|
||||
|
||||
private MemorySegment regSaveArea() {
|
||||
return getRegSaveArea(segment);
|
||||
}
|
||||
|
||||
private static MemorySegment getRegSaveArea(MemorySegment segment) {
|
||||
return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)),
|
||||
LAYOUT_REG_SAVE_AREA.byteSize(), segment.session());
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteAlignment() > 8) {
|
||||
stackPtr(Utils.alignUp(stackPtr(), 16));
|
||||
private static MemorySegment getArgOverflowArea(MemorySegment segment) {
|
||||
return MemorySegment.ofAddress(((MemoryAddress)VH_overflow_arg_area.get(segment)),
|
||||
Long.MAX_VALUE, segment.session()); // size unknown
|
||||
}
|
||||
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = overflowArgArea.address().toRawLongValue();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void setOverflowArgArea(MemorySegment newSegment) {
|
||||
overflowArgArea = newSegment;
|
||||
VH_overflow_arg_area.set(segment, overflowArgArea.address());
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setOverflowArgArea(overflowArgArea.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8));
|
||||
setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
@ -230,15 +245,14 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
try (MemorySession localSession = MemorySession.openConfined()) {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), localSession);
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
@ -274,17 +288,35 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegSaveAreaElement(MemoryLayout layout, TypeClass typeClass) {
|
||||
long gpSize = typeClass.nIntegerRegs() * GP_SLOT_SIZE;
|
||||
long fpSize = typeClass.nVectorRegs() * FP_SLOT_SIZE;
|
||||
if (currentGPOffset() + gpSize > gpLimit
|
||||
|| currentFPOffset() + fpSize > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
long offset = preAlignOffset(layout);
|
||||
if (offset + layout.byteSize() > overflowArgArea.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE));
|
||||
currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_SLOT_SIZE));
|
||||
}
|
||||
@ -304,16 +336,11 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
return segment.session();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
|
||||
copy.copyFrom(segment);
|
||||
return new SysVVaList(copy, regSaveArea);
|
||||
return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -331,8 +358,8 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
return "SysVVaList{"
|
||||
+ "gp_offset=" + currentGPOffset()
|
||||
+ ", fp_offset=" + currentFPOffset()
|
||||
+ ", overflow_arg_area=" + stackPtr()
|
||||
+ ", reg_save_area=" + regSaveArea()
|
||||
+ ", overflow_arg_area=" + overflowArgArea
|
||||
+ ", reg_save_area=" + regSaveArea
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@ -350,37 +377,37 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
// stack it!
|
||||
stackArgs.add(new SimpleVaArg(carrier, layout, value));
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
@ -426,31 +453,33 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream().reduce(0L, (acc, e) -> acc + e.layout.byteSize(), Long::sum);
|
||||
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment maOverflowArgArea = stackArgsSegment;
|
||||
long stackArgsSize = stackArgs.stream().reduce(0L,
|
||||
(acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
if (arg.layout.byteSize() > 8) {
|
||||
maOverflowArgArea = Utils.alignUp(maOverflowArgArea, Math.min(16, arg.layout.byteSize()));
|
||||
writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize()));
|
||||
}
|
||||
if (arg.value instanceof MemorySegment) {
|
||||
maOverflowArgArea.copyFrom((MemorySegment) arg.value);
|
||||
writeCursor.copyFrom((MemorySegment) arg.value);
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(maOverflowArgArea, arg.value);
|
||||
writer.set(writeCursor, arg.value);
|
||||
}
|
||||
maOverflowArgArea = maOverflowArgArea.asSlice(arg.layout.byteSize());
|
||||
writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE));
|
||||
}
|
||||
stackArgsPtr = stackArgsSegment.address();
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session);
|
||||
}
|
||||
|
||||
VH_fp_offset.set(vaListSegment, (int) FP_OFFSET);
|
||||
VH_overflow_arg_area.set(vaListSegment, stackArgsPtr);
|
||||
VH_overflow_arg_area.set(vaListSegment, stackArgsSegment.address());
|
||||
VH_reg_save_area.set(vaListSegment, reg_save_area.address());
|
||||
assert reg_save_area.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
return new SysVVaList(vaListSegment, reg_save_area);
|
||||
return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
|
||||
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
|
||||
//
|
||||
public non-sealed class WinVaList implements VaList, Scoped {
|
||||
public static final Class<?> CARRIER = MemoryAddress.class;
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
@ -77,38 +76,39 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
checkElement(layout);
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.typeClassFor(layout, false);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
@ -130,12 +130,21 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
||||
return res;
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout) {
|
||||
if (segment.byteSize() < VA_SLOT_SIZE_BYTES) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow();
|
||||
Stream.of(layouts).forEach(Objects::requireNonNull);
|
||||
segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
|
||||
sessionImpl().checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
checkElement(layout);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static WinVaList ofAddress(MemoryAddress addr, MemorySession session) {
|
||||
@ -152,14 +161,9 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySessionImpl sessionImpl() {
|
||||
return MemorySessionImpl.toSessionImpl(session());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow();
|
||||
sessionImpl().checkValidState();
|
||||
return new WinVaList(segment, session);
|
||||
}
|
||||
|
||||
@ -174,40 +178,40 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(MemorySession session) {
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow();
|
||||
MemorySessionImpl.toSessionImpl(session).checkValidState();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
@ -216,19 +220,16 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
||||
}
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
|
||||
List<MemorySegment> attachedSegments = new ArrayList<>();
|
||||
attachedSegments.add(segment);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.carrier == MemorySegment.class) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = allocator.allocate(arg.layout);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
attachedSegments.add(copy);
|
||||
VH_address.set(cursor, copy.address());
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
|
@ -3,7 +3,7 @@
|
||||
try {
|
||||
return get$Type$Internal(session, base, offset);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
private $type$ get$Type$Internal(MemorySessionImpl session, Object base, long offset) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.get$Type$(base, offset);
|
||||
} finally {
|
||||
@ -24,7 +24,7 @@
|
||||
try {
|
||||
put$Type$Internal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
private void put$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.put$Type$(base, offset, value);
|
||||
} finally {
|
||||
@ -46,7 +46,7 @@
|
||||
try {
|
||||
return get$Type$UnalignedInternal(session, base, offset, be);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
private $type$ get$Type$UnalignedInternal(MemorySessionImpl session, Object base, long offset, boolean be) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.get$Type$Unaligned(base, offset, be);
|
||||
} finally {
|
||||
@ -67,7 +67,7 @@
|
||||
try {
|
||||
put$Type$UnalignedInternal(session, base, offset, value, be);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@
|
||||
private void put$Type$UnalignedInternal(MemorySessionImpl session, Object base, long offset, $type$ value, boolean be) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.put$Type$Unaligned(base, offset, value, be);
|
||||
} finally {
|
||||
@ -89,7 +89,7 @@
|
||||
try {
|
||||
return get$Type$VolatileInternal(session, base, offset);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
private $type$ get$Type$VolatileInternal(MemorySessionImpl session, Object base, long offset) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.get$Type$Volatile(base, offset);
|
||||
} finally {
|
||||
@ -110,7 +110,7 @@
|
||||
try {
|
||||
put$Type$VolatileInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@
|
||||
private void put$Type$VolatileInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.put$Type$Volatile(base, offset, value);
|
||||
} finally {
|
||||
@ -131,7 +131,7 @@
|
||||
try {
|
||||
return get$Type$AcquireInternal(session, base, offset);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@
|
||||
private $type$ get$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.get$Type$Acquire(base, offset);
|
||||
} finally {
|
||||
@ -152,7 +152,7 @@
|
||||
try {
|
||||
put$Type$ReleaseInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@
|
||||
private void put$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.put$Type$Release(base, offset, value);
|
||||
} finally {
|
||||
@ -173,7 +173,7 @@
|
||||
try {
|
||||
return get$Type$OpaqueInternal(session, base, offset);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@
|
||||
private $type$ get$Type$OpaqueInternal(MemorySessionImpl session, Object base, long offset) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.get$Type$Opaque(base, offset);
|
||||
} finally {
|
||||
@ -193,7 +193,7 @@
|
||||
try {
|
||||
put$Type$OpaqueInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@
|
||||
private void put$Type$OpaqueInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.put$Type$Opaque(base, offset, value);
|
||||
} finally {
|
||||
@ -214,7 +214,7 @@
|
||||
try {
|
||||
return compareAndSet$Type$Internal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@
|
||||
private boolean compareAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.compareAndSet$Type$(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -235,7 +235,7 @@
|
||||
try {
|
||||
return compareAndExchange$Type$Internal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@
|
||||
private $type$ compareAndExchange$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.compareAndExchange$Type$(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -256,7 +256,7 @@
|
||||
try {
|
||||
return compareAndExchange$Type$AcquireInternal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@
|
||||
private $type$ compareAndExchange$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.compareAndExchange$Type$Acquire(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -277,7 +277,7 @@
|
||||
try {
|
||||
return compareAndExchange$Type$ReleaseInternal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@
|
||||
private $type$ compareAndExchange$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.compareAndExchange$Type$Release(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -298,7 +298,7 @@
|
||||
try {
|
||||
return weakCompareAndSet$Type$PlainInternal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +306,7 @@
|
||||
private boolean weakCompareAndSet$Type$PlainInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.weakCompareAndSet$Type$Plain(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -319,7 +319,7 @@
|
||||
try {
|
||||
return weakCompareAndSet$Type$Internal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,7 +327,7 @@
|
||||
private boolean weakCompareAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.weakCompareAndSet$Type$(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -340,7 +340,7 @@
|
||||
try {
|
||||
return weakCompareAndSet$Type$AcquireInternal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,7 +348,7 @@
|
||||
private boolean weakCompareAndSet$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.weakCompareAndSet$Type$Acquire(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -361,7 +361,7 @@
|
||||
try {
|
||||
return weakCompareAndSet$Type$ReleaseInternal(session, base, offset, expected, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,7 +369,7 @@
|
||||
private boolean weakCompareAndSet$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.weakCompareAndSet$Type$Release(base, offset, expected, value);
|
||||
} finally {
|
||||
@ -382,7 +382,7 @@
|
||||
try {
|
||||
return getAndSet$Type$Internal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,7 +390,7 @@
|
||||
private $type$ getAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndSet$Type$(base, offset, value);
|
||||
} finally {
|
||||
@ -403,7 +403,7 @@
|
||||
try {
|
||||
return getAndSet$Type$AcquireInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,7 +411,7 @@
|
||||
private $type$ getAndSet$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndSet$Type$Acquire(base, offset, value);
|
||||
} finally {
|
||||
@ -424,7 +424,7 @@
|
||||
try {
|
||||
return getAndSet$Type$ReleaseInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +432,7 @@
|
||||
private $type$ getAndSet$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndSet$Type$Release(base, offset, value);
|
||||
} finally {
|
||||
@ -447,7 +447,7 @@
|
||||
try {
|
||||
return getAndAdd$Type$Internal(session, base, offset, delta);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@
|
||||
private $type$ getAndAdd$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndAdd$Type$(base, offset, delta);
|
||||
} finally {
|
||||
@ -468,7 +468,7 @@
|
||||
try {
|
||||
return getAndAdd$Type$AcquireInternal(session, base, offset, delta);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,7 +476,7 @@
|
||||
private $type$ getAndAdd$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndAdd$Type$Acquire(base, offset, delta);
|
||||
} finally {
|
||||
@ -489,7 +489,7 @@
|
||||
try {
|
||||
return getAndAdd$Type$ReleaseInternal(session, base, offset, delta);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,7 +497,7 @@
|
||||
private $type$ getAndAdd$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndAdd$Type$Release(base, offset, delta);
|
||||
} finally {
|
||||
@ -512,7 +512,7 @@
|
||||
try {
|
||||
return getAndBitwiseOr$Type$Internal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -520,7 +520,7 @@
|
||||
private $type$ getAndBitwiseOr$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseOr$Type$(base, offset, value);
|
||||
} finally {
|
||||
@ -533,7 +533,7 @@
|
||||
try {
|
||||
return getAndBitwiseOr$Type$AcquireInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -541,7 +541,7 @@
|
||||
private $type$ getAndBitwiseOr$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseOr$Type$Acquire(base, offset, value);
|
||||
} finally {
|
||||
@ -554,7 +554,7 @@
|
||||
try {
|
||||
return getAndBitwiseOr$Type$ReleaseInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,7 +562,7 @@
|
||||
private $type$ getAndBitwiseOr$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseOr$Type$Release(base, offset, value);
|
||||
} finally {
|
||||
@ -575,7 +575,7 @@
|
||||
try {
|
||||
return getAndBitwiseAnd$Type$Internal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -583,7 +583,7 @@
|
||||
private $type$ getAndBitwiseAnd$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseAnd$Type$(base, offset, value);
|
||||
} finally {
|
||||
@ -596,7 +596,7 @@
|
||||
try {
|
||||
return getAndBitwiseAnd$Type$AcquireInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,7 +604,7 @@
|
||||
private $type$ getAndBitwiseAnd$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseAnd$Type$Acquire(base, offset, value);
|
||||
} finally {
|
||||
@ -617,7 +617,7 @@
|
||||
try {
|
||||
return getAndBitwiseAnd$Type$ReleaseInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,7 +625,7 @@
|
||||
private $type$ getAndBitwiseAnd$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseAnd$Type$Release(base, offset, value);
|
||||
} finally {
|
||||
@ -638,7 +638,7 @@
|
||||
try {
|
||||
return getAndBitwiseXor$Type$Internal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,7 +646,7 @@
|
||||
private $type$ getAndBitwiseXor$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseXor$Type$(base, offset, value);
|
||||
} finally {
|
||||
@ -659,7 +659,7 @@
|
||||
try {
|
||||
return getAndBitwiseXor$Type$AcquireInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,7 +667,7 @@
|
||||
private $type$ getAndBitwiseXor$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseXor$Type$Acquire(base, offset, value);
|
||||
} finally {
|
||||
@ -680,7 +680,7 @@
|
||||
try {
|
||||
return getAndBitwiseXor$Type$ReleaseInternal(session, base, offset, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,7 +688,7 @@
|
||||
private $type$ getAndBitwiseXor$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return UNSAFE.getAndBitwiseXor$Type$Release(base, offset, value);
|
||||
} finally {
|
||||
|
@ -32,6 +32,7 @@ import java.lang.annotation.Target;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.ref.Reference;
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
@ -55,10 +56,10 @@ import jdk.internal.vm.vector.VectorSupport;
|
||||
* a memory region while another thread is releasing it.
|
||||
* <p>
|
||||
* This class provides tools to manage races when multiple threads are accessing and/or releasing the same memory
|
||||
* region concurrently. More specifically, when a thread wants to release a memory region, it should call the
|
||||
* {@link MemorySessionImpl#close()} method. This method initiates thread-local handshakes with all the other VM threads,
|
||||
* session concurrently. More specifically, when a thread wants to release a memory session, it should call the
|
||||
* {@link ScopedMemoryAccess#closeScope(MemorySessionImpl)} method. This method initiates thread-local handshakes with all the other VM threads,
|
||||
* which are then stopped one by one. If any thread is found accessing a resource associated to the very memory session
|
||||
* being closed, the handshake fails, and the session cannot be closed.
|
||||
* being closed, the handshake fails, and the session will not be closed.
|
||||
* <p>
|
||||
* This synchronization strategy relies on the idea that accessing memory is atomic with respect to checking the
|
||||
* validity of the session associated with that memory region - that is, a thread that wants to perform memory access will be
|
||||
@ -97,12 +98,20 @@ public class ScopedMemoryAccess {
|
||||
}
|
||||
|
||||
public static final class ScopedAccessError extends Error {
|
||||
private ScopedAccessError() {
|
||||
super("Attempt to access an already released memory resource", null, false, false);
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private final Supplier<RuntimeException> runtimeExceptionSupplier;
|
||||
|
||||
public ScopedAccessError(Supplier<RuntimeException> runtimeExceptionSupplier) {
|
||||
super("Invalid memory access", null, false, false);
|
||||
this.runtimeExceptionSupplier = runtimeExceptionSupplier;
|
||||
}
|
||||
|
||||
static final long serialVersionUID = 1L;
|
||||
|
||||
public static final ScopedAccessError INSTANCE = new ScopedAccessError();
|
||||
public final RuntimeException newRuntimeException() {
|
||||
return runtimeExceptionSupplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
|
||||
@ -112,64 +121,64 @@ public class ScopedMemoryAccess {
|
||||
// bulk ops
|
||||
|
||||
@ForceInline
|
||||
public void copyMemory(MemorySessionImpl srcScope, MemorySessionImpl dstScope,
|
||||
public void copyMemory(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
|
||||
Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes) {
|
||||
try {
|
||||
copyMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes);
|
||||
copyMemoryInternal(srcSession, dstSession, srcBase, srcOffset, destBase, destOffset, bytes);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline @Scoped
|
||||
private void copyMemoryInternal(MemorySessionImpl srcScope, MemorySessionImpl dstScope,
|
||||
private void copyMemoryInternal(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
|
||||
Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes) {
|
||||
try {
|
||||
if (srcScope != null) {
|
||||
srcScope.checkValidState();
|
||||
if (srcSession != null) {
|
||||
srcSession.checkValidStateRaw();
|
||||
}
|
||||
if (dstScope != null) {
|
||||
dstScope.checkValidState();
|
||||
if (dstSession != null) {
|
||||
dstSession.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
|
||||
} finally {
|
||||
Reference.reachabilityFence(srcScope);
|
||||
Reference.reachabilityFence(dstScope);
|
||||
Reference.reachabilityFence(srcSession);
|
||||
Reference.reachabilityFence(dstSession);
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public void copySwapMemory(MemorySessionImpl srcScope, MemorySessionImpl dstScope,
|
||||
public void copySwapMemory(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
|
||||
Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize) {
|
||||
try {
|
||||
copySwapMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
copySwapMemoryInternal(srcSession, dstSession, srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline @Scoped
|
||||
private void copySwapMemoryInternal(MemorySessionImpl srcScope, MemorySessionImpl dstScope,
|
||||
private void copySwapMemoryInternal(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
|
||||
Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize) {
|
||||
try {
|
||||
if (srcScope != null) {
|
||||
srcScope.checkValidState();
|
||||
if (srcSession != null) {
|
||||
srcSession.checkValidStateRaw();
|
||||
}
|
||||
if (dstScope != null) {
|
||||
dstScope.checkValidState();
|
||||
if (dstSession != null) {
|
||||
dstSession.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.copySwapMemory(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
} finally {
|
||||
Reference.reachabilityFence(srcScope);
|
||||
Reference.reachabilityFence(dstScope);
|
||||
Reference.reachabilityFence(srcSession);
|
||||
Reference.reachabilityFence(dstSession);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,7 +187,7 @@ public class ScopedMemoryAccess {
|
||||
try {
|
||||
setMemoryInternal(session, o, offset, bytes, value);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,7 +195,7 @@ public class ScopedMemoryAccess {
|
||||
private void setMemoryInternal(MemorySessionImpl session, Object o, long offset, long bytes, byte value) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
UNSAFE.setMemory(o, offset, bytes, value);
|
||||
} finally {
|
||||
@ -195,35 +204,35 @@ public class ScopedMemoryAccess {
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public int vectorizedMismatch(MemorySessionImpl aScope, MemorySessionImpl bScope,
|
||||
public int vectorizedMismatch(MemorySessionImpl aSession, MemorySessionImpl bSession,
|
||||
Object a, long aOffset,
|
||||
Object b, long bOffset,
|
||||
int length,
|
||||
int log2ArrayIndexScale) {
|
||||
try {
|
||||
return vectorizedMismatchInternal(aScope, bScope, a, aOffset, b, bOffset, length, log2ArrayIndexScale);
|
||||
return vectorizedMismatchInternal(aSession, bSession, a, aOffset, b, bOffset, length, log2ArrayIndexScale);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ForceInline @Scoped
|
||||
private int vectorizedMismatchInternal(MemorySessionImpl aScope, MemorySessionImpl bScope,
|
||||
private int vectorizedMismatchInternal(MemorySessionImpl aSession, MemorySessionImpl bSession,
|
||||
Object a, long aOffset,
|
||||
Object b, long bOffset,
|
||||
int length,
|
||||
int log2ArrayIndexScale) {
|
||||
try {
|
||||
if (aScope != null) {
|
||||
aScope.checkValidState();
|
||||
if (aSession != null) {
|
||||
aSession.checkValidStateRaw();
|
||||
}
|
||||
if (bScope != null) {
|
||||
bScope.checkValidState();
|
||||
if (bSession != null) {
|
||||
bSession.checkValidStateRaw();
|
||||
}
|
||||
return ArraysSupport.vectorizedMismatch(a, aOffset, b, bOffset, length, log2ArrayIndexScale);
|
||||
} finally {
|
||||
Reference.reachabilityFence(aScope);
|
||||
Reference.reachabilityFence(bScope);
|
||||
Reference.reachabilityFence(aSession);
|
||||
Reference.reachabilityFence(bSession);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,7 +241,7 @@ public class ScopedMemoryAccess {
|
||||
try {
|
||||
return isLoadedInternal(session, address, isSync, size);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +249,7 @@ public class ScopedMemoryAccess {
|
||||
public boolean isLoadedInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
return SharedSecrets.getJavaNioAccess().isLoaded(address, isSync, size);
|
||||
} finally {
|
||||
@ -253,7 +262,7 @@ public class ScopedMemoryAccess {
|
||||
try {
|
||||
loadInternal(session, address, isSync, size);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,7 +270,7 @@ public class ScopedMemoryAccess {
|
||||
public void loadInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
SharedSecrets.getJavaNioAccess().load(address, isSync, size);
|
||||
} finally {
|
||||
@ -274,7 +283,7 @@ public class ScopedMemoryAccess {
|
||||
try {
|
||||
unloadInternal(session, address, isSync, size);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +291,7 @@ public class ScopedMemoryAccess {
|
||||
public void unloadInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
SharedSecrets.getJavaNioAccess().unload(address, isSync, size);
|
||||
} finally {
|
||||
@ -295,7 +304,7 @@ public class ScopedMemoryAccess {
|
||||
try {
|
||||
forceInternal(session, fd, address, isSync, index, length);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +312,7 @@ public class ScopedMemoryAccess {
|
||||
public void forceInternal(MemorySessionImpl session, FileDescriptor fd, long address, boolean isSync, long index, long length) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
}
|
||||
SharedSecrets.getJavaNioAccess().force(fd, address, isSync, index, length);
|
||||
} finally {
|
||||
@ -333,7 +342,7 @@ public class ScopedMemoryAccess {
|
||||
s,
|
||||
defaultImpl);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +356,7 @@ public class ScopedMemoryAccess {
|
||||
S s,
|
||||
VectorSupport.LoadOperation<AbstractMemorySegmentImpl, V, S> defaultImpl) {
|
||||
try {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
|
||||
return VectorSupport.load(vmClass, e, length,
|
||||
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,
|
||||
@ -378,7 +387,7 @@ public class ScopedMemoryAccess {
|
||||
s, offsetInRange,
|
||||
defaultImpl);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +402,7 @@ public class ScopedMemoryAccess {
|
||||
S s, int offsetInRange,
|
||||
VectorSupport.LoadVectorMaskedOperation<AbstractMemorySegmentImpl, V, S, M> defaultImpl) {
|
||||
try {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
|
||||
return VectorSupport.loadMasked(vmClass, maskClass, e, length,
|
||||
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, m, offsetInRange,
|
||||
@ -424,7 +433,7 @@ public class ScopedMemoryAccess {
|
||||
msp, offset,
|
||||
defaultImpl);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +447,7 @@ public class ScopedMemoryAccess {
|
||||
AbstractMemorySegmentImpl msp, long offset,
|
||||
VectorSupport.StoreVectorOperation<AbstractMemorySegmentImpl, V> defaultImpl) {
|
||||
try {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
|
||||
VectorSupport.store(vmClass, e, length,
|
||||
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,
|
||||
@ -470,7 +479,7 @@ public class ScopedMemoryAccess {
|
||||
msp, offset,
|
||||
defaultImpl);
|
||||
} catch (ScopedAccessError ex) {
|
||||
throw new IllegalStateException("This segment is already closed");
|
||||
throw ex.newRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,7 +493,7 @@ public class ScopedMemoryAccess {
|
||||
AbstractMemorySegmentImpl msp, long offset,
|
||||
VectorSupport.StoreVectorMaskedOperation<AbstractMemorySegmentImpl, V, M> defaultImpl) {
|
||||
try {
|
||||
session.checkValidState();
|
||||
session.checkValidStateRaw();
|
||||
|
||||
VectorSupport.storeMasked(vmClass, maskClass, e, length,
|
||||
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,
|
||||
|
@ -1199,9 +1199,6 @@ public class FileChannelImpl
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAP_MEM_SEG_DEFAULT_MODES = 0;
|
||||
private static final int MAP_MEM_SEG_READ_ONLY = 1;
|
||||
|
||||
@Override
|
||||
public MemorySegment map(MapMode mode, long offset, long size,
|
||||
MemorySession session)
|
||||
@ -1210,7 +1207,7 @@ public class FileChannelImpl
|
||||
Objects.requireNonNull(mode,"Mode is null");
|
||||
Objects.requireNonNull(session, "Session is null");
|
||||
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
|
||||
sessionImpl.checkValidStateSlow();
|
||||
sessionImpl.checkValidState();
|
||||
if (offset < 0)
|
||||
throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
|
||||
if (size < 0)
|
||||
@ -1219,14 +1216,14 @@ public class FileChannelImpl
|
||||
boolean isSync = isSync(mode);
|
||||
int prot = toProt(mode);
|
||||
Unmapper unmapper = mapInternal(mode, offset, size, prot, isSync);
|
||||
int modes = MAP_MEM_SEG_DEFAULT_MODES;
|
||||
boolean readOnly = false;
|
||||
if (mode == MapMode.READ_ONLY) {
|
||||
modes |= MAP_MEM_SEG_READ_ONLY;
|
||||
readOnly = true;
|
||||
}
|
||||
if (unmapper != null) {
|
||||
AbstractMemorySegmentImpl segment =
|
||||
new MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
|
||||
modes, session);
|
||||
readOnly, session);
|
||||
MemorySessionImpl.ResourceList.ResourceCleanup resource =
|
||||
new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
@ -1237,7 +1234,7 @@ public class FileChannelImpl
|
||||
sessionImpl.addOrCleanupIfFail(resource);
|
||||
return segment;
|
||||
} else {
|
||||
return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(modes, session);
|
||||
return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(readOnly, sessionImpl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1484,7 +1484,7 @@ public class HtmlDocletWriter {
|
||||
}
|
||||
};
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
configuration.tagletManager.checkTags(element, trees, true);
|
||||
configuration.tagletManager.checkTags(element, trees);
|
||||
commentRemoved = false;
|
||||
|
||||
for (ListIterator<? extends DocTree> iterator = trees.listIterator(); iterator.hasNext();) {
|
||||
|
@ -28,9 +28,8 @@ package jdk.javadoc.internal.doclets.toolkit.taglets;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.doclet.Taglet.Location;
|
||||
@ -42,9 +41,7 @@ import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
|
||||
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
|
||||
|
||||
/**
|
||||
* An inline taglet representing the {@code {@inheritDoc}} tag.
|
||||
* It is used to copy documentation from superclass (but not superinterface)
|
||||
* declarations and from overridden and implemented methods.
|
||||
* A taglet that represents the {@code {@inheritDoc}} tag.
|
||||
*/
|
||||
public class InheritDocTaglet extends BaseTaglet {
|
||||
|
||||
@ -52,7 +49,7 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
* Construct a new InheritDocTaglet.
|
||||
*/
|
||||
public InheritDocTaglet() {
|
||||
super(DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.TYPE, Location.METHOD));
|
||||
super(DocTree.Kind.INHERIT_DOC, true, EnumSet.of(Location.METHOD));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,14 +93,6 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
inheritedDoc.inlineTags, isFirstSentence);
|
||||
}
|
||||
} else {
|
||||
// This is to assert that we don't reach here for a class declaration.
|
||||
// Indeed, every class except for java.lang.Object has a superclass.
|
||||
// If we ever reach here, we would need a different warning; because
|
||||
// the below warning is about method declarations, not class declarations.
|
||||
// Unless @inheritDoc is used inside java.lang.Object itself,
|
||||
// which would clearly be an error, we shouldn't reach here.
|
||||
assert !(e instanceof TypeElement typeElement)
|
||||
|| typeElement.getSuperclass().getKind() == TypeKind.NONE;
|
||||
String signature = utils.getSimpleName(e) +
|
||||
((utils.isExecutableElement(e))
|
||||
? utils.flatSignature((ExecutableElement) e, writer.getCurrentPageElement())
|
||||
@ -115,6 +104,9 @@ public class InheritDocTaglet extends BaseTaglet {
|
||||
|
||||
@Override
|
||||
public Content getInlineTagOutput(Element e, DocTree inheritDoc, TagletWriter tagletWriter) {
|
||||
if (e.getKind() != ElementKind.METHOD) {
|
||||
return tagletWriter.getOutputInstance();
|
||||
}
|
||||
return retrieveInheritedDocumentation(tagletWriter, e, inheritDoc, tagletWriter.isFirstSentence);
|
||||
}
|
||||
}
|
||||
|
@ -350,16 +350,12 @@ public class TagletManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a series of {@code DocTree}s, check for spelling mistakes.
|
||||
* Given a series of {@code DocTree}s, check for misuse and spelling mistakes.
|
||||
*
|
||||
* @param element the tags holder
|
||||
* @param trees the trees containing the comments
|
||||
* @param inlineTrees true if the trees are inline and false otherwise
|
||||
*/
|
||||
public void checkTags(Element element, Iterable<? extends DocTree> trees, boolean inlineTrees) {
|
||||
if (trees == null) {
|
||||
return;
|
||||
}
|
||||
public void checkTags(Element element, Iterable<? extends DocTree> trees) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
for (DocTree tag : trees) {
|
||||
String name = tag.getKind().tagName;
|
||||
@ -386,73 +382,62 @@ public class TagletManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inlineTrees && !taglet.isInlineTag()) {
|
||||
printTagMisuseWarn(ch, taglet, tag, "inline");
|
||||
}
|
||||
|
||||
// nothing more to do
|
||||
if (element == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inlineTrees) {
|
||||
new SimpleElementVisitor14<Void, Void>() {
|
||||
@Override
|
||||
public Void visitModule(ModuleElement e, Void p) {
|
||||
if (!taglet.inModule()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module");
|
||||
}
|
||||
return null;
|
||||
new SimpleElementVisitor14<Void, Void>() {
|
||||
@Override
|
||||
public Void visitModule(ModuleElement e, Void p) {
|
||||
if (!taglet.inModule()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "module");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackage(PackageElement e, Void p) {
|
||||
if (!taglet.inPackage()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package");
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public Void visitPackage(PackageElement e, Void p) {
|
||||
if (!taglet.inPackage()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "package");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
if (!taglet.inType()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class");
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
if (!taglet.inType()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "class");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExecutable(ExecutableElement e, Void p) {
|
||||
if (utils.isConstructor(e) && !taglet.inConstructor()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor");
|
||||
} else if (!taglet.inMethod()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method");
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public Void visitExecutable(ExecutableElement e, Void p) {
|
||||
if (utils.isConstructor(e) && !taglet.inConstructor()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "constructor");
|
||||
} else if (!taglet.inMethod()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
if (utils.isField(e) && !taglet.inField()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field");
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
if (utils.isField(e) && !taglet.inField()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "field");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUnknown(Element e, Void p) {
|
||||
if (utils.isOverviewElement(e) && !taglet.inOverview()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview");
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public Void visitUnknown(Element e, Void p) {
|
||||
if (utils.isOverviewElement(e) && !taglet.inOverview()) {
|
||||
printTagMisuseWarn(utils.getCommentHelper(e), taglet, tag, "overview");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void defaultAction(Element e, Void p) {
|
||||
return null;
|
||||
}
|
||||
}.visit(element);
|
||||
}
|
||||
@Override
|
||||
protected Void defaultAction(Element e, Void p) {
|
||||
return null;
|
||||
}
|
||||
}.visit(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,22 +474,13 @@ public class TagletManager {
|
||||
if (taglet.inMethod()) {
|
||||
locationsSet.add("method");
|
||||
}
|
||||
if (taglet.isInlineTag()) {
|
||||
locationsSet.add("inline text");
|
||||
}
|
||||
if (locationsSet.isEmpty()) {
|
||||
//This known tag is excluded.
|
||||
return;
|
||||
}
|
||||
StringBuilder combined_locations = new StringBuilder();
|
||||
for (String location: locationsSet) {
|
||||
if (combined_locations.length() > 0) {
|
||||
combined_locations.append(", ");
|
||||
}
|
||||
combined_locations.append(location);
|
||||
}
|
||||
var combined_locations = String.join(", ", locationsSet);
|
||||
messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse",
|
||||
"@" + taglet.getName(), holderType, combined_locations.toString());
|
||||
"@" + taglet.getName(), holderType, combined_locations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,8 +275,8 @@ public abstract class TagletWriter {
|
||||
|
||||
Content output = getOutputInstance();
|
||||
Utils utils = configuration().utils;
|
||||
tagletManager.checkTags(element, utils.getBlockTags(element), false);
|
||||
tagletManager.checkTags(element, utils.getFullBody(element), true);
|
||||
tagletManager.checkTags(element, utils.getBlockTags(element));
|
||||
tagletManager.checkTags(element, utils.getFullBody(element));
|
||||
for (Taglet taglet : taglets) {
|
||||
if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) {
|
||||
// The type parameters and state components are documented in a special
|
||||
|
@ -30,7 +30,6 @@ import java.util.*;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
import com.sun.source.doctree.DocTree;
|
||||
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
|
||||
@ -258,17 +257,6 @@ public class DocFinder {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
} else if (utils.isTypeElement(input.element)) {
|
||||
TypeMirror t = ((TypeElement) input.element).getSuperclass();
|
||||
Element superclass = utils.asTypeElement(t);
|
||||
if (superclass != null) {
|
||||
inheritedSearchInput.element = superclass;
|
||||
output = search(configuration, inheritedSearchInput);
|
||||
output.isValidInheritDocTag = true;
|
||||
if (!output.inlineTags.isEmpty()) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ public class Checker extends DocTreePathScanner<Void, Void> {
|
||||
reportMissing("dc.missing.comment");
|
||||
}
|
||||
return null;
|
||||
} else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod) {
|
||||
} else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod && !pseudoElement(p)) {
|
||||
if (tree.getBlockTags().isEmpty()) {
|
||||
reportMissing("dc.empty.comment");
|
||||
return null;
|
||||
@ -224,7 +224,7 @@ public class Checker extends DocTreePathScanner<Void, Void> {
|
||||
|
||||
// this is for html files
|
||||
// ... if it is a legacy package.html, the doc comment comes after the <h1> page title
|
||||
// ... otherwise, (e.g. overview file and doc-files/*.html files) no additional headings are inserted
|
||||
// ... otherwise, (e.g. overview file and doc-files/**/*.html files) no additional headings are inserted
|
||||
case COMPILATION_UNIT -> fo.isNameCompatible("package", JavaFileObject.Kind.HTML) ? 1 : 0;
|
||||
|
||||
|
||||
@ -292,6 +292,13 @@ public class Checker extends DocTreePathScanner<Void, Void> {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Checks if the passed tree path corresponds to an entity, such as
|
||||
// the overview file and doc-files/**/*.html files.
|
||||
private boolean pseudoElement(TreePath p) {
|
||||
return p.getLeaf().getKind() == Tree.Kind.COMPILATION_UNIT
|
||||
&& p.getCompilationUnit().getSourceFile().getKind() == JavaFileObject.Kind.HTML;
|
||||
}
|
||||
|
||||
private void reportMissing(String code, Object... args) {
|
||||
env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@
|
||||
|
||||
public class framepop02 {
|
||||
|
||||
final static int MAX_THREADS_LIMIT = 32;
|
||||
final static int MAX_THREADS_LIMIT = 20;
|
||||
final static int NESTING_DEPTH = 20;
|
||||
final static String TEST_THREAD_NAME_BASE = "Test Thread #";
|
||||
|
||||
|
@ -69,11 +69,15 @@ void print_current_time() {
|
||||
}
|
||||
|
||||
static
|
||||
int isTestThread(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr) {
|
||||
bool isTestThread(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr) {
|
||||
jvmtiThreadInfo inf;
|
||||
const char* TEST_THREAD_NAME_BASE = "Test Thread";
|
||||
check_jvmti_status(jni, jvmti->GetThreadInfo(thr, &inf), "Error in GetThreadInfo.");
|
||||
return strncmp(inf.name, TEST_THREAD_NAME_BASE, strlen(TEST_THREAD_NAME_BASE)) == 0;
|
||||
|
||||
bool result = strncmp(inf.name, TEST_THREAD_NAME_BASE, strlen(TEST_THREAD_NAME_BASE)) == 0;
|
||||
jvmti->Deallocate((unsigned char *)inf.name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
@ -174,6 +178,10 @@ void JNICALL MethodEntry(jvmtiEnv *jvmti, JNIEnv *jni,
|
||||
|
||||
if (watch_events == JNI_FALSE) return;
|
||||
|
||||
if (!isTestThread(jni, jvmti, thr)) {
|
||||
return; // not a tested thread
|
||||
}
|
||||
|
||||
RawMonitorLocker rml(jvmti, jni, agent_lock);
|
||||
|
||||
if (!callbacksEnabled) {
|
||||
@ -183,7 +191,7 @@ void JNICALL MethodEntry(jvmtiEnv *jvmti, JNIEnv *jni,
|
||||
check_jvmti_status(jni, jvmti->GetFrameCount(thr, &frameCount), "Error in GetFrameCount");
|
||||
check_jvmti_status(jni, jvmti->IsMethodNative(method, &isNative), "Error in IsMethodNative.");
|
||||
|
||||
if (isTestThread(jni, jvmti, thr)) {
|
||||
{
|
||||
if (printdump == JNI_TRUE) {
|
||||
print_current_time();
|
||||
fflush(0);
|
||||
@ -220,7 +228,7 @@ void JNICALL FramePop(jvmtiEnv *jvmti, JNIEnv *jni,
|
||||
}
|
||||
check_jvmti_status(jni, jvmti->GetFrameCount(thr, &frameCount), "Error in GetFrameCount.");
|
||||
|
||||
if (isTestThread(jni, jvmti, thr)) {
|
||||
{
|
||||
if (printdump == JNI_TRUE) {
|
||||
print_current_time();
|
||||
fflush(0);
|
||||
|
@ -373,7 +373,7 @@ public class TestByteBuffer {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof IllegalStateException) {
|
||||
//all get/set buffer operation should fail because of the session check
|
||||
assertTrue(ex.getCause().getMessage().contains("already closed"));
|
||||
assertTrue(ex.getCause().getMessage().contains("Already closed"));
|
||||
} else {
|
||||
//all other exceptions were unexpected - fail
|
||||
fail("Unexpected exception", cause);
|
||||
@ -410,7 +410,7 @@ public class TestByteBuffer {
|
||||
handle.invoke(e.getValue());
|
||||
fail();
|
||||
} catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("already closed"));
|
||||
assertTrue(ex.getMessage().contains("Already closed"));
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
//skip
|
||||
} catch (Throwable ex) {
|
||||
|
@ -370,8 +370,8 @@ public class TestMemorySession {
|
||||
}
|
||||
|
||||
private void keepAlive(MemorySession child, MemorySession parent) {
|
||||
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(parent);
|
||||
sessionImpl.acquire0();
|
||||
child.addCloseAction(sessionImpl::release0);
|
||||
MemorySessionImpl parentImpl = MemorySessionImpl.toSessionImpl(parent);
|
||||
parentImpl.acquire0();
|
||||
child.addCloseAction(parentImpl::release0);
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,8 @@ import java.lang.invoke.MethodHandleProxies;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@ -60,9 +62,14 @@ import java.util.stream.DoubleStream;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_CHAR;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_FLOAT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_LONG;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@ -132,15 +139,15 @@ public class VaListTest extends NativeTestHelper {
|
||||
= (builder) -> VaList.make(builder, MemorySession.openConfined());
|
||||
|
||||
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> winVaListScopedFactory
|
||||
= (builder, session) -> Windowsx64Linker.newVaList(builder, session);
|
||||
= Windowsx64Linker::newVaList;
|
||||
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> sysvVaListScopedFactory
|
||||
= (builder, session) -> SysVx64Linker.newVaList(builder, session);
|
||||
= SysVx64Linker::newVaList;
|
||||
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> linuxAArch64VaListScopedFactory
|
||||
= (builder, session) -> LinuxAArch64Linker.newVaList(builder, session);
|
||||
= LinuxAArch64Linker::newVaList;
|
||||
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> macAArch64VaListScopedFactory
|
||||
= (builder, session) -> MacOsAArch64Linker.newVaList(builder, session);
|
||||
= MacOsAArch64Linker::newVaList;
|
||||
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> platformVaListScopedFactory
|
||||
= (builder, session) -> VaList.make(builder, session);
|
||||
= VaList::make;
|
||||
|
||||
@DataProvider
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -815,4 +822,87 @@ public class VaListTest extends NativeTestHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public static Object[][] overflow() {
|
||||
List<Function<Consumer<VaList.Builder>, VaList>> factories = List.of(
|
||||
winVaListFactory,
|
||||
sysvVaListFactory,
|
||||
linuxAArch64VaListFactory,
|
||||
macAArch64VaListFactory
|
||||
);
|
||||
List<List<MemoryLayout>> contentsCases = List.of(
|
||||
List.of(JAVA_INT),
|
||||
List.of(JAVA_LONG),
|
||||
List.of(JAVA_DOUBLE),
|
||||
List.of(ADDRESS),
|
||||
List.of(JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG,
|
||||
JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG, JAVA_LONG,
|
||||
JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE,
|
||||
JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE, JAVA_DOUBLE,
|
||||
JAVA_INT, JAVA_LONG, JAVA_DOUBLE, ADDRESS)
|
||||
);
|
||||
List<MemoryLayout> overflowCases = List.of(
|
||||
JAVA_INT,
|
||||
JAVA_LONG,
|
||||
JAVA_DOUBLE,
|
||||
ADDRESS
|
||||
);
|
||||
return factories.stream()
|
||||
.<Object[]>mapMulti((factory, sink) -> {
|
||||
for (List<MemoryLayout> content : contentsCases) {
|
||||
for (MemoryLayout overflow : overflowCases) {
|
||||
sink.accept(new Object[]{ factory, content, overflow });
|
||||
}
|
||||
}
|
||||
})
|
||||
.toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
private static void buildVaList(VaList.Builder builder, List<MemoryLayout> contents) {
|
||||
for (MemoryLayout layout : contents) {
|
||||
if (layout instanceof ValueLayout.OfInt ofInt) {
|
||||
builder.addVarg(ofInt, 1);
|
||||
} else if (layout instanceof ValueLayout.OfLong ofLong) {
|
||||
builder.addVarg(ofLong, 1L);
|
||||
} else if (layout instanceof ValueLayout.OfDouble ofDouble) {
|
||||
builder.addVarg(ofDouble, 1D);
|
||||
} else if (layout instanceof ValueLayout.OfAddress ofAddress) {
|
||||
builder.addVarg(ofAddress, MemoryAddress.ofLong(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "overflow")
|
||||
public void testSkipOverflow(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
||||
List<MemoryLayout> contents,
|
||||
MemoryLayout skipped) {
|
||||
VaList vaList = vaListFactory.apply(b -> buildVaList(b, contents));
|
||||
vaList.skip(contents.toArray(MemoryLayout[]::new));
|
||||
assertThrows(NoSuchElementException.class, () -> vaList.skip(skipped));
|
||||
}
|
||||
|
||||
private static void nextVarg(VaList vaList, MemoryLayout layout) {
|
||||
if (layout instanceof ValueLayout.OfInt ofInt) {
|
||||
assertEquals(vaList.nextVarg(ofInt), 1);
|
||||
} else if (layout instanceof ValueLayout.OfLong ofLong) {
|
||||
assertEquals(vaList.nextVarg(ofLong), 1L);
|
||||
} else if (layout instanceof ValueLayout.OfDouble ofDouble) {
|
||||
assertEquals(vaList.nextVarg(ofDouble), 1D);
|
||||
} else if (layout instanceof ValueLayout.OfAddress ofAddress) {
|
||||
assertEquals(vaList.nextVarg(ofAddress), MemoryAddress.ofLong(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "overflow")
|
||||
public void testVargOverflow(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
||||
List<MemoryLayout> contents,
|
||||
MemoryLayout next) {
|
||||
VaList vaList = vaListFactory.apply(b -> buildVaList(b, contents));
|
||||
for (MemoryLayout layout : contents) {
|
||||
nextVarg(vaList, layout);
|
||||
}
|
||||
assertThrows(NoSuchElementException.class, () -> nextVarg(vaList, next));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2022, 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,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8008768
|
||||
* @bug 8008768 8287379
|
||||
* @summary Using {@inheritDoc} in simple tag defined via -tag fails
|
||||
* @library ../../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
@ -33,13 +33,6 @@
|
||||
|
||||
import javadoc.tester.JavadocTester;
|
||||
|
||||
/**
|
||||
* DocTest documentation.
|
||||
*
|
||||
* @apiNote DocTest API note.
|
||||
* @implSpec DocTest implementation spec.
|
||||
* @implNote DocTest implementation note.
|
||||
*/
|
||||
public class DocTest extends JavadocTester {
|
||||
public static void main(String... args) throws Exception {
|
||||
DocTest tester = new DocTest();
|
||||
@ -51,9 +44,9 @@ public class DocTest extends JavadocTester {
|
||||
javadoc("-verbose",
|
||||
"-d", "DocTest",
|
||||
"-sourcepath", System.getProperty("test.src.path"),
|
||||
"-tag", "apiNote:optcm:<em>API Note</em>",
|
||||
"-tag", "implSpec:optcm:<em>Implementation Requirements</em>:",
|
||||
"-tag", "implNote:optcm:<em>Implementation Note</em>:",
|
||||
"-tag", "apiNote:a:API Note",
|
||||
"-tag", "implSpec:a:Implementation Requirements:",
|
||||
"-tag", "implNote:a:Implementation Note:",
|
||||
"-package",
|
||||
testSrc("DocTest.java")
|
||||
);
|
||||
@ -64,16 +57,6 @@ public class DocTest extends JavadocTester {
|
||||
checkOutput(Output.STDERR, false, "at com.sun");
|
||||
}
|
||||
|
||||
/**
|
||||
* DocTest() documentation.
|
||||
*
|
||||
* @apiNote DocTest() API note.
|
||||
* @implSpec DocTest() implementation spec.
|
||||
* @implNote DocTest() implementation note.
|
||||
*/
|
||||
public DocTest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* DocTest.testMethod() documentation.
|
||||
*
|
||||
@ -85,43 +68,8 @@ public class DocTest extends JavadocTester {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DocTestWithTags documentation.
|
||||
*
|
||||
* @apiNote DocTestWithTags API note.
|
||||
* <pre>
|
||||
* DocTestWithTags API note code sample.
|
||||
* </pre>
|
||||
* @implSpec DocTestWithTags implementation spec.
|
||||
* <pre>
|
||||
* DocTestWithTags implementation spec code sample.
|
||||
* </pre>
|
||||
* @implNote DocTestWithTags implementation note.
|
||||
* <pre>
|
||||
* DocTestWithTags implementation note code sample.
|
||||
* </pre>
|
||||
*/
|
||||
class DocTestWithTags {
|
||||
|
||||
/**
|
||||
* DocTestWithTags() documentation.
|
||||
*
|
||||
* @apiNote DocTestWithTags() API note.
|
||||
* <pre>
|
||||
* DocTestWithTags() API note code sample.
|
||||
* </pre>
|
||||
* @implSpec DocTestWithTags() implementation spec.
|
||||
* <pre>
|
||||
* DocTestWithTags() implementation spec code sample.
|
||||
* </pre>
|
||||
* @implNote DocTest() implementation note.
|
||||
* <pre>
|
||||
* DocTest() implementation note code sample.
|
||||
* </pre>
|
||||
*/
|
||||
public DocTestWithTags() {
|
||||
}
|
||||
|
||||
/**
|
||||
* DocTest.testMethod() documentation.
|
||||
*
|
||||
@ -131,11 +79,11 @@ class DocTestWithTags {
|
||||
* </pre>
|
||||
* @implSpec DocTestWithTags.testMethod() implementation spec.
|
||||
* <pre>
|
||||
* DocTestWithTags.testMethod() API implementation spec code sample.
|
||||
* DocTestWithTags.testMethod() implementation spec code sample.
|
||||
* </pre>
|
||||
* @implNote DocTest.testMethod() implementation note.
|
||||
* <pre>
|
||||
* DocTest.testMethod() API implementation code sample.
|
||||
* DocTest.testMethod() implementation note code sample.
|
||||
* </pre>
|
||||
*/
|
||||
public void testMethod() {
|
||||
@ -145,52 +93,26 @@ class DocTestWithTags {
|
||||
class MinimallyExtendsDocTest extends DocTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleExtendsDocTest documentation.
|
||||
*/
|
||||
class SimpleExtendsDocTest extends DocTest {
|
||||
|
||||
/**
|
||||
* SimpleExtendsDocTest() documentation.
|
||||
* SimpleExtendsDocTest.testMethod() documentation.
|
||||
*/
|
||||
public SimpleExtendsDocTest() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* SimpleExtendsDocTest.testMethod() documenation.
|
||||
*/
|
||||
@java.lang.Override
|
||||
@Override
|
||||
public void testMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
class SimpleInheritDocDocTest extends DocTest {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public SimpleInheritDocDocTest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@java.lang.Override
|
||||
@Override
|
||||
public void testMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @apiNote {@inheritDoc}
|
||||
* @implSpec {@inheritDoc}
|
||||
* @implNote {@inheritDoc}
|
||||
*/
|
||||
class FullInheritDocDocTest extends DocTest {
|
||||
|
||||
/**
|
||||
@ -200,50 +122,21 @@ class FullInheritDocDocTest extends DocTest {
|
||||
* @implSpec {@inheritDoc}
|
||||
* @implNote {@inheritDoc}
|
||||
*/
|
||||
public FullInheritDocDocTest() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @apiNote {@inheritDoc}
|
||||
* @implSpec {@inheritDoc}
|
||||
* @implNote {@inheritDoc}
|
||||
*/
|
||||
@java.lang.Override
|
||||
@Override
|
||||
public void testMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} and FullInheritDocPlusDocTest documentation.
|
||||
*
|
||||
* @implSpec {@inheritDoc} and FullInheritDocPlusDocTest API note.
|
||||
* @implNote {@inheritDoc} and FullInheritDocPlusDocTest implementation specification.
|
||||
* @apiNote {@inheritDoc} and FullInheritDocPlusDocTest implementation note.
|
||||
*/
|
||||
class FullInheritDocPlusDocTest extends DocTest {
|
||||
|
||||
/**
|
||||
* {@inheritDoc} and FullInheritDocPlusDocTest() documentation.
|
||||
*
|
||||
* @implSpec {@inheritDoc} and FullInheritDocPlusDocTest() API note.
|
||||
* @implNote {@inheritDoc} and FullInheritDocPlusDocTest() implementation specification.
|
||||
* @apiNote {@inheritDoc} and FullInheritDocPlusDocTest() implementation note.
|
||||
*/
|
||||
public FullInheritDocPlusDocTest() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() documentation.
|
||||
*
|
||||
* @implSpec {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() API note.
|
||||
* @implNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation specification.
|
||||
* @apiNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation note.
|
||||
* @implSpec {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation specification.
|
||||
* @implNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation note.
|
||||
* @apiNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() API note.
|
||||
*/
|
||||
@java.lang.Override
|
||||
@Override
|
||||
public void testMethod() {
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8284299
|
||||
* @bug 8284299 8287379
|
||||
* @library /tools/lib ../../lib
|
||||
* @modules jdk.javadoc/jdk.javadoc.internal.tool
|
||||
* @build toolbox.ToolBox javadoc.tester.*
|
||||
@ -97,4 +97,171 @@ public class TestInheritDocWithinInappropriateTag extends JavadocTester {
|
||||
^
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassOrInterfaceMainDescription(Path base) throws Exception {
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
/** Class A. */
|
||||
public class A { }
|
||||
""",
|
||||
"""
|
||||
/** {@inheritDoc} */
|
||||
public class B extends A { }
|
||||
""",
|
||||
"""
|
||||
/** Interface C. */
|
||||
public interface C { }
|
||||
""",
|
||||
"""
|
||||
/** {@inheritDoc} */
|
||||
public interface D extends C { }
|
||||
""",
|
||||
"""
|
||||
/** Interface E. */
|
||||
public interface E { }
|
||||
""",
|
||||
"""
|
||||
/** {@inheritDoc} */
|
||||
public class F implements E { }
|
||||
""");
|
||||
javadoc("-Xdoclint:none",
|
||||
"-d", base.resolve("out").toString(),
|
||||
src.resolve("A.java").toString(),
|
||||
src.resolve("B.java").toString(),
|
||||
src.resolve("C.java").toString(),
|
||||
src.resolve("D.java").toString(),
|
||||
src.resolve("E.java").toString(),
|
||||
src.resolve("F.java").toString());
|
||||
checkExit(Exit.OK);
|
||||
new OutputChecker(Output.OUT).setExpectOrdered(false).check(
|
||||
"""
|
||||
B.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** {@inheritDoc} */
|
||||
^
|
||||
""",
|
||||
"""
|
||||
D.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** {@inheritDoc} */
|
||||
^
|
||||
""",
|
||||
"""
|
||||
F.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** {@inheritDoc} */
|
||||
^
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassOrInterfaceTypeParameter(Path base) throws Exception {
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src,
|
||||
"""
|
||||
/** @param <T> A's parameter */
|
||||
public class A<T> { }
|
||||
""",
|
||||
"""
|
||||
/** @param <T> {@inheritDoc} */
|
||||
public class B extends A { }
|
||||
""",
|
||||
"""
|
||||
/** @param <T> C's parameter */
|
||||
public interface C<T> { }
|
||||
""",
|
||||
"""
|
||||
/** @param <T> {@inheritDoc} */
|
||||
public interface D<T> extends C<T> { }
|
||||
""",
|
||||
"""
|
||||
/** @param <T> E's parameter */
|
||||
public interface E<T> { }
|
||||
""",
|
||||
"""
|
||||
/** @param <T> {@inheritDoc} */
|
||||
public class F<T> implements E<T> { }
|
||||
""");
|
||||
javadoc("-Xdoclint:none",
|
||||
"-d", base.resolve("out").toString(),
|
||||
src.resolve("A.java").toString(),
|
||||
src.resolve("B.java").toString(),
|
||||
src.resolve("C.java").toString(),
|
||||
src.resolve("D.java").toString(),
|
||||
src.resolve("E.java").toString(),
|
||||
src.resolve("F.java").toString());
|
||||
checkExit(Exit.OK);
|
||||
new OutputChecker(Output.OUT).setExpectOrdered(false).check(
|
||||
"""
|
||||
B.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** @param <T> {@inheritDoc} */
|
||||
^
|
||||
""",
|
||||
"""
|
||||
D.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** @param <T> {@inheritDoc} */
|
||||
^
|
||||
""",
|
||||
"""
|
||||
F.java:1: warning: Tag @inheritDoc cannot be used in class documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
/** @param <T> {@inheritDoc} */
|
||||
^
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverview(Path base) throws Exception {
|
||||
Path src = base.resolve("src");
|
||||
tb.writeJavaFiles(src, """
|
||||
package p;
|
||||
|
||||
/** A class */
|
||||
public class C { }
|
||||
""");
|
||||
tb.writeFile(src.resolve("overview.html"), """
|
||||
<HTML lang="EN">
|
||||
<HEAD>
|
||||
<TITLE>overview</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
{@inheritDoc}
|
||||
</BODY>
|
||||
</HTML>
|
||||
""");
|
||||
tb.writeFile(
|
||||
src.resolve("p").resolve("doc-files").resolve("example.html"), """
|
||||
<HTML lang="EN">
|
||||
<HEAD>
|
||||
<TITLE>example</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
{@inheritDoc}
|
||||
</BODY>
|
||||
</HTML>
|
||||
""");
|
||||
javadoc("-Xdoclint:none",
|
||||
"-overview", src.resolve("overview.html").toString(),
|
||||
"-d", base.resolve("out").toString(),
|
||||
"-sourcepath", src.toString(),
|
||||
"p");
|
||||
checkExit(Exit.OK);
|
||||
new OutputChecker(Output.OUT).setExpectOrdered(false).check(
|
||||
"""
|
||||
overview.html:6: warning: Tag @inheritDoc cannot be used in overview documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
{@inheritDoc}
|
||||
^
|
||||
""",
|
||||
"""
|
||||
example.html:6: warning: Tag @inheritDoc cannot be used in overview documentation.\
|
||||
It can only be used in the following types of documentation: method.
|
||||
{@inheritDoc}
|
||||
^
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2022, 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
|
||||
@ -86,12 +86,8 @@ public class TestRelativeLinks extends JavadocTester {
|
||||
"""
|
||||
<a href="C.html#class-fragment">fragment class link</a>""");
|
||||
|
||||
// subclass in same pacakge
|
||||
// subclass in same package
|
||||
checkOutput("pkg/D.html", true,
|
||||
"""
|
||||
<a href="relative-class-link.html">relative class link</a>""",
|
||||
"""
|
||||
<a href="C.html#class-fragment">fragment class link</a>""",
|
||||
"""
|
||||
<a href="relative-method-link.html">relative method link</a>""",
|
||||
"""
|
||||
@ -102,10 +98,6 @@ public class TestRelativeLinks extends JavadocTester {
|
||||
|
||||
// subclass in subpackage
|
||||
checkOutput("pkg/sub/F.html", true,
|
||||
"""
|
||||
<a href="../../pkg/relative-class-link.html">relative class link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/C.html#class-fragment">fragment class link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/relative-method-link.html">relative method link</a>""",
|
||||
"""
|
||||
@ -149,10 +141,6 @@ public class TestRelativeLinks extends JavadocTester {
|
||||
|
||||
// CLASS_USE
|
||||
checkOutput("pkg/class-use/C.html", true,
|
||||
"""
|
||||
<a href="../../pkg/relative-class-link.html">relative class link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/C.html#class-fragment">fragment class link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/relative-field-link.html">relative field link</a>""",
|
||||
"""
|
||||
@ -175,19 +163,7 @@ public class TestRelativeLinks extends JavadocTester {
|
||||
"""
|
||||
<a href="../../pkg/relative-package-link.html">relative package link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/package-summary.html#package-fragment">package fragment link</a>""",
|
||||
// subclass inheriting relative link doc
|
||||
"""
|
||||
<a href="../../pkg/relative-class-link.html">relative class link</a>""",
|
||||
"""
|
||||
<a href="../../pkg/C.html#class-fragment">fragment class link</a>""");
|
||||
|
||||
// sibling package summary
|
||||
checkOutput("pkg2/package-summary.html", true,
|
||||
"""
|
||||
<a href="../pkg/relative-class-link.html">relative class link</a>""",
|
||||
"""
|
||||
<a href="../pkg/C.html#class-fragment">fragment class link</a>""");
|
||||
<a href="../../pkg/package-summary.html#package-fragment">package fragment link</a>""");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,8 +24,6 @@
|
||||
package pkg;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* A class that extends C and inherits some of its comments.
|
||||
*/
|
||||
public class D extends C {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,8 +26,6 @@ package pkg.sub;
|
||||
import pkg.C;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* A class that extends C and inherits some of its comments.
|
||||
*/
|
||||
public class F extends C {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,9 +26,7 @@ package pkg2;
|
||||
import pkg.C;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* A class that extends pkg.C from onother package and inherits some of its comments.
|
||||
* A class that extends pkg.C from another package and inherits some of its comments.
|
||||
*/
|
||||
public class E extends C {
|
||||
|
||||
|
@ -44,14 +44,11 @@ public class TestSimpleTagInherit extends JavadocTester {
|
||||
public void test() {
|
||||
javadoc("-d", "out",
|
||||
"-sourcepath", testSrc,
|
||||
"-tag", "custom:optcm:<em>Custom:</em>",
|
||||
"-tag", "custom:m:<em>Custom:</em>",
|
||||
"p");
|
||||
checkExit(Exit.OK);
|
||||
|
||||
checkOutput("p/TestClass.html", true,
|
||||
"""
|
||||
<dt><em>Custom:</em></dt>
|
||||
<dd>doc for BaseClass class</dd>""",
|
||||
"""
|
||||
<dt><em>Custom:</em></dt>
|
||||
<dd>doc for BaseClass method</dd>""");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2022, 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,9 +23,6 @@
|
||||
|
||||
package p;
|
||||
|
||||
/**
|
||||
* @custom doc for BaseClass class
|
||||
*/
|
||||
public class BaseClass {
|
||||
/**
|
||||
* @custom doc for BaseClass method
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2022, 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,9 +23,6 @@
|
||||
|
||||
package p;
|
||||
|
||||
/**
|
||||
* @custom {@inheritDoc}
|
||||
*/
|
||||
public class TestClass extends BaseClass {
|
||||
/**
|
||||
* @custom {@inheritDoc}
|
||||
|
@ -7,7 +7,7 @@
|
||||
@factory: block ........ ...... ....... .... ........... method ..... ...... ........
|
||||
@hidden: block ........ ...... ....... type ........... method field ...... ........
|
||||
{@index}: ..... overview module package type constructor method field inline ........
|
||||
{@inheritDoc}: ..... ........ ...... ....... type ........... method ..... inline ........
|
||||
{@inheritDoc}: ..... ........ ...... ....... .... ........... method ..... inline ........
|
||||
{@link}: ..... overview module package type constructor method field inline ........
|
||||
{@linkplain}: ..... overview module package type constructor method field inline ........
|
||||
{@literal}: ..... overview module package type constructor method field inline ........
|
||||
|
Loading…
x
Reference in New Issue
Block a user