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

View file

@ -25,6 +25,7 @@
*/ */
package java.lang.foreign; package java.lang.foreign;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -50,7 +51,16 @@ import jdk.internal.reflect.Reflection;
* <p> * <p>
* As such, this interface only supports reading {@code int}, {@code double}, * As such, this interface only supports reading {@code int}, {@code double},
* and any other type that fits into a {@code long}. * 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 * 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). * (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}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); int nextVarg(ValueLayout.OfInt layout);
@ -87,6 +98,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* {@linkplain MemorySession#isAlive() alive}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); long nextVarg(ValueLayout.OfLong layout);
@ -100,6 +112,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* {@linkplain MemorySession#isAlive() alive}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); double nextVarg(ValueLayout.OfDouble layout);
@ -113,6 +126,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* {@linkplain MemorySession#isAlive() alive}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); MemoryAddress nextVarg(ValueLayout.OfAddress layout);
@ -135,6 +149,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* {@linkplain MemorySession#isAlive() alive}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
@ -146,6 +161,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
* {@linkplain MemorySession#isAlive() alive}. * {@linkplain MemorySession#isAlive() alive}.
* @throws WrongThreadException if this method is called from a thread other than the thread owning * @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. * 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); 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 * 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. * 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 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. * @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}. * @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, * Note that when there are no elements added to the created va list,
* this method will return the same as {@link #empty()}. * 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 * @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
* of the underlying variable argument list. * of the underlying variable argument list.
* @param session the memory session to be associated with the new variable arity list. * @param session the memory session to be associated with the new variable arity list.

View file

@ -37,7 +37,6 @@ import jdk.internal.vm.annotation.ForceInline;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.lang.foreign.MemorySegment; import java.lang.foreign.MemorySegment;
import java.lang.foreign.MemorySession;
import java.util.Objects; import java.util.Objects;
import java.util.Spliterator; import java.util.Spliterator;
@ -771,11 +770,7 @@ public abstract sealed class Buffer
final void checkSession() { final void checkSession() {
MemorySessionImpl session = session(); MemorySessionImpl session = session();
if (session != null) { if (session != null) {
try {
session.checkValidState(); session.checkValidState();
} catch (ScopedMemoryAccess.ScopedAccessError e) {
throw new IllegalStateException("This segment is already closed");
}
} }
} }

View file

@ -313,11 +313,7 @@ class Direct$Type$Buffer$RW$$BO$
if (session.ownerThread() == null && session.isCloseable()) { if (session.ownerThread() == null && session.isCloseable()) {
throw new UnsupportedOperationException("ByteBuffer derived from closeable shared sessions not supported"); throw new UnsupportedOperationException("ByteBuffer derived from closeable shared sessions not supported");
} }
try {
session.checkValidState(); session.checkValidState();
} catch (ScopedAccessError e) {
throw new IllegalStateException("This segment is already closed");
}
} }
return address; return address;
} }

View file

@ -70,40 +70,33 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
static final int READ_ONLY = 1;
static final long NONCE = new Random().nextLong(); static final long NONCE = new Random().nextLong();
static final int DEFAULT_MODES = 0;
static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); static final JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
final long length; final long length;
final int mask; final boolean readOnly;
final MemorySession session; final MemorySession session;
@ForceInline @ForceInline
AbstractMemorySegmentImpl(long length, int mask, MemorySession session) { AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySession session) {
this.length = length; this.length = length;
this.mask = mask; this.readOnly = readOnly;
this.session = session; this.session = session;
} }
abstract long min(); abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session);
abstract Object base();
abstract AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session);
abstract ByteBuffer makeByteBuffer(); abstract ByteBuffer makeByteBuffer();
@Override @Override
public AbstractMemorySegmentImpl asReadOnly() { public AbstractMemorySegmentImpl asReadOnly() {
return dup(0, length, mask | READ_ONLY, session); return dup(0, length, true, session);
} }
@Override @Override
public boolean isReadOnly() { public boolean isReadOnly() {
return isSet(READ_ONLY); return readOnly;
} }
@Override @Override
@ -119,7 +112,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
} }
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) { private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
return dup(offset, newSize, mask, session); return dup(offset, newSize, readOnly, session);
} }
@Override @Override
@ -147,7 +140,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
@Override @Override
public final MemorySegment fill(byte value){ public final MemorySegment fill(byte value){
checkAccess(0, length, false); checkAccess(0, length, false);
SCOPED_MEMORY_ACCESS.setMemory(sessionImpl(), base(), min(), length, value); SCOPED_MEMORY_ACCESS.setMemory(sessionImpl(), unsafeGetBase(), unsafeGetOffset(), length, value);
return this; return this;
} }
@ -176,8 +169,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
return 0; return 0;
} }
i = vectorizedMismatchLargeForBytes(sessionImpl(), that.sessionImpl(), i = vectorizedMismatchLargeForBytes(sessionImpl(), that.sessionImpl(),
this.base(), this.min(), this.unsafeGetBase(), this.unsafeGetOffset(),
that.base(), that.min(), that.unsafeGetBase(), that.unsafeGetOffset(),
length); length);
if (i >= 0) { if (i >= 0) {
return i; return i;
@ -235,7 +228,7 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
public final ByteBuffer asByteBuffer() { public final ByteBuffer asByteBuffer() {
checkArraySize("ByteBuffer", 1); checkArraySize("ByteBuffer", 1);
ByteBuffer _bb = makeByteBuffer(); ByteBuffer _bb = makeByteBuffer();
if (isSet(READ_ONLY)) { if (readOnly) {
//session is IMMUTABLE - obtain a RO byte buffer //session is IMMUTABLE - obtain a RO byte buffer
_bb = _bb.asReadOnlyBuffer(); _bb = _bb.asReadOnlyBuffer();
} }
@ -260,9 +253,9 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
@Override @Override
public final Optional<MemorySegment> asOverlappingSlice(MemorySegment other) { public final Optional<MemorySegment> asOverlappingSlice(MemorySegment other) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other); AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)Objects.requireNonNull(other);
if (base() == that.base()) { // both either native or heap if (unsafeGetBase() == that.unsafeGetBase()) { // both either native or heap
final long thisStart = this.min(); final long thisStart = this.unsafeGetOffset();
final long thatStart = that.min(); final long thatStart = that.unsafeGetOffset();
final long thisEnd = thisStart + this.byteSize(); final long thisEnd = thisStart + this.byteSize();
final long thatEnd = thatStart + that.byteSize(); final long thatEnd = thatStart + that.byteSize();
@ -278,8 +271,8 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
@Override @Override
public final long segmentOffset(MemorySegment other) { public final long segmentOffset(MemorySegment other) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other); AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl) Objects.requireNonNull(other);
if (base() == that.base()) { if (unsafeGetBase() == that.unsafeGetBase()) {
return that.min() - this.min(); return that.unsafeGetOffset() - this.unsafeGetOffset();
} }
throw new UnsupportedOperationException("Cannot compute offset from native to heap (or vice versa)."); 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; return arr;
} }
@ForceInline
public void checkAccess(long offset, long length, boolean readOnly) { 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"); throw new UnsupportedOperationException("Attempt to write a read-only segment");
} }
checkBounds(offset, length); checkBounds(offset, length);
} }
public void checkValidState() { public void checkValidState() {
sessionImpl().checkValidStateSlow(); sessionImpl().checkValidState();
} }
public long unsafeGetOffset() { public abstract long unsafeGetOffset();
return min();
}
public Object unsafeGetBase() { public abstract Object unsafeGetBase();
return base();
}
// Helper methods // Helper methods
private boolean isSet(int mask) {
return (this.mask & mask) != 0;
}
public abstract long maxAlignMask(); public abstract long maxAlignMask();
@ForceInline @ForceInline
@ -407,25 +393,19 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
return outOfBoundException(offset, length); return outOfBoundException(offset, length);
} }
@Override
@ForceInline
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session);
}
@Override @Override
public MemorySession session() { public MemorySession session() {
return session; return session;
} }
private IndexOutOfBoundsException outOfBoundException(long offset, long length) { 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)); this, offset, length));
} }
protected int id() { protected int id() {
//compute a stable and random id for this memory segment //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> { static class SegmentSplitter implements Spliterator<MemorySegment> {
@ -541,42 +521,37 @@ public abstract non-sealed class AbstractMemorySegmentImpl implements MemorySegm
int size = limit - pos; int size = limit - pos;
AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb); AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl)nioAccess.bufferSegment(bb);
final MemorySessionImpl bufferSession; final MemorySession bufferSession;
int modes;
if (bufferSegment != null) { if (bufferSegment != null) {
bufferSession = bufferSegment.sessionImpl(); bufferSession = bufferSegment.session;
modes = bufferSegment.mask;
} else { } else {
bufferSession = MemorySessionImpl.heapSession(bb); bufferSession = MemorySessionImpl.heapSession(bb);
modes = DEFAULT_MODES;
}
if (bb.isReadOnly()) {
modes |= READ_ONLY;
} }
boolean readOnly = bb.isReadOnly();
int scaleFactor = getScaleFactor(bb); int scaleFactor = getScaleFactor(bb);
if (base != null) { if (base != null) {
if (base instanceof byte[]) { 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[]) { } 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[]) { } 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[]) { } 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[]) { } 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[]) { } 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[]) { } 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 { } else {
throw new AssertionError("Cannot get here"); throw new AssertionError("Cannot get here");
} }
} else if (unmapper == null) { } 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 { } else {
// we can ignore scale factor here, a mapped buffer is always a byte buffer, so scaleFactor == 0. // 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);
} }
} }

View file

@ -55,17 +55,12 @@ final class ConfinedSession extends MemorySessionImpl {
super(owner, new ConfinedResourceList(), cleaner); super(owner, new ConfinedResourceList(), cleaner);
} }
@Override
public boolean isAlive() {
return state != CLOSED;
}
@Override @Override
@ForceInline @ForceInline
public void acquire0() { public void acquire0() {
checkValidStateSlow(); checkValidState();
if (state == MAX_FORKS) { if (state == MAX_FORKS) {
throw new IllegalStateException("Session keep alive limit exceeded"); throw tooManyAcquires();
} }
state++; state++;
} }
@ -85,11 +80,11 @@ final class ConfinedSession extends MemorySessionImpl {
} }
void justClose() { void justClose() {
checkValidStateSlow(); checkValidState();
if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) { if (state == 0 || state - ((int)ASYNC_RELEASE_COUNT.getVolatile(this)) == 0) {
state = CLOSED; state = CLOSED;
} else { } 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; cleanup.next = fst;
fst = cleanup; fst = cleanup;
} else { } else {
throw new IllegalStateException("Already closed!"); throw alreadyClosed();
} }
} }
@ -114,7 +109,7 @@ final class ConfinedSession extends MemorySessionImpl {
fst = ResourceCleanup.CLOSED_LIST; fst = ResourceCleanup.CLOSED_LIST;
cleanup(prev); cleanup(prev);
} else { } else {
throw new IllegalStateException("Attempt to cleanup an already closed resource list"); throw alreadyClosed();
} }
} }
} }

View file

