public sealed interface VaList
VaList 是 Java 平台的预览 API。
预览功能可能会在未来的版本中删除,或升级为 Java 平台的永久功能。
用于创建和操作变量参数列表的帮助程序类,在功能上类似于 C
创建后,客户端可以获得与可变参数列表关联的平台相关的内存段,然后可以将其传递给向下调用方法句柄PREVIEW 使用 C
上面的方法接收一个外部段,建模一个可变参数列表;通过从段地址创建一个新的变量参数列表来访问段的内容。请注意,在访问任何元素之前,可变参数列表首先被复制到第二个列表中:这将允许我们遍历元素两次。使用
va_list。
可以使用 make(Consumer, SegmentScope) 工厂创建可变参数列表,如下所示:
VaList vaList = VaList.make(builder ->
builder.addVarg(C_INT, 42)
.addVarg(C_DOUBLE, 3.8d));
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 项),并删除所有 float 到 double 的参数。
因此,此接口仅支持读取 int 、 double 和适合 long 的任何其他类型。
安全考虑
使用错误的内存布局通过可变参数列表访问值将导致未定义的行为。例如,如果可变参数列表当前指向 Cint 值,则使用 nextVarg(ValueLayout.OfLong) 访问它是非法的。同样,使用 skip(MemoryLayout...) 访问可变参数列表,并提供 ValueLayout.OfInt 以外的布局PREVIEW 是非法的。任何此类非法访问都可能无法被实现检测到,并且可能会破坏变量参数列表,因此后续访问的行为也是未定义的。
客户端可以访问可变参数列表空间边界之外的元素。可变参数列表实现将尝试尽最大努力检测越界读取。
此检测是否成功取决于用于创建可变参数列表的工厂方法:
- 使用
make(Consumer, SegmentScope)创建的变量参数列表 safely 能够检测越界读取; - 使用
ofAddress(long, SegmentScope)创建的可变参数列表 unsafely 无法检测越界读取
此类不是线程安全的,所有访问都应在单个线程内发生(无论用于获取变量列表的范围如何)。
- 自从:
- 19
-
内部类总结
内部类 -
方法总结
修饰符和类型方法描述copy()将此可变参数列表的当前位置复制到与此可变参数列表具有相同作用域的新可变参数列表中。empty()make(Consumer<VaList.BuilderPREVIEW> actions, SegmentScopePREVIEW scope) 使用构建器创建可变参数列表(参见VaList.BuilderPREVIEW ), 具有给定的范围。nextVarg(GroupLayoutPREVIEW layout, SegmentAllocatorPREVIEW allocator) 将下一个复合值读入新的MemorySegment,使用提供的分配器分配,并推进此可变参数列表的位置。nextVarg(ValueLayout.OfAddressPREVIEW layout) 读取下一个地址值,将其包装到本机段中,并推进此可变参数列表的位置。doublenextVarg(ValueLayout.OfDoublePREVIEW layout) 读取下一个值作为double并推进此可变参数列表的位置。intnextVarg(ValueLayout.OfIntPREVIEW layout) 读取下一个值作为int并推进此可变参数列表的位置。longnextVarg(ValueLayout.OfLongPREVIEW layout) 读取下一个值作为long并推进此可变参数列表的位置。ofAddress(long address, SegmentScopePREVIEW scope) 根据给定的地址值和范围创建可变参数列表。segment()voidskip(MemoryLayoutPREVIEW... layouts) 跳过具有给定内存布局的多个元素,并推进此可变参数列表的位置。
-
方法详情
-
nextVarg
读取下一个值作为int并推进此可变参数列表的位置。此方法的行为等效于 Cva_arg函数。- 参数:
layout- 要读取的值的布局。- 返回:
-
从此变量参数列表中读取的
int值。 - 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。NoSuchElementException- 如果检测到 界外 读取。
-
nextVarg
读取下一个值作为long并推进此可变参数列表的位置。此方法的行为等效于 Cva_arg函数。- 参数:
layout- 要读取的值的布局。- 返回:
-
从此变量参数列表中读取的
long值。 - 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。NoSuchElementException- 如果检测到 界外 读取。
-
nextVarg
读取下一个值作为double并推进此可变参数列表的位置。此方法的行为等效于 Cva_arg函数。- 参数:
layout- 值的布局- 返回:
-
从此变量参数列表中读取的
double值。 - 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。NoSuchElementException- 如果检测到 界外 读取。
-
nextVarg
读取下一个地址值,将其包装到本机段中,并推进此可变参数列表的位置。此方法的行为等效于 Cva_arg函数。返回段的基数 MemorySegment.address()PREVIEW 设置为从可变参数列表中读取的值,并且该段与全球范围相关联PREVIEW .在正常情况下,返回段的大小为0。但是,如果提供的布局是 无界PREVIEW 地址布局,则返回段的大小为Long.MAX_VALUE。- 参数:
layout- 要读取的值的布局。- 返回:
- 一个原生段,其 addressPREVIEW 是从此变量参数列表中读取的值。
- 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。NoSuchElementException- 如果检测到 界外 读取。
-
nextVarg
将下一个复合值读入新的MemorySegment,使用提供的分配器分配,并推进此可变参数列表的位置。此方法的行为等效于 Cva_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
跳过具有给定内存布局的多个元素,并推进此可变参数列表的位置。- 参数:
layouts- 要跳过的值的布局。- 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。NoSuchElementException- 如果检测到 界外 读取。
-
copy
将此可变参数列表的当前位置复制到与此可变参数列表具有相同作用域的新可变参数列表中。此方法的行为等效于 Cva_copy函数。复制用于遍历可变参数列表元素,从当前位置开始,不影响原始可变参数列表的状态,本质上允许元素被遍历多次。
- 返回:
- 此可变参数列表的副本。
- 抛出:
IllegalStateException- 如果与此变量参数列表关联的范围不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则segment().scope().isAccessibleBy(T) == false。
-
segment
MemorySegment PREVIEW segment() -
ofAddress
根据给定的地址值和范围创建可变参数列表。地址通常通过调用MemorySegment.address()获得PREVIEW 在外部内存段实例上。提供的范围决定了返回的变量参数列表的生命周期:返回的变量参数列表将不再可访问,并且当范围变为 not 活 时,其关联的堆外内存区域将被释放PREVIEW .这个方法是restricted 。受限方法是不安全的,如果使用不当,它们的使用可能会使 JVM 崩溃,或者更糟的是,无声地导致内存损坏。因此,客户应避免依赖受限的方法,并尽可能使用安全和受支持的功能。
- 参数:
address- 可变参数列表的地址。scope- 与返回的变量参数列表关联的范围。- 返回:
- 由从给定地址值开始的堆外内存区域支持的新变量参数列表。
- 抛出:
IllegalStateException- 如果scope不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则scope.isAccessibleBy(T) == false。UnsupportedOperationException- 如果不支持底层原生平台。IllegalCallerException- 如果调用者所在的模块未启用本机访问。
-
make
使用构建器创建可变参数列表(参见VaList.BuilderPREVIEW ), 具有给定的范围。提供的范围决定了返回的变量参数列表的生命周期:返回的变量参数列表将不再可访问,并且当范围变为 not 活 时,其关联的堆外内存区域将被释放PREVIEW .请注意,当没有元素添加到创建的 va 列表时,此方法将返回与
empty()相同的结果。- 实现注意事项:
- 使用此方法创建的可变参数列表可以检测到 界外 读取。
- 参数:
actions- 构建器的消费者(参见VaList.BuilderPREVIEW ) 可用于指定基础变量参数列表的元素。scope- 与新变量列表关联的范围。- 返回:
- 一个新的可变参数列表。
- 抛出:
UnsupportedOperationException- 如果不支持底层原生平台。IllegalStateException- 如果scope不是 活PREVIEW .WrongThreadException- 如果从线程T调用此方法,则scope.isAccessibleBy(T) == false。
-
empty
返回与 全球范围 关联的空变量参数列表PREVIEW .生成的变量参数列表不包含任何参数,并在所有操作上抛出UnsupportedOperationException,除了segment()、copy()。- 返回:
- 一个空的变量参数列表。
- 抛出:
UnsupportedOperationException- 如果不支持底层原生平台。
-
VaList。