Module java.base
Package java.lang.foreign

# Interface VaList

public sealed interface VaList
`VaList` is a preview API of the Java platform.
Programs can only use `VaList` when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
Helper class to create and manipulate variable argument lists, similar in functionality to a C `va_list`.

A variable argument list can be created using the `make(Consumer, SegmentScope)` factory, as follows:

     VaList vaList = VaList.make(builder ->
                                        builder.addVarg(C_INT, 42)
                                               .addVarg(C_DOUBLE, 3.8d));

Once created, clients can obtain the platform-dependent memory segment associated with a variable argument list, which can then be passed to downcall method handlesPREVIEW targeting native functions using the C `va_list` type.

The contents of a foreign memory segment modelling a variable argument list can be accessed by unsafely creating a variable argument list, as follows:

     void upcall(int n, MemorySegment vaListSegment) {
        try (Arena arena = Arena.openConfined()) {
            VaList vaList = VaList.ofAddress(vaListSegment.address(), arena.scope());
            VaList copy = vaList.copy();
            int i = vaList.nextVarg(C_INT);
            double d = vaList.nextVarg(C_DOUBLE);
            // and again
            int i = copy.nextVarg(C_INT);
            double d = copy.nextVarg(C_DOUBLE);
         }
     }

The above method receives a foreign segment modelling a variable argument list; the contents of the segment are accessed by creating a new variable argument list, from the segment address. Note that the variable argument list is first copied into a second list before any element is accessed: this will allow us to iterate through the elements twice. Elements in the variable argument list are accessed using `nextVarg(ValueLayout.OfInt)` and `nextVarg(ValueLayout.OfDouble)`. These methods (as well as other access methods in the `VaList`PREVIEW class) take the layout of the element that needs to be accessed and perform all the necessary alignment checks as well as endianness conversions.

Per the C specification (see C99 standard 6.5.2.2 Function calls - item 6), arguments to variadic calls are erased by way of 'default argument promotions', which erases integral types by way of integer promotion (see C99 standard 6.3.1.1 - item 2), and which erases all `float` arguments to `double`.

As such, this interface only supports reading `int`, `double`, and any other type that fits into a `long`.

## Safety considerations

Accessing a value through a variable argument list using the wrong memory layout will result in undefined behavior. For instance, if a variable argument list currently points at a C `int` value, then accessing it using `nextVarg(ValueLayout.OfLong)` is illegal. Similarly, accessing the variable argument list with `skip(MemoryLayout...)`, and providing a layout other than `ValueLayout.OfInt`PREVIEW is illegal. Any such illegal accesses might not be detected by the implementation, and can corrupt the variable argument list, so that the behavior of subsequent accesses is also undefined.

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.

Whether this detection succeeds depends on the factory method used to create the variable argument list:

- Variable argument lists created safely, using `make(Consumer, SegmentScope)` are capable of detecting out-of-bounds reads;
- Variable argument lists created unsafely, using `ofAddress(long, SegmentScope)` are not capable of detecting out-of-bounds reads

This class is not thread safe, and all accesses should occur within a single thread (regardless of the scope used to obtain the variable arity list).

Since:
  19

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Interface
  Description
  `static interface`
  `VaList.BuilderPREVIEW`
  Preview.
  A builder used to construct a variable argument listPREVIEW.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `VaListPREVIEW`
  `copy()`
  Copies this variable argument list at its current position into a new variable argument list associated with the same scope as this variable argument list.
  `static VaListPREVIEW`
  `empty()`
  Returns an empty variable argument list, associated with the global scopePREVIEW.
  `static VaListPREVIEW`
  `make(Consumer<VaList.BuilderPREVIEW> actions, SegmentScopePREVIEW scope)`
  Creates a variable argument list using a builder (see `VaList.Builder`PREVIEW), with the given scope.
  `MemorySegmentPREVIEW`
  `nextVarg(GroupLayoutPREVIEW layout, SegmentAllocatorPREVIEW allocator)`
  Reads the next composite value into a new `MemorySegment`, allocated with the provided allocator, and advances this variable argument list's position.
  `MemorySegmentPREVIEW`
  `nextVarg(ValueLayout.OfAddressPREVIEW layout)`
  Reads the next address value, wraps it into a native segment, and advances this variable argument list's position.
  `double`
  `nextVarg(ValueLayout.OfDoublePREVIEW layout)`
  Reads the next value as a `double` and advances this variable argument list's position.
  `int`
  `nextVarg(ValueLayout.OfIntPREVIEW layout)`
  Reads the next value as an `int` and advances this variable argument list's position.
  `long`
  `nextVarg(ValueLayout.OfLongPREVIEW layout)`
  Reads the next value as a `long` and advances this variable argument list's position.
  `static VaListPREVIEW`
  `ofAddress(long address, SegmentScopePREVIEW scope)`
  Creates a variable argument list from the give address value and scope.
  `MemorySegmentPREVIEW`
  `segment()`
  Returns a zero-length memory segmentPREVIEW associated with this variable argument list.
  `void`
  `skip(MemoryLayoutPREVIEW... layouts)`
  Skips a number of elements with the given memory layouts, and advances this variable argument list's position.