@ -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 * 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 * 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 * 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 * 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 * 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, * 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; final Object base;
@ForceInline @ForceInline
HeapMemorySegmentImpl(long offset, Object base, long length, int mask) { HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) {
super(length, mask, MemorySessionImpl.GLOBAL); super(length, readOnly, MemorySessionImpl.GLOBAL);
this.offset = offset; this.offset = offset;
this.base = base; this.base = base;
} }
@Override @Override
abstract Object base(); public long unsafeGetOffset() {
@Override
long min() {
return offset; return offset;
} }
@Override @Override
abstract HeapMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session); abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session);
@Override @Override
ByteBuffer makeByteBuffer() { ByteBuffer makeByteBuffer() {
if (!(base() instanceof byte[])) { if (!(base instanceof byte[])) {
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array"); throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
} }
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); 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 // factories
public static class OfByte extends HeapMemorySegmentImpl { public static class OfByte extends HeapMemorySegmentImpl {
OfByte(long offset, Object base, long length, int mask) { OfByte(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfByte dup(long offset, long size, int mask, MemorySession session) { OfByte dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfByte(this.offset + offset, base, size, mask); return new OfByte(this.offset + offset, base, size, readOnly);
} }
@Override @Override
byte[] base() { public byte[] unsafeGetBase() {
return (byte[])Objects.requireNonNull(base); return (byte[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(byte[] arr) { public static MemorySegment fromArray(byte[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE; 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 @Override
@ -118,24 +115,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfChar extends HeapMemorySegmentImpl { public static class OfChar extends HeapMemorySegmentImpl {
OfChar(long offset, Object base, long length, int mask) { OfChar(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfChar dup(long offset, long size, int mask, MemorySession session) { OfChar dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfChar(this.offset + offset, base, size, mask); return new OfChar(this.offset + offset, base, size, readOnly);
} }
@Override @Override
char[] base() { public char[] unsafeGetBase() {
return (char[])Objects.requireNonNull(base); return (char[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(char[] arr) { public static MemorySegment fromArray(char[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE; 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 @Override
@ -146,24 +143,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfShort extends HeapMemorySegmentImpl { public static class OfShort extends HeapMemorySegmentImpl {
OfShort(long offset, Object base, long length, int mask) { OfShort(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfShort dup(long offset, long size, int mask, MemorySession session) { OfShort dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfShort(this.offset + offset, base, size, mask); return new OfShort(this.offset + offset, base, size, readOnly);
} }
@Override @Override
short[] base() { public short[] unsafeGetBase() {
return (short[])Objects.requireNonNull(base); return (short[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(short[] arr) { public static MemorySegment fromArray(short[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE; 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 @Override
@ -174,24 +171,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfInt extends HeapMemorySegmentImpl { public static class OfInt extends HeapMemorySegmentImpl {
OfInt(long offset, Object base, long length, int mask) { OfInt(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfInt dup(long offset, long size, int mask, MemorySession session) { OfInt dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfInt(this.offset + offset, base, size, mask); return new OfInt(this.offset + offset, base, size, readOnly);
} }
@Override @Override
int[] base() { public int[] unsafeGetBase() {
return (int[])Objects.requireNonNull(base); return (int[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(int[] arr) { public static MemorySegment fromArray(int[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE; 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 @Override
@ -202,24 +199,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfLong extends HeapMemorySegmentImpl { public static class OfLong extends HeapMemorySegmentImpl {
OfLong(long offset, Object base, long length, int mask) { OfLong(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfLong dup(long offset, long size, int mask, MemorySession session) { OfLong dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfLong(this.offset + offset, base, size, mask); return new OfLong(this.offset + offset, base, size, readOnly);
} }
@Override @Override
long[] base() { public long[] unsafeGetBase() {
return (long[])Objects.requireNonNull(base); return (long[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(long[] arr) { public static MemorySegment fromArray(long[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE; 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 @Override
@ -230,24 +227,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfFloat extends HeapMemorySegmentImpl { public static class OfFloat extends HeapMemorySegmentImpl {
OfFloat(long offset, Object base, long length, int mask) { OfFloat(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfFloat dup(long offset, long size, int mask, MemorySession session) { OfFloat dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfFloat(this.offset + offset, base, size, mask); return new OfFloat(this.offset + offset, base, size, readOnly);
} }
@Override @Override
float[] base() { public float[] unsafeGetBase() {
return (float[])Objects.requireNonNull(base); return (float[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(float[] arr) { public static MemorySegment fromArray(float[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE; 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 @Override
@ -258,24 +255,24 @@ public abstract class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static class OfDouble extends HeapMemorySegmentImpl { public static class OfDouble extends HeapMemorySegmentImpl {
OfDouble(long offset, Object base, long length, int mask) { OfDouble(long offset, Object base, long length, boolean readOnly) {
super(offset, base, length, mask); super(offset, base, length, readOnly);
} }
@Override @Override
OfDouble dup(long offset, long size, int mask, MemorySession session) { OfDouble dup(long offset, long size, boolean readOnly, MemorySession session) {
return new OfDouble(this.offset + offset, base, size, mask); return new OfDouble(this.offset + offset, base, size, readOnly);
} }
@Override @Override
double[] base() { public double[] unsafeGetBase() {
return (double[])Objects.requireNonNull(base); return (double[])Objects.requireNonNull(base);
} }
public static MemorySegment fromArray(double[] arr) { public static MemorySegment fromArray(double[] arr) {
Objects.requireNonNull(arr); Objects.requireNonNull(arr);
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE; 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 @Override

View file

@ -43,8 +43,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, MemorySession session) { public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySession session) {
super(min, length, mask, session); super(min, length, readOnly, session);
this.unmapper = unmapper; this.unmapper = unmapper;
} }
@ -55,8 +55,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
} }
@Override @Override
MappedMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session) { MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
return new MappedMemorySegmentImpl(min + offset, unmapper, size, mask, session); return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, session);
} }
// mapped segment methods // mapped segment methods
@ -95,8 +95,8 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
public static class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl { public static class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl {
public EmptyMappedMemorySegmentImpl(int modes, MemorySession session) { public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) {
super(0, null, 0, modes, session); super(0, null, 0, readOnly, session);
} }
@Override @Override

View file

@ -95,7 +95,7 @@ public final class MemoryAddressImpl implements MemoryAddress, Scoped {
} }
@Override @Override
public MemorySessionImpl sessionImpl() { public MemorySessionImpl session() {
return MemorySessionImpl.GLOBAL; return MemorySessionImpl.GLOBAL;
} }

View file

@ -43,16 +43,16 @@ import sun.nio.ch.DirectBuffer;
* This class manages the temporal bounds associated with a memory segment as well * 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 * 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 * (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. * 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 * 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 * 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, * 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 * 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}). * 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 ResourceList resourceList;
final Cleaner.Cleanable cleanable; final Cleaner.Cleanable cleanable;
final Thread owner; final Thread owner;
@ -78,8 +78,7 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
@Override @Override
public void addCloseAction(Runnable runnable) { public void addCloseAction(Runnable runnable) {
Objects.requireNonNull(runnable); Objects.requireNonNull(runnable);
addInternal(runnable instanceof ResourceList.ResourceCleanup cleanup ? addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
cleanup : ResourceList.ResourceCleanup.ofRunnable(runnable));
} }
/** /**
@ -102,7 +101,7 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
} }
void addInternal(ResourceList.ResourceCleanup resource) { 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 // 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 // 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 // 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(); public abstract void acquire0();
@Override @Override
public boolean equals(Object o) { public final boolean equals(Object o) {
return (o instanceof MemorySession other) && return (o instanceof MemorySession other) &&
toSessionImpl(other) == this; toSessionImpl(other) == this;
} }
@Override @Override
public int hashCode() { public final int hashCode() {
return super.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. * 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. * @return {@code true} if this session is not closed yet.
*/ */
public abstract boolean isAlive(); public boolean isAlive() {
return state >= OPEN;
}
@Override @Override
public MemorySession asNonCloseable() { public MemorySession asNonCloseable() {
@ -182,30 +183,27 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
new NonCloseableView(this) : this; new NonCloseableView(this) : this;
} }
@ForceInline
public static MemorySessionImpl toSessionImpl(MemorySession session) { public static MemorySessionImpl toSessionImpl(MemorySession session) {
return ((Scoped)session).sessionImpl(); return session instanceof MemorySessionImpl sessionImpl ?
} sessionImpl : ((NonCloseableView)session).session;
@Override
public MemorySessionImpl sessionImpl() {
return this;
} }
/** /**
* 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 * 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 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 * 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, * 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 @ForceInline
public final void checkValidState() { public void checkValidStateRaw() {
if (owner != null && owner != Thread.currentThread()) { if (owner != null && owner != Thread.currentThread()) {
throw new WrongThreadException("Attempted access outside owning thread"); throw WRONG_THREAD;
} }
if (state < OPEN) { 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 * @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. * a confined session and this method is called outside of the owner thread.
*/ */
public final void checkValidStateSlow() { public void checkValidState() {
if (owner != null && Thread.currentThread() != owner) { try {
throw new WrongThreadException("Attempted access outside owning thread"); checkValidStateRaw();
} else if (!isAlive()) { } catch (ScopedMemoryAccess.ScopedAccessError error) {
throw new IllegalStateException("Already closed"); throw error.newRuntimeException();
} }
} }
@ -289,14 +287,9 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
// do nothing // do nothing
} }
@Override
public boolean isAlive() {
return true;
}
@Override @Override
public void justClose() { public void justClose() {
throw new UnsupportedOperationException(); throw nonCloseable();
} }
} }
@ -335,19 +328,9 @@ public abstract non-sealed class MemorySessionImpl implements Scoped, MemorySess
return false; return false;
} }
@Override
public boolean isAlive() {
return true;
}
@Override
public MemorySession asNonCloseable() {
return this;
}
@Override @Override
public void justClose() { 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 * 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. * 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; final MemorySessionImpl session;
public NonCloseableView(MemorySessionImpl session) { public NonCloseableView(MemorySessionImpl session) {
this.session = session; this.session = session;
} }
public MemorySessionImpl sessionImpl() {
return session;
}
@Override @Override
public boolean isAlive() { public boolean isAlive() {
return session.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);
} }

View file

@ -41,14 +41,14 @@ import sun.security.action.GetBooleanAction;
*/ */
public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { 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 @Override
void checkBounds(long offset, long length) { void checkBounds(long offset, long length) {
// do nothing // do nothing
} }
@Override @Override
NativeMemorySegmentImpl dup(long offset, long size, int mask, MemorySession scope) { NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
}; };
@ -64,8 +64,8 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
final long min; final long min;
@ForceInline @ForceInline
NativeMemorySegmentImpl(long min, long length, int mask, MemorySession session) { NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySession session) {
super(length, mask, session); super(length, readOnly, session);
this.min = min; this.min = min;
} }
@ -77,13 +77,13 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
} }
@Override @Override
NativeMemorySegmentImpl dup(long offset, long size, int mask, MemorySession session) { NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySession session) {
return new NativeMemorySegmentImpl(min + offset, size, mask, session); return new NativeMemorySegmentImpl(min + offset, size, readOnly, session);
} }
@Override @Override
ByteBuffer makeByteBuffer() { ByteBuffer makeByteBuffer() {
return nioAccess.newDirectByteBuffer(min(), (int) this.length, null, return nioAccess.newDirectByteBuffer(min, (int) this.length, null,
session == MemorySessionImpl.GLOBAL ? null : this); session == MemorySessionImpl.GLOBAL ? null : this);
} }
@ -93,12 +93,12 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
} }
@Override @Override
long min() { public long unsafeGetOffset() {
return min; return min;
} }
@Override @Override
Object base() { public Object unsafeGetBase() {
return null; return null;
} }
@ -111,7 +111,7 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes, MemorySession session) { public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes, MemorySession session) {
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session); MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
sessionImpl.checkValidStateSlow(); sessionImpl.checkValidState();
if (VM.isDirectMemoryPageAligned()) { if (VM.isDirectMemoryPageAligned()) {
alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize()); alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize());
} }
@ -127,7 +127,7 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
} }
long alignedBuf = Utils.alignUp(buf, alignmentBytes); long alignedBuf = Utils.alignUp(buf, alignmentBytes);
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize, AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
DEFAULT_MODES, session); false, session);
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() { sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override @Override
public void cleanup() { public void cleanup() {
@ -143,9 +143,8 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
} }
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, MemorySession session) { public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, MemorySession session) {
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session); MemorySessionImpl.toSessionImpl(session).checkValidState();
sessionImpl.checkValidStateSlow(); AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, false, session);
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, DEFAULT_MODES, session);
return segment; return segment;
} }
} }

View file

@ -25,6 +25,14 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.foreign.MemorySession;
public interface Scoped { public interface Scoped {
MemorySessionImpl sessionImpl(); @ForceInline
default MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session());
}
MemorySession session();
} }

View file

@ -56,10 +56,10 @@ class SharedSession extends MemorySessionImpl {
value = (int) STATE.getVolatile(this); value = (int) STATE.getVolatile(this);
if (value < OPEN) { if (value < OPEN) {
//segment is not open! //segment is not open!
throw new IllegalStateException("Already closed"); throw alreadyClosed();
} else if (value == MAX_FORKS) { } else if (value == MAX_FORKS) {
//overflow //overflow
throw new IllegalStateException("Session acquire limit exceeded"); throw tooManyAcquires();
} }
} while (!STATE.compareAndSet(this, value, value + 1)); } while (!STATE.compareAndSet(this, value, value + 1));
} }
@ -72,7 +72,7 @@ class SharedSession extends MemorySessionImpl {
value = (int) STATE.getVolatile(this); value = (int) STATE.getVolatile(this);
if (value <= OPEN) { if (value <= OPEN) {
//cannot get here - we can't close segment twice //cannot get here - we can't close segment twice
throw new IllegalStateException("Already closed"); throw alreadyClosed();
} }
} while (!STATE.compareAndSet(this, value, value - 1)); } while (!STATE.compareAndSet(this, value, value - 1));
} }
@ -80,22 +80,17 @@ class SharedSession extends MemorySessionImpl {
void justClose() { void justClose() {
int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSING); int prevState = (int) STATE.compareAndExchange(this, OPEN, CLOSING);
if (prevState < 0) { if (prevState < 0) {
throw new IllegalStateException("Already closed"); throw alreadyClosed();
} else if (prevState != OPEN) { } else if (prevState != OPEN) {
throw new IllegalStateException("Session is acquired by " + prevState + " clients"); throw alreadyAcquired(prevState);
} }
boolean success = SCOPED_MEMORY_ACCESS.closeScope(this); boolean success = SCOPED_MEMORY_ACCESS.closeScope(this);
STATE.setVolatile(this, success ? CLOSED : OPEN); STATE.setVolatile(this, success ? CLOSED : OPEN);
if (!success) { 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. * 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); ResourceCleanup prev = (ResourceCleanup) FST.getVolatile(this);
if (prev == ResourceCleanup.CLOSED_LIST) { if (prev == ResourceCleanup.CLOSED_LIST) {
// too late // too late
throw new IllegalStateException("Already closed"); throw alreadyClosed();
} }
cleanup.next = prev; cleanup.next = prev;
if (FST.compareAndSet(this, prev, cleanup)) { if (FST.compareAndSet(this, prev, cleanup)) {
@ -144,7 +139,7 @@ class SharedSession extends MemorySessionImpl {
} }
cleanup(prev); cleanup(prev);
} else { } else {
throw new IllegalStateException("Attempt to cleanup an already closed resource list"); throw alreadyClosed();
} }
} }
} }

View file

@ -56,6 +56,7 @@ import java.lang.ref.Reference;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -382,13 +383,15 @@ public class SharedUtils {
return firstPos != -1 && argIndex >= firstPos; return firstPos != -1 && argIndex >= firstPos;
} }
public static NoSuchElementException newVaListNSEE(MemoryLayout layout) {
return new NoSuchElementException("No such element: " + layout);
}
public static class SimpleVaArg { public static class SimpleVaArg {
public final Class<?> carrier;
public final MemoryLayout layout; public final MemoryLayout layout;
public final Object value; public final Object value;
public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) { public SimpleVaArg(MemoryLayout layout, Object value) {
this.carrier = carrier;
this.layout = layout; this.layout = layout;
this.value = value; this.value = value;
} }
@ -445,11 +448,6 @@ public class SharedUtils {
return MemorySessionImpl.GLOBAL; return MemorySessionImpl.GLOBAL;
} }
@Override
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.GLOBAL;
}
@Override @Override
public VaList copy() { public VaList copy() {
return this; return this;

View file

@ -52,8 +52,6 @@ import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMEN
public non-sealed class LinuxAArch64VaList implements VaList, Scoped { public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
private static final Unsafe U = Unsafe.getUnsafe(); private static final Unsafe U = Unsafe.getUnsafe();
static final Class<?> CARRIER = MemoryAddress.class;
// See AAPCS Appendix B "Variable Argument Lists" for definition of // See AAPCS Appendix B "Variable Argument Lists" for definition of
// va_list on AArch64. // va_list on AArch64.
// //
@ -73,6 +71,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
AArch64.C_INT.withName("__vr_offs") AArch64.C_INT.withName("__vr_offs")
).withName("__va_list"); ).withName("__va_list");
private static final long STACK_SLOT_SIZE = 8;
private static final MemoryLayout GP_REG private static final MemoryLayout GP_REG
= MemoryLayout.paddingLayout(64).withBitAlignment(64); = MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG private static final MemoryLayout FP_REG
@ -101,22 +101,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
= new SharedUtils.EmptyVaList(emptyListAddress()); = new SharedUtils.EmptyVaList(emptyListAddress());
private final MemorySegment segment; private final MemorySegment segment;
private MemorySegment stack;
private final MemorySegment gpRegsArea; private final MemorySegment gpRegsArea;
private final long gpLimit;
private final MemorySegment fpRegsArea; 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.segment = segment;
this.stack = stack;
this.gpRegsArea = gpRegsArea; this.gpRegsArea = gpRegsArea;
this.gpLimit = gpLimit;
this.fpRegsArea = fpRegsArea; this.fpRegsArea = fpRegsArea;
this.fpLimit = fpLimit;
} }
private static LinuxAArch64VaList readFromSegment(MemorySegment segment) { 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), MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET),
MAX_GP_OFFSET, segment.session()); MAX_GP_OFFSET, segment.session());
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET), MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET),
MAX_FP_OFFSET, segment.session()); 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() { private static MemoryAddress emptyListAddress() {
@ -165,12 +175,17 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
return offs; return offs;
} }
private MemoryAddress stackPtr() { private static MemoryAddress stackPtr(MemorySegment segment) {
return (MemoryAddress) VH_stack.get(segment); return (MemoryAddress) VH_stack.get(segment);
} }
private void stackPtr(MemoryAddress ptr) { private MemoryAddress stackPtr() {
VH_stack.set(segment, ptr); return stackPtr(segment);
}
private void setStack(MemorySegment newStack) {
stack = newStack;
VH_stack.set(segment, stack.address());
} }
private void consumeGPSlots(int num) { private void consumeGPSlots(int num) {
@ -199,54 +214,62 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
return fpRegsArea.byteSize() + vrOffs(); return fpRegsArea.byteSize() + vrOffs();
} }
private void preAlignStack(MemoryLayout layout) { private long preAlignOffset(MemoryLayout layout) {
if (layout.byteAlignment() > 8) { long alignmentOffset = 0;
stackPtr(Utils.alignUp(stackPtr(), 16)); 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) { private void postAlignStack(MemoryLayout layout) {
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8)); setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
} }
@Override @Override
public int nextVarg(ValueLayout.OfInt layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(layout);
} }
@Override @Override
public long nextVarg(ValueLayout.OfLong layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(layout);
} }
@Override @Override
public double nextVarg(ValueLayout.OfDouble layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(layout);
} }
@Override @Override
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(layout);
} }
@Override @Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read( layout, allocator);
} }
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(MemoryLayout layout) {
return read(carrier, layout, THROWING_ALLOCATOR); return read(layout, THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
checkStackElement(layout);
preAlignStack(layout); preAlignStack(layout);
return switch (typeClass) { return switch (typeClass) {
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> { 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); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice); seg.copyFrom(slice);
postAlignStack(layout); postAlignStack(layout);
@ -254,7 +277,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
} }
case POINTER, INTEGER, FLOAT -> { case POINTER, INTEGER, FLOAT -> {
VarHandle reader = layout.varHandle(); VarHandle reader = layout.varHandle();
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session()); MemorySegment slice = stack.asSlice(0, layout.byteSize());
Object res = reader.get(slice); Object res = reader.get(slice);
postAlignStack(layout); postAlignStack(layout);
yield res; yield res;
@ -263,6 +286,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
} else { } else {
return switch (typeClass) { return switch (typeClass) {
case STRUCT_REGISTER -> { case STRUCT_REGISTER -> {
checkGPElement(layout, numSlots(layout));
// Struct is passed packed in integer registers. // Struct is passed packed in integer registers.
MemorySegment value = allocator.allocate(layout); MemorySegment value = allocator.allocate(layout);
long offset = 0; long offset = 0;
@ -275,6 +299,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
yield value; yield value;
} }
case STRUCT_HFA -> { case STRUCT_HFA -> {
checkFPElement(layout, numSlots(layout));
// Struct is passed with each element in a separate floating // Struct is passed with each element in a separate floating
// point register. // point register.
MemorySegment value = allocator.allocate(layout); MemorySegment value = allocator.allocate(layout);
@ -290,6 +315,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
yield value; yield value;
} }
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
checkGPElement(layout, 1);
// Struct is passed indirectly via a pointer in an integer register. // Struct is passed indirectly via a pointer in an integer register.
VarHandle ptrReader = AArch64.C_POINTER.varHandle(); VarHandle ptrReader = AArch64.C_POINTER.varHandle();
MemoryAddress ptr = (MemoryAddress) ptrReader.get( MemoryAddress ptr = (MemoryAddress) ptrReader.get(
@ -302,12 +328,14 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
yield seg; yield seg;
} }
case POINTER, INTEGER -> { case POINTER, INTEGER -> {
checkGPElement(layout, 1);
VarHandle reader = layout.varHandle(); VarHandle reader = layout.varHandle();
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset())); Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
consumeGPSlots(1); consumeGPSlots(1);
yield res; yield res;
} }
case FLOAT -> { case FLOAT -> {
checkFPElement(layout, 1);
VarHandle reader = layout.varHandle(); VarHandle reader = layout.varHandle();
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset())); Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
consumeFPSlots(1); 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 @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow(); sessionImpl().checkValidState();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) { if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
checkStackElement(layout);
preAlignStack(layout); preAlignStack(layout);
postAlignStack(layout); postAlignStack(layout);
} else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) { } 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) { } else if (typeClass == TypeClass.STRUCT_REFERENCE) {
checkGPElement(layout, 1);
consumeGPSlots(1); consumeGPSlots(1);
} else { } 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(); return segment.session();
} }
@Override
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session());
}
@Override @Override
public VaList copy() { public VaList copy() {
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session()); MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
copy.copyFrom(segment); copy.copyFrom(segment);
return new LinuxAArch64VaList(copy, gpRegsArea, fpRegsArea); return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit);
} }
@Override @Override
@ -367,8 +414,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
return segment.address(); return segment.address();
} }
private static int numSlots(MemoryLayout layout) { private static long numSlots(MemoryLayout layout) {
return (int) Utils.alignUp(layout.byteSize(), 8) / 8; return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE;
} }
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset,
@ -410,35 +457,35 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
@Override @Override
public Builder addVarg(ValueLayout.OfInt layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfLong layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(layout, value.address());
} }
@Override @Override
public Builder addVarg(GroupLayout layout, MemorySegment value) { 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(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) { if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
stackArgs.add(new SimpleVaArg(carrier, layout, value)); stackArgs.add(new SimpleVaArg(layout, value));
} else { } else {
switch (typeClass) { switch (typeClass) {
case STRUCT_REGISTER -> { case STRUCT_REGISTER -> {
@ -500,30 +547,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemorySegment vaListSegment = allocator.allocate(LAYOUT);
MemoryAddress stackArgsPtr = MemoryAddress.NULL; MemorySegment stackArgsSegment;
if (!stackArgs.isEmpty()) { if (!stackArgs.isEmpty()) {
long stackArgsSize = stackArgs.stream() long stackArgsSize = stackArgs.stream()
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), 8), Long::sum); .reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16); stackArgsSegment = allocator.allocate(stackArgsSize, 16);
stackArgsPtr = stackArgsSegment.address(); MemorySegment writeCursor = stackArgsSegment;
for (SimpleVaArg arg : stackArgs) { for (SimpleVaArg arg : stackArgs) {
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), 8); final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE);
stackArgsSegment = Utils.alignUp(stackArgsSegment, alignedSize); writeCursor = Utils.alignUp(writeCursor, alignedSize);
VarHandle writer = arg.varHandle(); VarHandle writer = arg.varHandle();
writer.set(stackArgsSegment, arg.value); writer.set(writeCursor, arg.value);
stackArgsSegment = stackArgsSegment.asSlice(alignedSize); writeCursor = writeCursor.asSlice(alignedSize);
} }
} else {
stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session);
} }
VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()).address()); VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()).address());
VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.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_gr_offs.set(vaListSegment, -MAX_GP_OFFSET);
VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET); VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET);
assert gpRegs.session().ownerThread() == vaListSegment.session().ownerThread(); assert gpRegs.session().ownerThread() == vaListSegment.session().ownerThread();
assert fpRegs.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);
} }
} }
} }

