模块 java.base

接口 VaList


public sealed interface VaList
VaList 是 Java 平台的预览 API。
程序只能在启用预览功能时使用 VaList
预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。
用于创建和操作变量参数列表的帮助程序类,在功能上类似于 C va_list

可以使用 make(Consumer, SegmentScope) 工厂创建可变参数列表,如下所示:

 VaList vaList = VaList.make(builder ->
                  builder.addVarg(C_INT, 42)
                      .addVarg(C_DOUBLE, 3.8d));
 
创建后,客户端可以获得与可变参数列表关联的平台相关的内存段,然后可以将其传递给向下调用方法句柄PREVIEW 使用 C va_list 类型定位本机函数。

可以通过unsafely创建可变参数列表来访问建模可变参数列表的外部内存段的内容,如下所示:

 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);
   }
 }
 
上面的方法接收一个外部段,建模一个可变参数列表;通过从段地址创建一个新的变量参数列表来访问段的内容。请注意,在访问任何元素之前,可变参数列表首先被复制到第二个列表中:这将允许我们遍历元素两次。使用 nextVarg(ValueLayout.OfInt) nextVarg(ValueLayout.OfDouble) 访问可变参数列表中的元素。这些方法(以及 VaList 中的其他访问方法PREVIEW class) 采用需要访问的元素的布局并执行所有必要的对齐检查以及字节顺序转换。

根据 C 规范(参见 C99 标准 6.5.2.2 函数调用 - 第 6 项),可变参数调用的参数通过“默认参数提升”擦除,它通过整数提升擦除整数类型(参见 C99 标准 6.3.1.1 -第 2 项),并删除所有 floatdouble 的参数。

因此,此接口仅支持读取 intdouble 和适合 long 的任何其他类型。

安全考虑

使用错误的内存布局通过可变参数列表访问值将导致未定义的行为。例如,如果可变参数列表当前指向 C int 值,则使用 nextVarg(ValueLayout.OfLong) 访问它是非法的。同样,使用 skip(MemoryLayout...) 访问可变参数列表,并提供 ValueLayout.OfInt 以外的布局PREVIEW 是非法的。任何此类非法访问都可能无法被实现检测到,并且可能会破坏变量参数列表,因此后续访问的行为也是未定义的。

客户端可以访问可变参数列表空间边界之外的元素。可变参数列表实现将尝试尽最大努力检测越界读取。

此检测是否成功取决于用于创建可变参数列表的工厂方法:

此类不是线程安全的,所有访问都应在单个线程内发生(无论用于获取变量列表的范围如何)。

