Skip to content
Permalink
Browse files

8247678: StdLibTest fails to create an empty VaList on Windows

Reviewed-by: mcimadamore
  • Loading branch information
JornVernee committed Jun 16, 2020
1 parent 5c7c216 commit 2ed13bf9313018510b444b8a92545822871a61fe
@@ -175,6 +175,9 @@ static VaList ofAddress(MemoryAddress address) {
/**
* Constructs a new {@code VaList} using a builder (see {@link Builder}).
*
* Note that when there are no arguments added to the created va list,
* this method will return the same as {@linkplain #empty()}.
*
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the contents
* of the underlying C {@code va_list}.
* @return a new {@code VaList} instance backed by a fresh C {@code va_list}.
@@ -183,6 +186,17 @@ static VaList make(Consumer<VaList.Builder> actions) {
return SharedUtils.newVaList(actions);
}

/**
* Returns an empty C {@code va_list} constant.
*
* The returned {@code VaList} can not be closed.
*
* @return a {@code VaList} modelling an empty C {@code va_list}.
*/
static VaList empty() {
return SharedUtils.emptyVaList();
}

/**
* A builder interface used to construct a C {@code va_list}.
*/
@@ -37,6 +37,7 @@
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.aarch64.AArch64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;

@@ -273,6 +274,44 @@ public static VaList newVaListOfAddress(MemoryAddress ma) {
};
}

public static VaList emptyVaList() {
String name = CSupport.getSystemLinker().name();
return switch(name) {
case Win64.NAME -> Windowsx64Linker.emptyVaList();
case SysV.NAME -> SysVx64Linker.emptyVaList();
case AArch64.NAME -> throw new UnsupportedOperationException("Not yet implemented for this platform");
default -> throw new IllegalStateException("Unknown linker name: " + name);
};
}

public static MethodType convertVaListCarriers(MethodType mt, Class<?> carrier) {
Class<?>[] params = new Class<?>[mt.parameterCount()];
for (int i = 0; i < params.length; i++) {
Class<?> pType = mt.parameterType(i);
params[i] = ((pType == VaList.class) ? carrier : pType);
}
return methodType(mt.returnType(), params);
}

public static MethodHandle unboxVaLists(MethodType type, MethodHandle handle, MethodHandle unboxer) {
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = MethodHandles.filterArguments(handle, i, unboxer);
}
}
return handle;
}

public static MethodHandle boxVaLists(MethodHandle handle, MethodHandle boxer) {
MethodType type = handle.type();
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = MethodHandles.filterArguments(handle, i, boxer);
}
}
return handle;
}

public static class SimpleVaArg {
public final Class<?> carrier;
public final MemoryLayout layout;
@@ -290,4 +329,67 @@ public VarHandle varHandle() {
: layout.varHandle(carrier);
}
}

public static class EmptyVaList implements CSupport.VaList {

private final MemoryAddress address;

public EmptyVaList(MemoryAddress address) {
this.address = address;
}

private static UnsupportedOperationException uoe() {
return new UnsupportedOperationException("Empty VaList");
}

@Override
public int vargAsInt(MemoryLayout layout) {
throw uoe();
}

@Override
public long vargAsLong(MemoryLayout layout) {
throw uoe();
}

@Override
public double vargAsDouble(MemoryLayout layout) {
throw uoe();
}

@Override
public MemoryAddress vargAsAddress(MemoryLayout layout) {
throw uoe();
}

@Override
public MemorySegment vargAsSegment(MemoryLayout layout) {
throw uoe();
}

@Override
public void skip(MemoryLayout... layouts) {
throw uoe();
}

@Override
public boolean isAlive() {
return true;
}

@Override
public void close() {
throw uoe();
}

@Override
public VaList copy() {
return this;
}

@Override
public MemoryAddress address() {
return address;
}
}
}
@@ -31,10 +31,13 @@
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.misc.Unsafe;

import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
@@ -48,6 +51,8 @@

// See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists"
public class SysVVaList implements VaList {
private static final Unsafe U = Unsafe.getUnsafe();

static final Class<?> CARRIER = MemoryAddress.class;

// struct typedef __va_list_tag __va_list_tag {
@@ -111,6 +116,9 @@
private static final VarHandle VH_reg_save_area
= MemoryHandles.asAddressVarHandle(LAYOUT.varHandle(long.class, groupElement("reg_save_area")));

private static final Cleaner cleaner = Cleaner.create();
private static final CSupport.VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());

private final MemorySegment segment;
private final List<MemorySegment> slices = new ArrayList<>();
private final MemorySegment regSaveArea;
@@ -121,6 +129,23 @@
slices.add(regSaveArea);
}

private static MemoryAddress emptyListAddress() {
long ptr = U.allocateMemory(LAYOUT.byteSize());
MemorySegment ms = NativeMemorySegmentImpl.makeNativeSegmentUnchecked(
MemoryAddress.ofLong(ptr), LAYOUT.byteSize(), null, () -> U.freeMemory(ptr), null);
cleaner.register(SysVVaList.class, ms::close);
MemoryAddress base = ms.baseAddress();
VH_gp_offset.set(base, MAX_GP_OFFSET);
VH_fp_offset.set(base, MAX_FP_OFFSET);
VH_overflow_arg_area.set(base, MemoryAddress.NULL);
VH_reg_save_area.set(base, MemoryAddress.NULL);
return ms.withAccessModes(0).baseAddress();
}

public static CSupport.VaList empty() {
return EMPTY;
}