View file

@ -46,7 +46,6 @@ import static jdk.internal.foreign.abi.SharedUtils.alignUp;
* char* instead of the structure defined in the AAPCS. * char* instead of the structure defined in the AAPCS.
*/ */
public non-sealed class MacOsAArch64VaList implements VaList, Scoped { 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 long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = C_POINTER.varHandle(); private static final VarHandle VH_address = C_POINTER.varHandle();
@ -66,41 +65,42 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
@Override @Override
public int nextVarg(ValueLayout.OfInt layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(layout);
} }
@Override @Override
public long nextVarg(ValueLayout.OfLong layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(layout);
} }
@Override @Override
public double nextVarg(ValueLayout.OfDouble layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(layout);
} }
@Override @Override
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(layout);
} }
@Override @Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(layout, allocator);
} }
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(MemoryLayout layout) {
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); 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); Objects.requireNonNull(layout);
Object res; Object res;
if (carrier == MemorySegment.class) { if (layout instanceof GroupLayout) {
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
res = switch (typeClass) { res = switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
checkElement(layout, VA_SLOT_SIZE_BYTES);
MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment); MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session()); MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
@ -109,14 +109,17 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
yield seg; yield seg;
} }
case STRUCT_REGISTER, STRUCT_HFA -> { case STRUCT_REGISTER, STRUCT_HFA -> {
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
checkElement(layout, size);
MemorySegment struct = allocator.allocate(layout) MemorySegment struct = allocator.allocate(layout)
.copyFrom(segment.asSlice(0, layout.byteSize())); .copyFrom(segment.asSlice(0, layout.byteSize()));
segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES)); segment = segment.asSlice(size);
yield struct; yield struct;
} }
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass); default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
}; };
} else { } else {
checkElement(layout, VA_SLOT_SIZE_BYTES);
VarHandle reader = layout.varHandle(); VarHandle reader = layout.varHandle();
res = reader.get(segment); res = reader.get(segment);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES); segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
@ -124,17 +127,29 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
return res; 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 @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow(); sessionImpl().checkValidState();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
segment = segment.asSlice(switch (TypeClass.classifyLayout(layout)) { long size = sizeOf(layout);
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES); checkElement(layout, size);
default -> VA_SLOT_SIZE_BYTES; 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; return session;
} }
@Override
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session());
}
@Override @Override
public VaList copy() { public VaList copy() {
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow(); sessionImpl().checkValidState();
return new MacOsAArch64VaList(segment, session); return new MacOsAArch64VaList(segment, session);
} }
@ -174,40 +184,40 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
private final List<SimpleVaArg> args = new ArrayList<>(); private final List<SimpleVaArg> args = new ArrayList<>();
public Builder(MemorySession session) { public Builder(MemorySession session) {
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow(); MemorySessionImpl.toSessionImpl(session).checkValidState();
this.session = session; this.session = session;
} }
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
args.add(new SimpleVaArg(carrier, layout, value)); args.add(new SimpleVaArg(layout, value));
return this; return this;
} }
@Override @Override
public Builder addVarg(ValueLayout.OfInt layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfLong layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(layout, value.address());
} }
@Override @Override
public Builder addVarg(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(layout, value);
} }
public VaList build() { public VaList build() {
@ -217,22 +227,18 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
// Each argument may occupy up to four slots long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4); MemorySegment segment = allocator.allocate(allocationSize);
List<MemorySegment> attachedSegments = new ArrayList<>();
attachedSegments.add(segment);
MemorySegment cursor = segment; MemorySegment cursor = segment;
for (SimpleVaArg arg : args) { for (SimpleVaArg arg : args) {
if (arg.carrier == MemorySegment.class) { if (arg.layout instanceof GroupLayout) {
MemorySegment msArg = ((MemorySegment) arg.value); MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.classifyLayout(arg.layout); TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
switch (typeClass) { switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
MemorySegment copy = allocator.allocate(arg.layout); MemorySegment copy = allocator.allocate(arg.layout);
copy.copyFrom(msArg); // by-value copy.copyFrom(msArg); // by-value
attachedSegments.add(copy);
VH_address.set(cursor, copy.address()); VH_address.set(cursor, copy.address());
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES); cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
} }

View file

@ -48,8 +48,6 @@ import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
public non-sealed class SysVVaList implements VaList, Scoped { public non-sealed class SysVVaList implements VaList, Scoped {
private static final Unsafe U = Unsafe.getUnsafe(); private static final Unsafe U = Unsafe.getUnsafe();
static final Class<?> CARRIER = MemoryAddress.class;
// struct typedef __va_list_tag __va_list_tag { // struct typedef __va_list_tag __va_list_tag {
// unsigned int gp_offset; /* 0 4 */ // unsigned int gp_offset; /* 0 4 */
// unsigned int fp_offset; /* 4 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") SysV.C_POINTER.withName("reg_save_area")
).withName("__va_list_tag"); ).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 GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128); 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 static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
private final MemorySegment segment; private final MemorySegment segment;
private MemorySegment overflowArgArea;
private final MemorySegment regSaveArea; 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.segment = segment;
this.overflowArgArea = overflowArgArea;
this.regSaveArea = regSaveArea; this.regSaveArea = regSaveArea;
this.gpLimit = gpLimit;
this.fpLimit = fpLimit;
} }
private static SysVVaList readFromSegment(MemorySegment segment) { private static SysVVaList readFromSegment(MemorySegment segment) {
MemorySegment regSaveArea = getRegSaveArea(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() { private static MemoryAddress emptyListAddress() {
@ -157,72 +166,78 @@ public non-sealed class SysVVaList implements VaList, Scoped {
VH_fp_offset.set(segment, i); 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) { private static MemorySegment getRegSaveArea(MemorySegment segment) {
return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)), return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)),
LAYOUT_REG_SAVE_AREA.byteSize(), segment.session()); LAYOUT_REG_SAVE_AREA.byteSize(), segment.session());
} }
private void preAlignStack(MemoryLayout layout) { private static MemorySegment getArgOverflowArea(MemorySegment segment) {
if (layout.byteAlignment() > 8) { return MemorySegment.ofAddress(((MemoryAddress)VH_overflow_arg_area.get(segment)),
stackPtr(Utils.alignUp(stackPtr(), 16)); 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) { private void postAlignStack(MemoryLayout layout) {
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8)); setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
} }
@Override @Override
public int nextVarg(ValueLayout.OfInt layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(layout);
} }
@Override @Override
public long nextVarg(ValueLayout.OfLong layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(layout);
} }
@Override @Override
public double nextVarg(ValueLayout.OfDouble layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(layout);
} }
@Override @Override
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(layout);
} }
@Override @Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(layout, allocator);
} }
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(MemoryLayout layout) {
return read(carrier, layout, THROWING_ALLOCATOR); return read(layout, THROWING_ALLOCATOR);
} }
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) { private Object read(MemoryLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass) if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|| typeClass.inMemory()) { || typeClass.inMemory()) {
checkStackElement(layout);
preAlignStack(layout); preAlignStack(layout);
return switch (typeClass.kind()) { return switch (typeClass.kind()) {
case STRUCT -> { case STRUCT -> {
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session()); MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
MemorySegment seg = allocator.allocate(layout); MemorySegment seg = allocator.allocate(layout);
seg.copyFrom(slice); seg.copyFrom(slice);
postAlignStack(layout); postAlignStack(layout);
@ -230,15 +245,14 @@ public non-sealed class SysVVaList implements VaList, Scoped {
} }
case POINTER, INTEGER, FLOAT -> { case POINTER, INTEGER, FLOAT -> {
VarHandle reader = layout.varHandle(); VarHandle reader = layout.varHandle();
try (MemorySession localSession = MemorySession.openConfined()) { MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), localSession);
Object res = reader.get(slice); Object res = reader.get(slice);
postAlignStack(layout); postAlignStack(layout);
yield res; yield res;
} }
}
}; };
} else { } else {
checkRegSaveAreaElement(layout, typeClass);
return switch (typeClass.kind()) { return switch (typeClass.kind()) {
case STRUCT -> { case STRUCT -> {
MemorySegment value = allocator.allocate(layout); 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 @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow(); sessionImpl().checkValidState();
for (MemoryLayout layout : layouts) { for (MemoryLayout layout : layouts) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) { if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) {
checkStackElement(layout);
preAlignStack(layout); preAlignStack(layout);
postAlignStack(layout); postAlignStack(layout);
} else { } else {
checkRegSaveAreaElement(layout, typeClass);
currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE)); currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE));
currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_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(); return segment.session();
} }
@Override
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session());
}
@Override @Override
public VaList copy() { public VaList copy() {
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session()); MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
copy.copyFrom(segment); copy.copyFrom(segment);
return new SysVVaList(copy, regSaveArea); return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit);
} }
@Override @Override
@ -331,8 +358,8 @@ public non-sealed class SysVVaList implements VaList, Scoped {
return "SysVVaList{" return "SysVVaList{"
+ "gp_offset=" + currentGPOffset() + "gp_offset=" + currentGPOffset()
+ ", fp_offset=" + currentFPOffset() + ", fp_offset=" + currentFPOffset()
+ ", overflow_arg_area=" + stackPtr() + ", overflow_arg_area=" + overflowArgArea
+ ", reg_save_area=" + regSaveArea() + ", reg_save_area=" + regSaveArea
+ '}'; + '}';
} }
@ -350,37 +377,37 @@ public non-sealed class SysVVaList implements VaList, Scoped {
@Override @Override
public Builder addVarg(ValueLayout.OfInt layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfLong layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(layout, value.address());
} }
@Override @Override
public Builder addVarg(GroupLayout layout, MemorySegment value) { 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(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
TypeClass typeClass = TypeClass.classifyLayout(layout); TypeClass typeClass = TypeClass.classifyLayout(layout);
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass) if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|| typeClass.inMemory()) { || typeClass.inMemory()) {
// stack it! // stack it!
stackArgs.add(new SimpleVaArg(carrier, layout, value)); stackArgs.add(new SimpleVaArg(layout, value));
} else { } else {
switch (typeClass.kind()) { switch (typeClass.kind()) {
case STRUCT -> { case STRUCT -> {
@ -426,31 +453,33 @@ public non-sealed class SysVVaList implements VaList, Scoped {
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
MemorySegment vaListSegment = allocator.allocate(LAYOUT); MemorySegment vaListSegment = allocator.allocate(LAYOUT);
MemoryAddress stackArgsPtr = MemoryAddress.NULL; MemorySegment stackArgsSegment;
if (!stackArgs.isEmpty()) { if (!stackArgs.isEmpty()) {
long stackArgsSize = stackArgs.stream().reduce(0L, (acc, e) -> acc + e.layout.byteSize(), Long::sum); long stackArgsSize = stackArgs.stream().reduce(0L,
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16); (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
MemorySegment maOverflowArgArea = stackArgsSegment; stackArgsSegment = allocator.allocate(stackArgsSize, 16);
MemorySegment writeCursor = stackArgsSegment;
for (SimpleVaArg arg : stackArgs) { for (SimpleVaArg arg : stackArgs) {
if (arg.layout.byteSize() > 8) { 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) { if (arg.value instanceof MemorySegment) {
maOverflowArgArea.copyFrom((MemorySegment) arg.value); writeCursor.copyFrom((MemorySegment) arg.value);
} else { } else {
VarHandle writer = arg.varHandle(); 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_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()); VH_reg_save_area.set(vaListSegment, reg_save_area.address());
assert reg_save_area.session().ownerThread() == vaListSegment.session().ownerThread(); 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);
} }
} }
} }

View file

@ -57,7 +57,6 @@ import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64))) // : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
// //
public non-sealed class WinVaList implements VaList, Scoped { 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 long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = C_POINTER.varHandle(); private static final VarHandle VH_address = C_POINTER.varHandle();
@ -77,38 +76,39 @@ public non-sealed class WinVaList implements VaList, Scoped {
@Override @Override
public int nextVarg(ValueLayout.OfInt layout) { public int nextVarg(ValueLayout.OfInt layout) {
return (int) read(int.class, layout); return (int) read(layout);
} }
@Override @Override
public long nextVarg(ValueLayout.OfLong layout) { public long nextVarg(ValueLayout.OfLong layout) {
return (long) read(long.class, layout); return (long) read(layout);
} }
@Override @Override
public double nextVarg(ValueLayout.OfDouble layout) { public double nextVarg(ValueLayout.OfDouble layout) {
return (double) read(double.class, layout); return (double) read(layout);
} }
@Override @Override
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) { public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
return (MemoryAddress) read(MemoryAddress.class, layout); return (MemoryAddress) read(layout);
} }
@Override @Override
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) { public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
Objects.requireNonNull(allocator); Objects.requireNonNull(allocator);
return (MemorySegment) read(MemorySegment.class, layout, allocator); return (MemorySegment) read(layout, allocator);
} }
private Object read(Class<?> carrier, MemoryLayout layout) { private Object read(MemoryLayout layout) {
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR); 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); Objects.requireNonNull(layout);
Object res; Object res;
if (carrier == MemorySegment.class) { checkElement(layout);
if (layout instanceof GroupLayout) {
TypeClass typeClass = TypeClass.typeClassFor(layout, false); TypeClass typeClass = TypeClass.typeClassFor(layout, false);
res = switch (typeClass) { res = switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
@ -130,12 +130,21 @@ public non-sealed class WinVaList implements VaList, Scoped {
return res; return res;
} }
private void checkElement(MemoryLayout layout) {
if (segment.byteSize() < VA_SLOT_SIZE_BYTES) {
throw SharedUtils.newVaListNSEE(layout);
}
}
@Override @Override
public void skip(MemoryLayout... layouts) { public void skip(MemoryLayout... layouts) {
Objects.requireNonNull(layouts); Objects.requireNonNull(layouts);
MemorySessionImpl.toSessionImpl(session()).checkValidStateSlow(); sessionImpl().checkValidState();
Stream.of(layouts).forEach(Objects::requireNonNull); for (MemoryLayout layout : layouts) {
segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES); Objects.requireNonNull(layout);
checkElement(layout);
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
}
} }
static WinVaList ofAddress(MemoryAddress addr, MemorySession session) { static WinVaList ofAddress(MemoryAddress addr, MemorySession session) {
@ -152,14 +161,9 @@ public non-sealed class WinVaList implements VaList, Scoped {
return session; return session;
} }
@Override
public MemorySessionImpl sessionImpl() {
return MemorySessionImpl.toSessionImpl(session());
}
@Override @Override
public VaList copy() { public VaList copy() {
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow(); sessionImpl().checkValidState();
return new WinVaList(segment, session); return new WinVaList(segment, session);
} }
@ -174,40 +178,40 @@ public non-sealed class WinVaList implements VaList, Scoped {
private final List<SimpleVaArg> args = new ArrayList<>(); private final List<SimpleVaArg> args = new ArrayList<>();
public Builder(MemorySession session) { public Builder(MemorySession session) {
MemorySessionImpl.toSessionImpl(session).checkValidStateSlow(); MemorySessionImpl.toSessionImpl(session).checkValidState();
this.session = session; this.session = session;
} }
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) { private Builder arg(MemoryLayout layout, Object value) {
Objects.requireNonNull(layout); Objects.requireNonNull(layout);
Objects.requireNonNull(value); Objects.requireNonNull(value);
args.add(new SimpleVaArg(carrier, layout, value)); args.add(new SimpleVaArg(layout, value));
return this; return this;
} }
@Override @Override
public Builder addVarg(ValueLayout.OfInt layout, int value) { public Builder addVarg(ValueLayout.OfInt layout, int value) {
return arg(int.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfLong layout, long value) { public Builder addVarg(ValueLayout.OfLong layout, long value) {
return arg(long.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfDouble layout, double value) { public Builder addVarg(ValueLayout.OfDouble layout, double value) {
return arg(double.class, layout, value); return arg(layout, value);
} }
@Override @Override
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) { public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
return arg(MemoryAddress.class, layout, value.address()); return arg(layout, value.address());
} }
@Override @Override
public Builder addVarg(GroupLayout layout, MemorySegment value) { public Builder addVarg(GroupLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value); return arg(layout, value);
} }
public VaList build() { public VaList build() {
@ -216,19 +220,16 @@ public non-sealed class WinVaList implements VaList, Scoped {
} }
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session); SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size()); MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
List<MemorySegment> attachedSegments = new ArrayList<>();
attachedSegments.add(segment);
MemorySegment cursor = segment; MemorySegment cursor = segment;
for (SimpleVaArg arg : args) { for (SimpleVaArg arg : args) {
if (arg.carrier == MemorySegment.class) { if (arg.layout instanceof GroupLayout) {
MemorySegment msArg = ((MemorySegment) arg.value); MemorySegment msArg = ((MemorySegment) arg.value);
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false); TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
switch (typeClass) { switch (typeClass) {
case STRUCT_REFERENCE -> { case STRUCT_REFERENCE -> {
MemorySegment copy = allocator.allocate(arg.layout); MemorySegment copy = allocator.allocate(arg.layout);
copy.copyFrom(msArg); // by-value copy.copyFrom(msArg); // by-value
attachedSegments.add(copy);
VH_address.set(cursor, copy.address()); VH_address.set(cursor, copy.address());
} }
case STRUCT_REGISTER -> case STRUCT_REGISTER ->

View file

@ -3,7 +3,7 @@
try { try {
return get$Type$Internal(session, base, offset); return get$Type$Internal(session, base, offset);
} catch (ScopedAccessError ex) { } 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) { private $type$ get$Type$Internal(MemorySessionImpl session, Object base, long offset) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.get$Type$(base, offset); return UNSAFE.get$Type$(base, offset);
} finally { } finally {
@ -24,7 +24,7 @@
try { try {
put$Type$Internal(session, base, offset, value); put$Type$Internal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private void put$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.put$Type$(base, offset, value); UNSAFE.put$Type$(base, offset, value);
} finally { } finally {
@ -46,7 +46,7 @@
try { try {
return get$Type$UnalignedInternal(session, base, offset, be); return get$Type$UnalignedInternal(session, base, offset, be);
} catch (ScopedAccessError ex) { } 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) { private $type$ get$Type$UnalignedInternal(MemorySessionImpl session, Object base, long offset, boolean be) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.get$Type$Unaligned(base, offset, be); return UNSAFE.get$Type$Unaligned(base, offset, be);
} finally { } finally {
@ -67,7 +67,7 @@
try { try {
put$Type$UnalignedInternal(session, base, offset, value, be); put$Type$UnalignedInternal(session, base, offset, value, be);
} catch (ScopedAccessError ex) { } 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) { private void put$Type$UnalignedInternal(MemorySessionImpl session, Object base, long offset, $type$ value, boolean be) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.put$Type$Unaligned(base, offset, value, be); UNSAFE.put$Type$Unaligned(base, offset, value, be);
} finally { } finally {
@ -89,7 +89,7 @@
try { try {
return get$Type$VolatileInternal(session, base, offset); return get$Type$VolatileInternal(session, base, offset);
} catch (ScopedAccessError ex) { } 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) { private $type$ get$Type$VolatileInternal(MemorySessionImpl session, Object base, long offset) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.get$Type$Volatile(base, offset); return UNSAFE.get$Type$Volatile(base, offset);
} finally { } finally {
@ -110,7 +110,7 @@
try { try {
put$Type$VolatileInternal(session, base, offset, value); put$Type$VolatileInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private void put$Type$VolatileInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.put$Type$Volatile(base, offset, value); UNSAFE.put$Type$Volatile(base, offset, value);
} finally { } finally {
@ -131,7 +131,7 @@
try { try {
return get$Type$AcquireInternal(session, base, offset); return get$Type$AcquireInternal(session, base, offset);
} catch (ScopedAccessError ex) { } 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) { private $type$ get$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.get$Type$Acquire(base, offset); return UNSAFE.get$Type$Acquire(base, offset);
} finally { } finally {
@ -152,7 +152,7 @@
try { try {
put$Type$ReleaseInternal(session, base, offset, value); put$Type$ReleaseInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private void put$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.put$Type$Release(base, offset, value); UNSAFE.put$Type$Release(base, offset, value);
} finally { } finally {
@ -173,7 +173,7 @@
try { try {
return get$Type$OpaqueInternal(session, base, offset); return get$Type$OpaqueInternal(session, base, offset);
} catch (ScopedAccessError ex) { } 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) { private $type$ get$Type$OpaqueInternal(MemorySessionImpl session, Object base, long offset) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.get$Type$Opaque(base, offset); return UNSAFE.get$Type$Opaque(base, offset);
} finally { } finally {
@ -193,7 +193,7 @@
try { try {
put$Type$OpaqueInternal(session, base, offset, value); put$Type$OpaqueInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private void put$Type$OpaqueInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.put$Type$Opaque(base, offset, value); UNSAFE.put$Type$Opaque(base, offset, value);
} finally { } finally {
@ -214,7 +214,7 @@
try { try {
return compareAndSet$Type$Internal(session, base, offset, expected, value); return compareAndSet$Type$Internal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private boolean compareAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.compareAndSet$Type$(base, offset, expected, value); return UNSAFE.compareAndSet$Type$(base, offset, expected, value);
} finally { } finally {
@ -235,7 +235,7 @@
try { try {
return compareAndExchange$Type$Internal(session, base, offset, expected, value); return compareAndExchange$Type$Internal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ compareAndExchange$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.compareAndExchange$Type$(base, offset, expected, value); return UNSAFE.compareAndExchange$Type$(base, offset, expected, value);
} finally { } finally {
@ -256,7 +256,7 @@
try { try {
return compareAndExchange$Type$AcquireInternal(session, base, offset, expected, value); return compareAndExchange$Type$AcquireInternal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ compareAndExchange$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.compareAndExchange$Type$Acquire(base, offset, expected, value); return UNSAFE.compareAndExchange$Type$Acquire(base, offset, expected, value);
} finally { } finally {
@ -277,7 +277,7 @@
try { try {
return compareAndExchange$Type$ReleaseInternal(session, base, offset, expected, value); return compareAndExchange$Type$ReleaseInternal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ compareAndExchange$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.compareAndExchange$Type$Release(base, offset, expected, value); return UNSAFE.compareAndExchange$Type$Release(base, offset, expected, value);
} finally { } finally {
@ -298,7 +298,7 @@
try { try {
return weakCompareAndSet$Type$PlainInternal(session, base, offset, expected, value); return weakCompareAndSet$Type$PlainInternal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private boolean weakCompareAndSet$Type$PlainInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.weakCompareAndSet$Type$Plain(base, offset, expected, value); return UNSAFE.weakCompareAndSet$Type$Plain(base, offset, expected, value);
} finally { } finally {
@ -319,7 +319,7 @@
try { try {
return weakCompareAndSet$Type$Internal(session, base, offset, expected, value); return weakCompareAndSet$Type$Internal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private boolean weakCompareAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.weakCompareAndSet$Type$(base, offset, expected, value); return UNSAFE.weakCompareAndSet$Type$(base, offset, expected, value);
} finally { } finally {
@ -340,7 +340,7 @@
try { try {
return weakCompareAndSet$Type$AcquireInternal(session, base, offset, expected, value); return weakCompareAndSet$Type$AcquireInternal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private boolean weakCompareAndSet$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.weakCompareAndSet$Type$Acquire(base, offset, expected, value); return UNSAFE.weakCompareAndSet$Type$Acquire(base, offset, expected, value);
} finally { } finally {
@ -361,7 +361,7 @@
try { try {
return weakCompareAndSet$Type$ReleaseInternal(session, base, offset, expected, value); return weakCompareAndSet$Type$ReleaseInternal(session, base, offset, expected, value);
} catch (ScopedAccessError ex) { } 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) { private boolean weakCompareAndSet$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ expected, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.weakCompareAndSet$Type$Release(base, offset, expected, value); return UNSAFE.weakCompareAndSet$Type$Release(base, offset, expected, value);
} finally { } finally {
@ -382,7 +382,7 @@
try { try {
return getAndSet$Type$Internal(session, base, offset, value); return getAndSet$Type$Internal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndSet$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndSet$Type$(base, offset, value); return UNSAFE.getAndSet$Type$(base, offset, value);
} finally { } finally {
@ -403,7 +403,7 @@
try { try {
return getAndSet$Type$AcquireInternal(session, base, offset, value); return getAndSet$Type$AcquireInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndSet$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndSet$Type$Acquire(base, offset, value); return UNSAFE.getAndSet$Type$Acquire(base, offset, value);
} finally { } finally {
@ -424,7 +424,7 @@
try { try {
return getAndSet$Type$ReleaseInternal(session, base, offset, value); return getAndSet$Type$ReleaseInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndSet$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndSet$Type$Release(base, offset, value); return UNSAFE.getAndSet$Type$Release(base, offset, value);
} finally { } finally {
@ -447,7 +447,7 @@
try { try {
return getAndAdd$Type$Internal(session, base, offset, delta); return getAndAdd$Type$Internal(session, base, offset, delta);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndAdd$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndAdd$Type$(base, offset, delta); return UNSAFE.getAndAdd$Type$(base, offset, delta);
} finally { } finally {
@ -468,7 +468,7 @@
try { try {
return getAndAdd$Type$AcquireInternal(session, base, offset, delta); return getAndAdd$Type$AcquireInternal(session, base, offset, delta);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndAdd$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndAdd$Type$Acquire(base, offset, delta); return UNSAFE.getAndAdd$Type$Acquire(base, offset, delta);
} finally { } finally {
@ -489,7 +489,7 @@
try { try {
return getAndAdd$Type$ReleaseInternal(session, base, offset, delta); return getAndAdd$Type$ReleaseInternal(session, base, offset, delta);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndAdd$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ delta) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndAdd$Type$Release(base, offset, delta); return UNSAFE.getAndAdd$Type$Release(base, offset, delta);
} finally { } finally {
@ -512,7 +512,7 @@
try { try {
return getAndBitwiseOr$Type$Internal(session, base, offset, value); return getAndBitwiseOr$Type$Internal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseOr$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseOr$Type$(base, offset, value); return UNSAFE.getAndBitwiseOr$Type$(base, offset, value);
} finally { } finally {
@ -533,7 +533,7 @@
try { try {
return getAndBitwiseOr$Type$AcquireInternal(session, base, offset, value); return getAndBitwiseOr$Type$AcquireInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseOr$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseOr$Type$Acquire(base, offset, value); return UNSAFE.getAndBitwiseOr$Type$Acquire(base, offset, value);
} finally { } finally {
@ -554,7 +554,7 @@
try { try {
return getAndBitwiseOr$Type$ReleaseInternal(session, base, offset, value); return getAndBitwiseOr$Type$ReleaseInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseOr$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseOr$Type$Release(base, offset, value); return UNSAFE.getAndBitwiseOr$Type$Release(base, offset, value);
} finally { } finally {
@ -575,7 +575,7 @@
try { try {
return getAndBitwiseAnd$Type$Internal(session, base, offset, value); return getAndBitwiseAnd$Type$Internal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseAnd$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseAnd$Type$(base, offset, value); return UNSAFE.getAndBitwiseAnd$Type$(base, offset, value);
} finally { } finally {
@ -596,7 +596,7 @@
try { try {
return getAndBitwiseAnd$Type$AcquireInternal(session, base, offset, value); return getAndBitwiseAnd$Type$AcquireInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseAnd$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseAnd$Type$Acquire(base, offset, value); return UNSAFE.getAndBitwiseAnd$Type$Acquire(base, offset, value);
} finally { } finally {
@ -617,7 +617,7 @@
try { try {
return getAndBitwiseAnd$Type$ReleaseInternal(session, base, offset, value); return getAndBitwiseAnd$Type$ReleaseInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseAnd$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseAnd$Type$Release(base, offset, value); return UNSAFE.getAndBitwiseAnd$Type$Release(base, offset, value);
} finally { } finally {
@ -638,7 +638,7 @@
try { try {
return getAndBitwiseXor$Type$Internal(session, base, offset, value); return getAndBitwiseXor$Type$Internal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseXor$Type$Internal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseXor$Type$(base, offset, value); return UNSAFE.getAndBitwiseXor$Type$(base, offset, value);
} finally { } finally {
@ -659,7 +659,7 @@
try { try {
return getAndBitwiseXor$Type$AcquireInternal(session, base, offset, value); return getAndBitwiseXor$Type$AcquireInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseXor$Type$AcquireInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseXor$Type$Acquire(base, offset, value); return UNSAFE.getAndBitwiseXor$Type$Acquire(base, offset, value);
} finally { } finally {
@ -680,7 +680,7 @@
try { try {
return getAndBitwiseXor$Type$ReleaseInternal(session, base, offset, value); return getAndBitwiseXor$Type$ReleaseInternal(session, base, offset, value);
} catch (ScopedAccessError ex) { } 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) { private $type$ getAndBitwiseXor$Type$ReleaseInternal(MemorySessionImpl session, Object base, long offset, $type$ value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return UNSAFE.getAndBitwiseXor$Type$Release(base, offset, value); return UNSAFE.getAndBitwiseXor$Type$Release(base, offset, value);
} finally { } finally {

View file

@ -32,6 +32,7 @@ import java.lang.annotation.Target;
import java.lang.foreign.MemorySegment; import java.lang.foreign.MemorySegment;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.util.function.Supplier;
import jdk.internal.access.JavaNioAccess; import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets; import jdk.internal.access.SharedSecrets;
@ -55,10 +56,10 @@ import jdk.internal.vm.vector.VectorSupport;
* a memory region while another thread is releasing it. * a memory region while another thread is releasing it.
* <p> * <p>
* This class provides tools to manage races when multiple threads are accessing and/or releasing the same memory * 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 * session concurrently. More specifically, when a thread wants to release a memory session, it should call the
* {@link MemorySessionImpl#close()} method. This method initiates thread-local handshakes with all the other VM threads, * {@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 * 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> * <p>
* This synchronization strategy relies on the idea that accessing memory is atomic with respect to checking the * 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 * 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 { 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; static final long serialVersionUID = 1L;
public static final ScopedAccessError INSTANCE = new ScopedAccessError(); public final RuntimeException newRuntimeException() {
return runtimeExceptionSupplier.get();
}
} }
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@ -112,64 +121,64 @@ public class ScopedMemoryAccess {
// bulk ops // bulk ops
@ForceInline @ForceInline
public void copyMemory(MemorySessionImpl srcScope, MemorySessionImpl dstScope, public void copyMemory(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
Object srcBase, long srcOffset, Object srcBase, long srcOffset,
Object destBase, long destOffset, Object destBase, long destOffset,
long bytes) { long bytes) {
try { try {
copyMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes); copyMemoryInternal(srcSession, dstSession, srcBase, srcOffset, destBase, destOffset, bytes);
} catch (ScopedAccessError ex) { } catch (ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed"); throw ex.newRuntimeException();
} }
} }
@ForceInline @Scoped @ForceInline @Scoped
private void copyMemoryInternal(MemorySessionImpl srcScope, MemorySessionImpl dstScope, private void copyMemoryInternal(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
Object srcBase, long srcOffset, Object srcBase, long srcOffset,
Object destBase, long destOffset, Object destBase, long destOffset,
long bytes) { long bytes) {
try { try {
if (srcScope != null) { if (srcSession != null) {
srcScope.checkValidState(); srcSession.checkValidStateRaw();
} }
if (dstScope != null) { if (dstSession != null) {
dstScope.checkValidState(); dstSession.checkValidStateRaw();
} }
UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
} finally { } finally {
Reference.reachabilityFence(srcScope); Reference.reachabilityFence(srcSession);
Reference.reachabilityFence(dstScope); Reference.reachabilityFence(dstSession);
} }
} }
@ForceInline @ForceInline
public void copySwapMemory(MemorySessionImpl srcScope, MemorySessionImpl dstScope, public void copySwapMemory(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
Object srcBase, long srcOffset, Object srcBase, long srcOffset,
Object destBase, long destOffset, Object destBase, long destOffset,
long bytes, long elemSize) { long bytes, long elemSize) {
try { try {
copySwapMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes, elemSize); copySwapMemoryInternal(srcSession, dstSession, srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
} catch (ScopedAccessError ex) { } catch (ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed"); throw ex.newRuntimeException();
} }
} }
@ForceInline @Scoped @ForceInline @Scoped
private void copySwapMemoryInternal(MemorySessionImpl srcScope, MemorySessionImpl dstScope, private void copySwapMemoryInternal(MemorySessionImpl srcSession, MemorySessionImpl dstSession,
Object srcBase, long srcOffset, Object srcBase, long srcOffset,
Object destBase, long destOffset, Object destBase, long destOffset,
long bytes, long elemSize) { long bytes, long elemSize) {
try { try {
if (srcScope != null) { if (srcSession != null) {
srcScope.checkValidState(); srcSession.checkValidStateRaw();
} }
if (dstScope != null) { if (dstSession != null) {
dstScope.checkValidState(); dstSession.checkValidStateRaw();
} }
UNSAFE.copySwapMemory(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); UNSAFE.copySwapMemory(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
} finally { } finally {
Reference.reachabilityFence(srcScope); Reference.reachabilityFence(srcSession);
Reference.reachabilityFence(dstScope); Reference.reachabilityFence(dstSession);
} }
} }
@ -178,7 +187,7 @@ public class ScopedMemoryAccess {
try { try {
setMemoryInternal(session, o, offset, bytes, value); setMemoryInternal(session, o, offset, bytes, value);
} catch (ScopedAccessError ex) { } 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) { private void setMemoryInternal(MemorySessionImpl session, Object o, long offset, long bytes, byte value) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
UNSAFE.setMemory(o, offset, bytes, value); UNSAFE.setMemory(o, offset, bytes, value);
} finally { } finally {
@ -195,35 +204,35 @@ public class ScopedMemoryAccess {
} }
@ForceInline @ForceInline
public int vectorizedMismatch(MemorySessionImpl aScope, MemorySessionImpl bScope, public int vectorizedMismatch(MemorySessionImpl aSession, MemorySessionImpl bSession,
Object a, long aOffset, Object a, long aOffset,
Object b, long bOffset, Object b, long bOffset,
int length, int length,
int log2ArrayIndexScale) { int log2ArrayIndexScale) {
try { try {
return vectorizedMismatchInternal(aScope, bScope, a, aOffset, b, bOffset, length, log2ArrayIndexScale); return vectorizedMismatchInternal(aSession, bSession, a, aOffset, b, bOffset, length, log2ArrayIndexScale);
} catch (ScopedAccessError ex) { } catch (ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed"); throw ex.newRuntimeException();
} }
} }
@ForceInline @Scoped @ForceInline @Scoped
private int vectorizedMismatchInternal(MemorySessionImpl aScope, MemorySessionImpl bScope, private int vectorizedMismatchInternal(MemorySessionImpl aSession, MemorySessionImpl bSession,
Object a, long aOffset, Object a, long aOffset,
Object b, long bOffset, Object b, long bOffset,
int length, int length,
int log2ArrayIndexScale) { int log2ArrayIndexScale) {
try { try {
if (aScope != null) { if (aSession != null) {
aScope.checkValidState(); aSession.checkValidStateRaw();
} }
if (bScope != null) { if (bSession != null) {
bScope.checkValidState(); bSession.checkValidStateRaw();
} }
return ArraysSupport.vectorizedMismatch(a, aOffset, b, bOffset, length, log2ArrayIndexScale); return ArraysSupport.vectorizedMismatch(a, aOffset, b, bOffset, length, log2ArrayIndexScale);
} finally { } finally {
Reference.reachabilityFence(aScope); Reference.reachabilityFence(aSession);
Reference.reachabilityFence(bScope); Reference.reachabilityFence(bSession);
} }
} }
@ -232,7 +241,7 @@ public class ScopedMemoryAccess {
try { try {
return isLoadedInternal(session, address, isSync, size); return isLoadedInternal(session, address, isSync, size);
} catch (ScopedAccessError ex) { } 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) { public boolean isLoadedInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
return SharedSecrets.getJavaNioAccess().isLoaded(address, isSync, size); return SharedSecrets.getJavaNioAccess().isLoaded(address, isSync, size);
} finally { } finally {
@ -253,7 +262,7 @@ public class ScopedMemoryAccess {
try { try {
loadInternal(session, address, isSync, size); loadInternal(session, address, isSync, size);
} catch (ScopedAccessError ex) { } 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) { public void loadInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
SharedSecrets.getJavaNioAccess().load(address, isSync, size); SharedSecrets.getJavaNioAccess().load(address, isSync, size);
} finally { } finally {
@ -274,7 +283,7 @@ public class ScopedMemoryAccess {
try { try {
unloadInternal(session, address, isSync, size); unloadInternal(session, address, isSync, size);
} catch (ScopedAccessError ex) { } 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) { public void unloadInternal(MemorySessionImpl session, long address, boolean isSync, long size) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
SharedSecrets.getJavaNioAccess().unload(address, isSync, size); SharedSecrets.getJavaNioAccess().unload(address, isSync, size);
} finally { } finally {
@ -295,7 +304,7 @@ public class ScopedMemoryAccess {
try { try {
forceInternal(session, fd, address, isSync, index, length); forceInternal(session, fd, address, isSync, index, length);
} catch (ScopedAccessError ex) { } 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) { public void forceInternal(MemorySessionImpl session, FileDescriptor fd, long address, boolean isSync, long index, long length) {
try { try {
if (session != null) { if (session != null) {
session.checkValidState(); session.checkValidStateRaw();
} }
SharedSecrets.getJavaNioAccess().force(fd, address, isSync, index, length); SharedSecrets.getJavaNioAccess().force(fd, address, isSync, index, length);
} finally { } finally {
@ -333,7 +342,7 @@ public class ScopedMemoryAccess {
s, s,
defaultImpl); defaultImpl);
} catch (ScopedAccessError ex) { } catch (ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed"); throw ex.newRuntimeException();
} }
} }
@ -347,7 +356,7 @@ public class ScopedMemoryAccess {
S s, S s,
VectorSupport.LoadOperation<AbstractMemorySegmentImpl, V, S> defaultImpl) { VectorSupport.LoadOperation<AbstractMemorySegmentImpl, V, S> defaultImpl) {
try { try {
session.checkValidState(); session.checkValidStateRaw();
return VectorSupport.load(vmClass, e, length, return VectorSupport.load(vmClass, e, length,
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,
@ -378,7 +387,7 @@ public class ScopedMemoryAccess {
s, offsetInRange, s, offsetInRange,
defaultImpl); defaultImpl);
} catch (ScopedAccessError ex) { } 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, S s, int offsetInRange,
VectorSupport.LoadVectorMaskedOperation<AbstractMemorySegmentImpl, V, S, M> defaultImpl) { VectorSupport.LoadVectorMaskedOperation<AbstractMemorySegmentImpl, V, S, M> defaultImpl) {
try { try {
session.checkValidState(); session.checkValidStateRaw();
return VectorSupport.loadMasked(vmClass, maskClass, e, length, return VectorSupport.loadMasked(vmClass, maskClass, e, length,
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, m, offsetInRange, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, m, offsetInRange,
@ -424,7 +433,7 @@ public class ScopedMemoryAccess {
msp, offset, msp, offset,
defaultImpl); defaultImpl);
} catch (ScopedAccessError ex) { } 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, AbstractMemorySegmentImpl msp, long offset,
VectorSupport.StoreVectorOperation<AbstractMemorySegmentImpl, V> defaultImpl) { VectorSupport.StoreVectorOperation<AbstractMemorySegmentImpl, V> defaultImpl) {
try { try {
session.checkValidState(); session.checkValidStateRaw();
VectorSupport.store(vmClass, e, length, VectorSupport.store(vmClass, e, length,
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,
@ -470,7 +479,7 @@ public class ScopedMemoryAccess {
msp, offset, msp, offset,
defaultImpl); defaultImpl);
} catch (ScopedAccessError ex) { } 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, AbstractMemorySegmentImpl msp, long offset,
VectorSupport.StoreVectorMaskedOperation<AbstractMemorySegmentImpl, V, M> defaultImpl) { VectorSupport.StoreVectorMaskedOperation<AbstractMemorySegmentImpl, V, M> defaultImpl) {
try { try {
session.checkValidState(); session.checkValidStateRaw();
VectorSupport.storeMasked(vmClass, maskClass, e, length, VectorSupport.storeMasked(vmClass, maskClass, e, length,
msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset,

View file

@ -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 @Override
public MemorySegment map(MapMode mode, long offset, long size, public MemorySegment map(MapMode mode, long offset, long size,
MemorySession session) MemorySession session)
@ -1210,7 +1207,7 @@ public class FileChannelImpl
Objects.requireNonNull(mode,"Mode is null"); Objects.requireNonNull(mode,"Mode is null");
Objects.requireNonNull(session, "Session is null"); Objects.requireNonNull(session, "Session is null");
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session); MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(session);
sessionImpl.checkValidStateSlow(); sessionImpl.checkValidState();
if (offset < 0) if (offset < 0)
throw new IllegalArgumentException("Requested bytes offset must be >= 0."); throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
if (size < 0) if (size < 0)
@ -1219,14 +1216,14 @@ public class FileChannelImpl
boolean isSync = isSync(mode); boolean isSync = isSync(mode);
int prot = toProt(mode); int prot = toProt(mode);
Unmapper unmapper = mapInternal(mode, offset, size, prot, isSync); Unmapper unmapper = mapInternal(mode, offset, size, prot, isSync);
int modes = MAP_MEM_SEG_DEFAULT_MODES; boolean readOnly = false;
if (mode == MapMode.READ_ONLY) { if (mode == MapMode.READ_ONLY) {
modes |= MAP_MEM_SEG_READ_ONLY; readOnly = true;
} }
if (unmapper != null) { if (unmapper != null) {
AbstractMemorySegmentImpl segment = AbstractMemorySegmentImpl segment =
new MappedMemorySegmentImpl(unmapper.address(), unmapper, size, new MappedMemorySegmentImpl(unmapper.address(), unmapper, size,
modes, session); readOnly, session);
MemorySessionImpl.ResourceList.ResourceCleanup resource = MemorySessionImpl.ResourceList.ResourceCleanup resource =
new MemorySessionImpl.ResourceList.ResourceCleanup() { new MemorySessionImpl.ResourceList.ResourceCleanup() {
@Override @Override
@ -1237,7 +1234,7 @@ public class FileChannelImpl
sessionImpl.addOrCleanupIfFail(resource); sessionImpl.addOrCleanupIfFail(resource);
return segment; return segment;
} else { } else {
return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(modes, session); return new MappedMemorySegmentImpl.EmptyMappedMemorySegmentImpl(readOnly, sessionImpl);
} }
} }

View file

@ -1484,7 +1484,7 @@ public class HtmlDocletWriter {
} }
}; };
CommentHelper ch = utils.getCommentHelper(element); CommentHelper ch = utils.getCommentHelper(element);
configuration.tagletManager.checkTags(element, trees, true); configuration.tagletManager.checkTags(element, trees);
commentRemoved = false; commentRemoved = false;
for (ListIterator<? extends DocTree> iterator = trees.listIterator(); iterator.hasNext();) { for (ListIterator<? extends DocTree> iterator = trees.listIterator(); iterator.hasNext();) {

View file

@ -28,9 +28,8 @@ package jdk.javadoc.internal.doclets.toolkit.taglets;
import java.util.EnumSet; import java.util.EnumSet;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement; 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 com.sun.source.doctree.DocTree;
import jdk.javadoc.doclet.Taglet.Location; 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; import jdk.javadoc.internal.doclets.toolkit.util.Utils;
/** /**
* An inline taglet representing the {@code {@inheritDoc}} tag. * A taglet that represents the {@code {@inheritDoc}} tag.
* It is used to copy documentation from superclass (but not superinterface)
* declarations and from overridden and implemented methods.
*/ */
public class InheritDocTaglet extends BaseTaglet { public class InheritDocTaglet extends BaseTaglet {
@ -52,7 +49,7 @@ public class InheritDocTaglet extends BaseTaglet {
* Construct a new InheritDocTaglet. * Construct a new InheritDocTaglet.
*/ */
public 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); inheritedDoc.inlineTags, isFirstSentence);
} }
} else { } 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) + String signature = utils.getSimpleName(e) +
((utils.isExecutableElement(e)) ((utils.isExecutableElement(e))
? utils.flatSignature((ExecutableElement) e, writer.getCurrentPageElement()) ? utils.flatSignature((ExecutableElement) e, writer.getCurrentPageElement())
@ -115,6 +104,9 @@ public class InheritDocTaglet extends BaseTaglet {
@Override @Override
public Content getInlineTagOutput(Element e, DocTree inheritDoc, TagletWriter tagletWriter) { public Content getInlineTagOutput(Element e, DocTree inheritDoc, TagletWriter tagletWriter) {
if (e.getKind() != ElementKind.METHOD) {
return tagletWriter.getOutputInstance();
}
return retrieveInheritedDocumentation(tagletWriter, e, inheritDoc, tagletWriter.isFirstSentence); return retrieveInheritedDocumentation(tagletWriter, e, inheritDoc, tagletWriter.isFirstSentence);
} }
} }

View file

@ -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 element the tags holder
* @param trees the trees containing the comments * @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) { public void checkTags(Element element, Iterable<? extends DocTree> trees) {
if (trees == null) {
return;
}
CommentHelper ch = utils.getCommentHelper(element); CommentHelper ch = utils.getCommentHelper(element);
for (DocTree tag : trees) { for (DocTree tag : trees) {
String name = tag.getKind().tagName; String name = tag.getKind().tagName;
@ -386,16 +382,6 @@ public class TagletManager {
return; return;
} }
if (inlineTrees && !taglet.isInlineTag()) {
printTagMisuseWarn(ch, taglet, tag, "inline");
}
// nothing more to do
if (element == null) {
return;
}
if (!inlineTrees) {
new SimpleElementVisitor14<Void, Void>() { new SimpleElementVisitor14<Void, Void>() {
@Override @Override
public Void visitModule(ModuleElement e, Void p) { public Void visitModule(ModuleElement e, Void p) {
@ -455,7 +441,6 @@ public class TagletManager {
} }
} }
} }
}
/** /**
* Given the taglet, the tag and the type of documentation that the tag * Given the taglet, the tag and the type of documentation that the tag
@ -489,22 +474,13 @@ public class TagletManager {
if (taglet.inMethod()) { if (taglet.inMethod()) {
locationsSet.add("method"); locationsSet.add("method");
} }
if (taglet.isInlineTag()) {
locationsSet.add("inline text");
}
if (locationsSet.isEmpty()) { if (locationsSet.isEmpty()) {
//This known tag is excluded. //This known tag is excluded.
return; return;
} }
StringBuilder combined_locations = new StringBuilder(); var combined_locations = String.join(", ", locationsSet);
for (String location: locationsSet) {
if (combined_locations.length() > 0) {
combined_locations.append(", ");
}
combined_locations.append(location);
}
messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse", messages.warning(ch.getDocTreePath(tag), "doclet.tag_misuse",
"@" + taglet.getName(), holderType, combined_locations.toString()); "@" + taglet.getName(), holderType, combined_locations);
} }
/** /**

View file

@ -275,8 +275,8 @@ public abstract class TagletWriter {
Content output = getOutputInstance(); Content output = getOutputInstance();
Utils utils = configuration().utils; Utils utils = configuration().utils;
tagletManager.checkTags(element, utils.getBlockTags(element), false); tagletManager.checkTags(element, utils.getBlockTags(element));
tagletManager.checkTags(element, utils.getFullBody(element), true); tagletManager.checkTags(element, utils.getFullBody(element));
for (Taglet taglet : taglets) { for (Taglet taglet : taglets) {
if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) { if (utils.isTypeElement(element) && taglet instanceof ParamTaglet) {
// The type parameters and state components are documented in a special // The type parameters and state components are documented in a special

View file

@ -30,7 +30,6 @@ import java.util.*;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import com.sun.source.doctree.DocTree; import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
@ -258,17 +257,6 @@ public class DocFinder {
return output; 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; return output;
} }

View file

@ -193,7 +193,7 @@ public class Checker extends DocTreePathScanner<Void, Void> {
reportMissing("dc.missing.comment"); reportMissing("dc.missing.comment");
} }
return null; return null;
} else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod) { } else if (tree.getFirstSentence().isEmpty() && !isOverridingMethod && !pseudoElement(p)) {
if (tree.getBlockTags().isEmpty()) { if (tree.getBlockTags().isEmpty()) {
reportMissing("dc.empty.comment"); reportMissing("dc.empty.comment");
return null; return null;
@ -224,7 +224,7 @@ public class Checker extends DocTreePathScanner<Void, Void> {
// this is for html files // this is for html files
// ... if it is a legacy package.html, the doc comment comes after the <h1> page title // ... 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; case COMPILATION_UNIT -> fo.isNameCompatible("package", JavaFileObject.Kind.HTML) ? 1 : 0;
@ -292,6 +292,13 @@ public class Checker extends DocTreePathScanner<Void, Void> {
return true; 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) { private void reportMissing(String code, Object... args) {
env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args); env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args);
} }

View file

@ -53,7 +53,7 @@
public class framepop02 { 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 int NESTING_DEPTH = 20;
final static String TEST_THREAD_NAME_BASE = "Test Thread #"; final static String TEST_THREAD_NAME_BASE = "Test Thread #";

View file

@ -69,11 +69,15 @@ void print_current_time() {
} }
static static
int isTestThread(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr) { bool isTestThread(JNIEnv *jni, jvmtiEnv *jvmti, jthread thr) {
jvmtiThreadInfo inf; jvmtiThreadInfo inf;
const char* TEST_THREAD_NAME_BASE = "Test Thread"; const char* TEST_THREAD_NAME_BASE = "Test Thread";
check_jvmti_status(jni, jvmti->GetThreadInfo(thr, &inf), "Error in GetThreadInfo."); 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 static
@ -174,6 +178,10 @@ void JNICALL MethodEntry(jvmtiEnv *jvmti, JNIEnv *jni,
if (watch_events == JNI_FALSE) return; if (watch_events == JNI_FALSE) return;
if (!isTestThread(jni, jvmti, thr)) {
return; // not a tested thread
}
RawMonitorLocker rml(jvmti, jni, agent_lock); RawMonitorLocker rml(jvmti, jni, agent_lock);
if (!callbacksEnabled) { 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->GetFrameCount(thr, &frameCount), "Error in GetFrameCount");
check_jvmti_status(jni, jvmti->IsMethodNative(method, &isNative), "Error in IsMethodNative."); check_jvmti_status(jni, jvmti->IsMethodNative(method, &isNative), "Error in IsMethodNative.");
if (isTestThread(jni, jvmti, thr)) { {
if (printdump == JNI_TRUE) { if (printdump == JNI_TRUE) {
print_current_time(); print_current_time();
fflush(0); fflush(0);
@ -220,7 +228,7 @@ void JNICALL FramePop(jvmtiEnv *jvmti, JNIEnv *jni,
} }
check_jvmti_status(jni, jvmti->GetFrameCount(thr, &frameCount), "Error in GetFrameCount."); check_jvmti_status(jni, jvmti->GetFrameCount(thr, &frameCount), "Error in GetFrameCount.");
if (isTestThread(jni, jvmti, thr)) { {
if (printdump == JNI_TRUE) { if (printdump == JNI_TRUE) {
print_current_time(); print_current_time();
fflush(0); fflush(0);

View file

@ -373,7 +373,7 @@ public class TestByteBuffer {
Throwable cause = ex.getCause(); Throwable cause = ex.getCause();
if (cause instanceof IllegalStateException) { if (cause instanceof IllegalStateException) {
//all get/set buffer operation should fail because of the session check //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 { } else {
//all other exceptions were unexpected - fail //all other exceptions were unexpected - fail
fail("Unexpected exception", cause); fail("Unexpected exception", cause);
@ -410,7 +410,7 @@ public class TestByteBuffer {
handle.invoke(e.getValue()); handle.invoke(e.getValue());
fail(); fail();
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("already closed")); assertTrue(ex.getMessage().contains("Already closed"));
} catch (UnsupportedOperationException ex) { } catch (UnsupportedOperationException ex) {
//skip //skip
} catch (Throwable ex) { } catch (Throwable ex) {

View file

@ -370,8 +370,8 @@ public class TestMemorySession {
} }
private void keepAlive(MemorySession child, MemorySession parent) { private void keepAlive(MemorySession child, MemorySession parent) {
MemorySessionImpl sessionImpl = MemorySessionImpl.toSessionImpl(parent); MemorySessionImpl parentImpl = MemorySessionImpl.toSessionImpl(parent);
sessionImpl.acquire0(); parentImpl.acquire0();
child.addCloseAction(sessionImpl::release0); child.addCloseAction(parentImpl::release0);
} }
} }

View file

@ -53,6 +53,8 @@ import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -60,9 +62,14 @@ import java.util.stream.DoubleStream;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import static java.lang.foreign.MemoryLayout.PathElement.groupElement; 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_DOUBLE;
import static java.lang.foreign.ValueLayout.JAVA_FLOAT;
import static java.lang.foreign.ValueLayout.JAVA_INT; import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG; import static java.lang.foreign.ValueLayout.JAVA_LONG;
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
import static jdk.internal.foreign.PlatformLayouts.*; import static jdk.internal.foreign.PlatformLayouts.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -132,15 +139,15 @@ public class VaListTest extends NativeTestHelper {
= (builder) -> VaList.make(builder, MemorySession.openConfined()); = (builder) -> VaList.make(builder, MemorySession.openConfined());
private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> winVaListScopedFactory 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 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 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 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 private static final BiFunction<Consumer<VaList.Builder>, MemorySession, VaList> platformVaListScopedFactory
= (builder, session) -> VaList.make(builder, session); = VaList::make;
@DataProvider @DataProvider
@SuppressWarnings("unchecked") @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));
}
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8008768 * @bug 8008768 8287379
* @summary Using {@inheritDoc} in simple tag defined via -tag fails * @summary Using {@inheritDoc} in simple tag defined via -tag fails
* @library ../../lib * @library ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool * @modules jdk.javadoc/jdk.javadoc.internal.tool
@ -33,13 +33,6 @@
import javadoc.tester.JavadocTester; import javadoc.tester.JavadocTester;
/**
* DocTest documentation.
*
* @apiNote DocTest API note.
* @implSpec DocTest implementation spec.
* @implNote DocTest implementation note.
*/
public class DocTest extends JavadocTester { public class DocTest extends JavadocTester {
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
DocTest tester = new DocTest(); DocTest tester = new DocTest();
@ -51,9 +44,9 @@ public class DocTest extends JavadocTester {
javadoc("-verbose", javadoc("-verbose",
"-d", "DocTest", "-d", "DocTest",
"-sourcepath", System.getProperty("test.src.path"), "-sourcepath", System.getProperty("test.src.path"),
"-tag", "apiNote:optcm:<em>API Note</em>", "-tag", "apiNote:a:API Note",
"-tag", "implSpec:optcm:<em>Implementation Requirements</em>:", "-tag", "implSpec:a:Implementation Requirements:",
"-tag", "implNote:optcm:<em>Implementation Note</em>:", "-tag", "implNote:a:Implementation Note:",
"-package", "-package",
testSrc("DocTest.java") testSrc("DocTest.java")
); );
@ -64,16 +57,6 @@ public class DocTest extends JavadocTester {
checkOutput(Output.STDERR, false, "at com.sun"); 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. * 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 { 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. * DocTest.testMethod() documentation.
* *
@ -131,11 +79,11 @@ class DocTestWithTags {
* </pre> * </pre>
* @implSpec DocTestWithTags.testMethod() implementation spec. * @implSpec DocTestWithTags.testMethod() implementation spec.
* <pre> * <pre>
* DocTestWithTags.testMethod() API implementation spec code sample. * DocTestWithTags.testMethod() implementation spec code sample.
* </pre> * </pre>
* @implNote DocTest.testMethod() implementation note. * @implNote DocTest.testMethod() implementation note.
* <pre> * <pre>
* DocTest.testMethod() API implementation code sample. * DocTest.testMethod() implementation note code sample.
* </pre> * </pre>
*/ */
public void testMethod() { public void testMethod() {
@ -145,52 +93,26 @@ class DocTestWithTags {
class MinimallyExtendsDocTest extends DocTest { class MinimallyExtendsDocTest extends DocTest {
} }
/**
* SimpleExtendsDocTest documentation.
*/
class SimpleExtendsDocTest extends DocTest { class SimpleExtendsDocTest extends DocTest {
/** /**
* SimpleExtendsDocTest() documentation. * SimpleExtendsDocTest.testMethod() documentation.
*/ */
public SimpleExtendsDocTest() { @Override
}
/**
* SimpleExtendsDocTest.testMethod() documenation.
*/
@java.lang.Override
public void testMethod() { public void testMethod() {
} }
} }
/**
* {@inheritDoc}
*/
class SimpleInheritDocDocTest extends DocTest { class SimpleInheritDocDocTest extends DocTest {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public SimpleInheritDocDocTest() { @Override
}
/**
* {@inheritDoc}
*/
@java.lang.Override
public void testMethod() { public void testMethod() {
} }
} }
/**
* {@inheritDoc}
*
* @apiNote {@inheritDoc}
* @implSpec {@inheritDoc}
* @implNote {@inheritDoc}
*/
class FullInheritDocDocTest extends DocTest { class FullInheritDocDocTest extends DocTest {
/** /**
@ -200,50 +122,21 @@ class FullInheritDocDocTest extends DocTest {
* @implSpec {@inheritDoc} * @implSpec {@inheritDoc}
* @implNote {@inheritDoc} * @implNote {@inheritDoc}
*/ */
public FullInheritDocDocTest() { @Override
}
/**
* {@inheritDoc}
*
* @apiNote {@inheritDoc}
* @implSpec {@inheritDoc}
* @implNote {@inheritDoc}
*/
@java.lang.Override
public void testMethod() { 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 { 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. * {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() documentation.
* *
* @implSpec {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() API note. * @implSpec {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation specification.
* @implNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation specification. * @implNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation note.
* @apiNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() implementation note. * @apiNote {@inheritDoc} and FullInheritDocPlusDocTest.testMethod() API note.
*/ */
@java.lang.Override @Override
public void testMethod() { public void testMethod() {
} }
} }

View file

@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8284299 * @bug 8284299 8287379
* @library /tools/lib ../../lib * @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool * @modules jdk.javadoc/jdk.javadoc.internal.tool
* @build toolbox.ToolBox javadoc.tester.* * @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}
^
""");
}
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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>"""); <a href="C.html#class-fragment">fragment class link</a>""");
// subclass in same pacakge // subclass in same package
checkOutput("pkg/D.html", true, 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>""", <a href="relative-method-link.html">relative method link</a>""",
""" """
@ -102,10 +98,6 @@ public class TestRelativeLinks extends JavadocTester {
// subclass in subpackage // subclass in subpackage
checkOutput("pkg/sub/F.html", true, 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>""", <a href="../../pkg/relative-method-link.html">relative method link</a>""",
""" """
@ -149,10 +141,6 @@ public class TestRelativeLinks extends JavadocTester {
// CLASS_USE // CLASS_USE
checkOutput("pkg/class-use/C.html", true, 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>""", <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/relative-package-link.html">relative package link</a>""",
""" """
<a href="../../pkg/package-summary.html#package-fragment">package fragment 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>""");
} }
@Override @Override

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -24,8 +24,6 @@
package pkg; package pkg;
/** /**
* {@inheritDoc}
*
* A class that extends C and inherits some of its comments. * A class that extends C and inherits some of its comments.
*/ */
public class D extends C { public class D extends C {

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,8 +26,6 @@ package pkg.sub;
import pkg.C; import pkg.C;
/** /**
* {@inheritDoc}
*
* A class that extends C and inherits some of its comments. * A class that extends C and inherits some of its comments.
*/ */
public class F extends C { public class F extends C {

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,9 +26,7 @@ package pkg2;
import pkg.C; import pkg.C;
/** /**
* {@inheritDoc} * A class that extends pkg.C from another package and inherits some of its comments.
*
* A class that extends pkg.C from onother package and inherits some of its comments.
*/ */
public class E extends C { public class E extends C {

View file

@ -44,14 +44,11 @@ public class TestSimpleTagInherit extends JavadocTester {
public void test() { public void test() {
javadoc("-d", "out", javadoc("-d", "out",
"-sourcepath", testSrc, "-sourcepath", testSrc,
"-tag", "custom:optcm:<em>Custom:</em>", "-tag", "custom:m:<em>Custom:</em>",
"p"); "p");
checkExit(Exit.OK); checkExit(Exit.OK);
checkOutput("p/TestClass.html", true, checkOutput("p/TestClass.html", true,
"""
<dt><em>Custom:</em></dt>
<dd>doc for BaseClass class</dd>""",
""" """
<dt><em>Custom:</em></dt> <dt><em>Custom:</em></dt>
<dd>doc for BaseClass method</dd>"""); <dd>doc for BaseClass method</dd>""");

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,9 +23,6 @@
package p; package p;
/**
* @custom doc for BaseClass class
*/
public class BaseClass { public class BaseClass {
/** /**
* @custom doc for BaseClass method * @custom doc for BaseClass method

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,9 +23,6 @@
package p; package p;
/**
* @custom {@inheritDoc}
*/
public class TestClass extends BaseClass { public class TestClass extends BaseClass {
/** /**
* @custom {@inheritDoc} * @custom {@inheritDoc}

View file

@ -7,7 +7,7 @@
@factory: block ........ ...... ....... .... ........... method ..... ...... ........ @factory: block ........ ...... ....... .... ........... method ..... ...... ........
@hidden: block ........ ...... ....... type ........... method field ...... ........ @hidden: block ........ ...... ....... type ........... method field ...... ........
{@index}: ..... overview module package type constructor method field inline ........ {@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 ........ {@link}: ..... overview module package type constructor method field inline ........
{@linkplain}: ..... 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 ........ {@literal}: ..... overview module package type constructor method field inline ........