自从:
19
  • 方法详情

    • nextVarg

      int nextVarg(ValueLayout.OfInt PREVIEW  layout)
      读取下一个值作为 int 并推进此可变参数列表的位置。此方法的行为等效于 C va_arg 函数。
      参数:
      layout - 要读取的值的布局。
      返回:
      从此变量参数列表中读取的 int 值。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • nextVarg

      long nextVarg(ValueLayout.OfLong PREVIEW  layout)
      读取下一个值作为 long 并推进此可变参数列表的位置。此方法的行为等效于 C va_arg 函数。
      参数:
      layout - 要读取的值的布局。
      返回:
      从此变量参数列表中读取的 long 值。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • nextVarg

      double nextVarg(ValueLayout.OfDouble PREVIEW  layout)
      读取下一个值作为 double 并推进此可变参数列表的位置。此方法的行为等效于 C va_arg 函数。
      参数:
      layout - 值的布局
      返回:
      从此变量参数列表中读取的 double 值。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • nextVarg

      读取下一个地址值,将其包装到本机段中,并推进此可变参数列表的位置。此方法的行为等效于 C va_arg 函数。返回段的基数 MemorySegment.address()PREVIEW 设置为从可变参数列表中读取的值,并且该段与全球范围相关联PREVIEW .在正常情况下,返回段的大小为 0 。但是,如果提供的布局是 无界PREVIEW 地址布局,则返回段的大小为 Long.MAX_VALUE
      参数:
      layout - 要读取的值的布局。
      返回:
      一个原生段,其 addressPREVIEW 是从此变量参数列表中读取的值。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • nextVarg

      将下一个复合值读入新的 MemorySegment ,使用提供的分配器分配,并推进此可变参数列表的位置。此方法的行为等效于 C va_arg 函数。提供的组布局必须对应于 C 结构或联合类型。

      如何在返回的段中读取值取决于 ABI:在具有成员布局 L_1, L_2, ... L_n 的组布局上调用此方法不能保证在语义上等同于对 L_1, L_2, ... L_n 中的每个布局执行对 nextVarg 的不同调用。

      此方法返回的内存段将使用给定的 SegmentAllocator 进行分配PREVIEW .

      参数:
      layout - 要读取的值的布局。
      allocator - 用于创建段的分配器,变量参数列表的内容将被复制到该段中。
      返回:
      从此变量参数列表中读取的 MemorySegment 值。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • skip

      void skip(MemoryLayout PREVIEW ... layouts)
      跳过具有给定内存布局的多个元素,并推进此可变参数列表的位置。
      参数:
      layouts - 要跳过的值的布局。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
      NoSuchElementException - 如果检测到 界外 读取。
    • copy

      VaList PREVIEW  copy()
      将此可变参数列表的当前位置复制到与此可变参数列表具有相同作用域的新可变参数列表中。此方法的行为等效于 C va_copy 函数。

      复制用于遍历可变参数列表元素,从当前位置开始,不影响原始可变参数列表的状态,本质上允许元素被遍历多次。

      返回:
      此可变参数列表的副本。
      抛出:
      IllegalStateException - 如果与此变量参数列表关联的范围不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 segment().scope().isAccessibleBy(T) == false
    • segment

      MemorySegment PREVIEW  segment()
      返回零长度 内存段PREVIEW 与此可变参数列表相关联。返回的内存段的内容是平台相关的。在迭代可变参数列表的内容时,是否以及如何更新返回段的内容也取决于平台。
      返回:
      零长度 内存段PREVIEW 与此可变参数列表相关联。
    • ofAddress

      static VaList PREVIEW  ofAddress(long address, SegmentScope PREVIEW  scope)
      根据给定的地址值和范围创建可变参数列表。地址通常通过调用MemorySegment.address() 获得PREVIEW 在外部内存段实例上。提供的范围决定了返回的变量参数列表的生命周期:返回的变量参数列表将不再可访问,并且当范围变为 not 时,其关联的堆外内存区域将被释放PREVIEW .

      这个方法是restricted 。受限方法是不安全的,如果使用不当,它们的使用可能会使 JVM 崩溃,或者更糟的是,无声地导致内存损坏。因此,客户应避免依赖受限的方法,并尽可能使用安全和受支持的功能。

      参数:
      address - 可变参数列表的地址。
      scope - 与返回的变量参数列表关联的范围。
      返回:
      由从给定地址值开始的堆外内存区域支持的新变量参数列表。
      抛出:
      IllegalStateException - 如果 scope 不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 scope.isAccessibleBy(T) == false
      UnsupportedOperationException - 如果不支持底层原生平台。
      IllegalCallerException - 如果调用者所在的模块未启用本机访问。
    • make

      static VaList PREVIEW  make(Consumer <VaList.Builder PREVIEW > actions, SegmentScope PREVIEW  scope)
      使用构建器创建可变参数列表(参见 VaList.Builder PREVIEW ), 具有给定的范围。提供的范围决定了返回的变量参数列表的生命周期:返回的变量参数列表将不再可访问,并且当范围变为 not 时,其关联的堆外内存区域将被释放PREVIEW .

      请注意,当没有元素添加到创建的 va 列表时,此方法将返回与 empty() 相同的结果。

      实现注意事项:
      使用此方法创建的可变参数列表可以检测到 界外 读取。
      参数:
      actions - 构建器的消费者(参见 VaList.Builder PREVIEW ) 可用于指定基础变量参数列表的元素。
      scope - 与新变量列表关联的范围。
      返回:
      一个新的可变参数列表。
      抛出:
      UnsupportedOperationException - 如果不支持底层原生平台。
      IllegalStateException - 如果 scope 不是 PREVIEW .
      WrongThreadException - 如果从线程 T 调用此方法,则 scope.isAccessibleBy(T) == false
    • empty

      static VaList PREVIEW  empty()
      返回与 全球范围 关联的空变量参数列表PREVIEW .生成的变量参数列表不包含任何参数,并在所有操作上抛出 UnsupportedOperationException ,除了 segment() copy()
      返回:
      一个空的变量参数列表。
      抛出:
      UnsupportedOperationException - 如果不支持底层原生平台。