- ## Method Details

  - ### nextVarg

    int nextVarg(ValueLayout.OfIntPREVIEW layout)
    Reads the next value as an `int` and advances this variable argument list's position. The behavior of this method is equivalent to the C `va_arg` function.
    Parameters:
      `layout` - the layout of the value to be read.
    Returns:
      the `int` value read from this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### nextVarg

    long nextVarg(ValueLayout.OfLongPREVIEW layout)
    Reads the next value as a `long` and advances this variable argument list's position. The behavior of this method is equivalent to the C `va_arg` function.
    Parameters:
      `layout` - the layout of the value to be read.
    Returns:
      the `long` value read from this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### nextVarg

    double nextVarg(ValueLayout.OfDoublePREVIEW layout)
    Reads the next value as a `double` and advances this variable argument list's position. The behavior of this method is equivalent to the C `va_arg` function.
    Parameters:
      `layout` - the layout of the value
    Returns:
      the `double` value read from this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### nextVarg

    MemorySegmentPREVIEW nextVarg(ValueLayout.OfAddressPREVIEW layout)
    Reads the next address value, wraps it into a native segment, and advances this variable argument list's position. The behavior of this method is equivalent to the C `va_arg` function. The returned segment's base MemorySegment.address()PREVIEW is set to the value read from the variable argument list, and the segment is associated with the global scopePREVIEW. Under normal conditions, the size of the returned segment is `0`. However, if the provided layout is an unboundedPREVIEW address layout, then the size of the returned segment is `Long.MAX_VALUE`.
    Parameters:
      `layout` - the layout of the value to be read.
    Returns:
      a native segment whose addressPREVIEW is the value read from this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### nextVarg

    MemorySegmentPREVIEW nextVarg(GroupLayoutPREVIEW layout, SegmentAllocatorPREVIEW allocator)
    Reads the next composite value into a new `MemorySegment`, allocated with the provided allocator, and advances this variable argument list's position. The behavior of this method is equivalent to the C `va_arg` function. The provided group layout must correspond to a C struct or union type.

    How the value is read in the returned segment is ABI-dependent: calling this method on a group layout with member layouts `L_1, L_2, ... L_n` is not guaranteed to be semantically equivalent to perform distinct calls to `nextVarg` for each of the layouts in `L_1, L_2, ... L_n`.

    The memory segment returned by this method will be allocated using the given `SegmentAllocator`PREVIEW.

    Parameters:
      `layout` - the layout of the value to be read.
      `allocator` - the allocator to be used to create a segment where the contents of the variable argument list will be copied.
    Returns:
      the `MemorySegment` value read from this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### skip

    void skip(MemoryLayoutPREVIEW... layouts)
    Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
    Parameters:
      `layouts` - the layouts of the values to be skipped.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.
      `NoSuchElementException` - if an out-of-bounds read is detected.

  - ### copy

    VaListPREVIEW copy()
    Copies this variable argument list at its current position into a new variable argument list associated with the same scope as this variable argument list. The behavior of this method is equivalent to the C `va_copy` function.

    Copying is useful to traverse the variable argument list elements, starting from the current position, without affecting the state of the original variable argument list, essentially allowing the elements to be traversed multiple times.

    Returns:
      a copy of this variable argument list.
    Throws:
      `IllegalStateException` - if the scope associated with this variable argument list is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `segment().scope().isAccessibleBy(T) == false`.

  - ### segment

    MemorySegmentPREVIEW segment()
    Returns a zero-length memory segmentPREVIEW associated with this variable argument list. The contents of the returned memory segment are platform-dependent. Whether and how the contents of the returned segment are updated when iterating the contents of a variable argument list is also platform-dependent.
    Returns:
      a zero-length memory segmentPREVIEW associated with this variable argument list.

  - ### ofAddress

    static VaListPREVIEW ofAddress(long address, SegmentScopePREVIEW scope)
    Creates a variable argument list from the give address value and scope. The address is typically obtained by calling `MemorySegment.address()`PREVIEW on a foreign memory segment instance. The provided scope determines the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible, and its associated off-heap memory region will be deallocated when the scope becomes not alivePREVIEW.

    This method is restricted. Restricted methods are unsafe, and, if used incorrectly, their use might crash 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.

    Parameters:
      `address` - the address of the variable argument list.
      `scope` - the scope associated with the returned variable argument list.
    Returns:
      a new variable argument list backed by an off-heap region of memory starting at the given address value.
    Throws:
      `IllegalStateException` - if `scope` is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `scope.isAccessibleBy(T) == false`.
      `UnsupportedOperationException` - if the underlying native platform is not supported.
      `IllegalCallerException` - If the caller is in a module that does not have native access enabled.

  - ### make

    static VaListPREVIEW make(Consumer<VaList.BuilderPREVIEW> actions, SegmentScopePREVIEW scope)
    Creates a variable argument list using a builder (see `VaList.Builder`PREVIEW), with the given scope. The provided scope determines the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible, and its associated off-heap memory region will be deallocated when the scope becomes not alivePREVIEW.

    Note that when there are no elements added to the created va list, this method will return the same as `empty()`.

    Implementation Note:
      variable argument lists created using this method can detect out-of-bounds reads.
    Parameters:
      `actions` - a consumer for a builder (see `VaList.Builder`PREVIEW) which can be used to specify the elements of the underlying variable argument list.
      `scope` - the scope to be associated with the new variable arity list.
    Returns:
      a new variable argument list.
    Throws:
      `UnsupportedOperationException` - if the underlying native platform is not supported.
      `IllegalStateException` - if `scope` is not alivePREVIEW.
      `WrongThreadException` - if this method is called from a thread `T`, such that `scope.isAccessibleBy(T) == false`.

  - ### empty

    static VaListPREVIEW empty()
    Returns an empty variable argument list, associated with the global scopePREVIEW. The resulting variable argument list does not contain any argument, and throws `UnsupportedOperationException` on all operations, except for `segment()`, `copy()`.
    Returns:
      an empty variable argument list.
    Throws:
      `UnsupportedOperationException` - if the underlying native platform is not supported.