private int currentGPOffset() {
return (int) VH_gp_offset.get(segment.baseAddress());
}
@@ -269,10 +294,6 @@ public static VaList ofAddress(MemoryAddress ma) {
return new SysVVaList(MemorySegment.ofNativeRestricted(ma, LAYOUT.byteSize(), Thread.currentThread(), null, null));
}

MemorySegment getSegment() {
return segment;
}

@Override
public boolean isAlive() {
return segment.isAlive();
@@ -383,7 +404,15 @@ private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
return this;
}

public SysVVaList build() {
private boolean isEmpty() {
return currentGPOffset == 0 && currentFPOffset == FP_OFFSET && stackArgs.isEmpty();
}

public VaList build() {
if (isEmpty()) {
return EMPTY;
}

MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT.byteSize());
SysVVaList res = new SysVVaList(vaListSegment);
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
@@ -30,6 +30,7 @@
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.UpcallStubs;

import java.lang.invoke.MethodHandle;
@@ -60,9 +61,9 @@
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_unboxVaList = lookup.findStatic(SysVx64Linker.class, "unboxVaList",
MethodType.methodType(MemoryAddress.class, CSupport.VaList.class));
MH_boxVaList = lookup.findStatic(SysVx64Linker.class, "boxVaList",
MH_unboxVaList = lookup.findVirtual(CSupport.VaList.class, "address",
MethodType.methodType(MemoryAddress.class));
MH_boxVaList = lookup.findStatic(SysVx64Linker.class, "newVaListOfAddress",
MethodType.methodType(VaList.class, MemoryAddress.class));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
@@ -82,45 +83,17 @@ public static VaList newVaList(Consumer<VaList.Builder> actions) {
return builder.build();
}

private static MethodType convertVaListCarriers(MethodType mt) {
Class<?>[] params = new Class<?>[mt.parameterCount()];
for (int i = 0; i < params.length; i++) {
Class<?> pType = mt.parameterType(i);
params[i] = ((pType == CSupport.VaList.class) ? SysVVaList.CARRIER : pType);
}
return MethodType.methodType(mt.returnType(), params);
}

private static MethodHandle unxboxVaLists(MethodType type, MethodHandle handle) {
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = MethodHandles.filterArguments(handle, i, MH_unboxVaList);
}
}
return handle;
}

@Override
public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, FunctionDescriptor function) {
MethodType llMt = convertVaListCarriers(type);
MethodType llMt = SharedUtils.convertVaListCarriers(type, SysVVaList.CARRIER);
MethodHandle handle = CallArranger.arrangeDowncall(symbol, llMt, function);
handle = unxboxVaLists(type, handle);
return handle;
}

private static MethodHandle boxVaLists(MethodHandle handle) {
MethodType type = handle.type();
for (int i = 0; i < type.parameterCount(); i++) {
if (type.parameterType(i) == VaList.class) {
handle = MethodHandles.filterArguments(handle, i, MH_boxVaList);
}
}
handle = SharedUtils.unboxVaLists(type, handle, MH_unboxVaList);
return handle;
}

@Override
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
target = boxVaLists(target);
target = SharedUtils.boxVaLists(target, MH_boxVaList);
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
}

@@ -143,15 +116,11 @@ public String name() {
});
}

private static MemoryAddress unboxVaList(CSupport.VaList list) {
return ((SysVVaList) list).getSegment().baseAddress();
}

private static CSupport.VaList boxVaList(MemoryAddress ma) {
public static VaList newVaListOfAddress(MemoryAddress ma) {
return SysVVaList.ofAddress(ma);
}

public static VaList newVaListOfAddress(MemoryAddress ma) {
return SysVVaList.ofAddress(ma);
public static VaList emptyVaList() {
return SysVVaList.empty();
}
}
@@ -26,6 +26,7 @@
package jdk.internal.foreign.abi.x64.windows;

import jdk.incubator.foreign.CSupport;
import jdk.incubator.foreign.CSupport.VaList;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
@@ -57,11 +58,13 @@
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
//
class WinVaList implements CSupport.VaList {
class WinVaList implements VaList {
public static final Class<?> CARRIER = MemoryAddress.class;
private static final long VA_SLOT_SIZE_BYTES = 8;
private static final VarHandle VH_address = MemoryHandles.asAddressVarHandle(C_POINTER.varHandle(long.class));

private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemoryAddress.NULL);

private final MemorySegment segment;
private MemoryAddress ptr;
private final List<MemorySegment> copies;
@@ -76,6 +79,10 @@
this.copies = copies;
}

public static final VaList empty() {
return EMPTY;
}

@Override
public int vargAsInt(MemoryLayout layout) {
return (int) read(int.class, layout);
@@ -144,18 +151,14 @@ static Builder builder() {
return new Builder();
}

MemorySegment getSegment() {
return segment;
}

@Override
public void close() {
segment.close();
copies.forEach(MemorySegment::close);
}

@Override
public CSupport.VaList copy() {
public VaList copy() {
return WinVaList.ofAddress(ptr);
}

@@ -169,7 +172,7 @@ public boolean isAlive() {
return segment.isAlive();
}

static class Builder implements CSupport.VaList.Builder {
static class Builder implements VaList.Builder {

private final List<SimpleVaArg> args = new ArrayList<>();

@@ -204,7 +207,10 @@ public Builder vargFromSegment(MemoryLayout layout, MemorySegment value) {
return arg(MemorySegment.class, layout, value);
}

public WinVaList build() {
public VaList build() {
if (args.isEmpty()) {
return EMPTY;
}
MemorySegment ms = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size());
List<MemorySegment> copies = new ArrayList<>();

0 comments on commit 2ed13bf

Please sign in to comment.
You can’t perform that action at this time.