This commit is contained in:
Jesper Wilhelmsson 2022-07-12 16:16:16 +00:00
commit d9ca438d06
41 changed files with 979 additions and 824 deletions

@ -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 ........