Skip to content

Commit 8345472

Browse files
committed
Add support for methods returning a long
1 parent fbfea59 commit 8345472

File tree

2 files changed

+126
-39
lines changed

2 files changed

+126
-39
lines changed

src/java.base/share/classes/jdk/internal/foreign/ErrnoUtil.java

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.lang.foreign.Linker;
3131
import java.lang.foreign.MemoryLayout;
3232
import java.lang.foreign.MemorySegment;
33-
import java.lang.foreign.StructLayout;
3433
import java.lang.invoke.MethodHandle;
3534
import java.lang.invoke.MethodHandles;
3635
import java.lang.invoke.MethodType;
@@ -39,55 +38,76 @@
3938
public final class ErrnoUtil {
4039

4140
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
42-
private static final MethodHandles.Lookup ARGUS = MethodHandles.lookup();
43-
private static final TerminatingThreadLocal<MemorySegment> TL = new TerminatingThreadLocal<>(){
41+
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
42+
43+
private static final TerminatingThreadLocal<MemorySegment> TL = new TerminatingThreadLocal<>() {
4444
@Override
4545
protected void threadTerminated(MemorySegment value) {
4646
free(value);
4747
}
4848
};
4949

50-
private static final StructLayout CAPTURE_STATE_LAYOUT = Linker.Option.captureStateLayout();
51-
private static final VarHandle ERRNO_HANDLE =
52-
CAPTURE_STATE_LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("errno"));
50+
private static final VarHandle ERRNO_HANDLE =
51+
Linker.Option.captureStateLayout()
52+
.varHandle(MemoryLayout.PathElement.groupElement("errno"));
5353

5454
private static final MethodHandle ACQUIRE_MH =
55-
MhUtil.findStatic(ARGUS, "acquireCaptureStateSegment",
55+
MhUtil.findStatic(LOOKUP, "acquireCaptureStateSegment",
5656
MethodType.methodType(MemorySegment.class));
57-
private static final MethodHandle RETURN_FILTER_MH =
58-
MhUtil.findStatic(ARGUS, "returnFilter",
57+
58+
private static final MethodHandle INT_RETURN_FILTER_MH =
59+
MhUtil.findStatic(LOOKUP, "returnFilter",
5960
MethodType.methodType(int.class, int.class));
6061

61-
private ErrnoUtil() {}
62+
private static final MethodHandle LONG_RETURN_FILTER_MH =
63+
MhUtil.findStatic(LOOKUP, "returnFilter",
64+
MethodType.methodType(long.class, long.class));
65+
66+
private ErrnoUtil() {
67+
}
6268

6369
/**
64-
* {@return a new MethodHandle that returns the returned int value if the
65-
* returned value is non-negative, or else the negative errno}
70+
* {@return a new MethodHandle that adapts the provided {@code target} so that it
71+
* directly returns the same value as the {@code target} if it is
72+
* non-negative, otherwise returns the negated errno}
6673
* <p>
67-
* As such, this method is suitable for adapting method handles for system calls
68-
* (e.g. {@code open()}, {@code read()}, and {@code close()}).
74+
* This method is suitable for adapting system-call method handles(e.g.
75+
* {@code open()}, {@code read()}, and {@code close()}). Clients can check the return
76+
* value as shown in this example:
77+
* {@snippet lang = java:
78+
* static final MethodHandle OPEN = ErrnoUtil.adaptSystemCall(CAPTURING_OPEN);
6979
*
70-
* @param target that returns an {@code int} and has an errno MemorySegment as
71-
* the first parameter
72-
* @throws IllegalArgumentException if the provided {@code target}'s return type
73-
* is not {@code int}
80+
* try {
81+
* int fh = (int)OPEN.invoke(pathName, flags);
82+
* if (fh < 0) {
83+
* throw new IOException("Error opening file: errno = " + (-fh));
84+
* }
85+
* processFile(fh);
86+
* }
87+
*
88+
*}
89+
*
90+
* @param target method handle that returns an {@code int} or a {@code long} and has
91+
* an errno MemorySegment as its first parameter
92+
* @throws IllegalArgumentException if the provided {@code target}'s return type is
93+
* not {@code int} or {@code long}
7494
* @throws IllegalArgumentException if the provided {@code target}'s first parameter
75-
* type is not {@linkplain MemorySegment}
95+
* type is not {@linkplain MemorySegment}
7696
*/
7797
public static MethodHandle adaptSystemCall(MethodHandle target) {
78-
if (target.type().returnType() != int.class) {
79-
throw new IllegalArgumentException("The provided target " + target
80-
+ " does not return an int");
98+
// Implicit null check
99+
final Class<?> returnType = target.type().returnType();
100+
if (!(returnType.equals(int.class) || returnType.equals(long.class))) {
101+
throw illegalArgNot(target, "return an int or a long");
81102
}
82103
if (target.type().parameterType(0) != MemorySegment.class) {
83-
throw new IllegalArgumentException("The provided target " + target
84-
+ " does not have a MemorySegment as the first parameter");
104+
throw illegalArgNot(target, "have a MemorySegment as the first parameter");
85105
}
86-
// (MemorySegment, C*)int -> (C*)int
106+
// (MemorySegment, C*)(int | long) -> (C*)(int | long)
87107
target = MethodHandles.collectArguments(target, 0, ACQUIRE_MH);
88-
// (C*)int -> (C*)int
89-
target = MethodHandles.filterReturnValue(target, RETURN_FILTER_MH);
90-
return target;
108+
// (C*)(int | long) -> (C*)(int | long)
109+
return MethodHandles.filterReturnValue(target,
110+
returnType.equals(int.class) ? INT_RETURN_FILTER_MH : LONG_RETURN_FILTER_MH);
91111
}
92112

93113
// Used reflectively via ACQUIRE_MH
@@ -101,19 +121,32 @@ private static MemorySegment acquireCaptureStateSegment() {
101121

102122
// Used reflectively via RETURN_FILTER_MH
103123
private static int returnFilter(int result) {
104-
return result >= 0
105-
? result
106-
: -(int) ERRNO_HANDLE.get(acquireCaptureStateSegment(), 0L);
124+
return result >= 0 ? result : -errno();
125+
}
126+
127+
// Used reflectively via RETURN_FILTER_MH
128+
private static long returnFilter(long result) {
129+
return result >= 0 ? result : -errno();
130+
}
131+
132+
private static int errno() {
133+
return (int) ERRNO_HANDLE.get(acquireCaptureStateSegment(), 0L);
107134
}
108135

109136
@SuppressWarnings("restricted")
110137
private static MemorySegment malloc() {
111-
long address = UNSAFE.allocateMemory(CAPTURE_STATE_LAYOUT.byteSize());
112-
return MemorySegment.ofAddress(address).reinterpret(CAPTURE_STATE_LAYOUT.byteSize());
138+
final long size = Linker.Option.captureStateLayout().byteSize();
139+
final long address = UNSAFE.allocateMemory(size);
140+
return MemorySegment.ofAddress(address).reinterpret(size);
113141
}
114142

115143
private static void free(MemorySegment segment) {
116144
UNSAFE.freeMemory(segment.address());
117145
}
118146

147+
private static IllegalArgumentException illegalArgNot(MethodHandle target, String info) {
148+
return new IllegalArgumentException("The provided target " + target
149+
+ " does not " + info);
150+
}
151+
119152
}

test/jdk/java/foreign/TestErrnoUtil.java

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,39 +46,93 @@ final class TestErrnoUtil {
4646
private static final VarHandle ERRNO_HANDLE = Linker.Option.captureStateLayout()
4747
.varHandle(MemoryLayout.PathElement.groupElement("errno"));
4848

49-
private static final MethodHandle DUMMY_HANDLE;
49+
private static final MethodHandle INT_DUMMY_HANDLE;
50+
private static final MethodHandle LONG_DUMMY_HANDLE;
5051

5152
static {
5253
try {
53-
DUMMY_HANDLE = MethodHandles.lookup()
54+
MethodHandles.Lookup lookup = MethodHandles.lookup();
55+
INT_DUMMY_HANDLE = lookup
5456
.findStatic(TestErrnoUtil.class, "dummy",
5557
MethodType.methodType(int.class, MemorySegment.class, int.class, int.class));
58+
LONG_DUMMY_HANDLE = lookup
59+
.findStatic(TestErrnoUtil.class, "dummy",
60+
MethodType.methodType(long.class, MemorySegment.class, long.class, int.class));
5661
} catch (ReflectiveOperationException e) {
5762
throw new InternalError(e);
5863
}
5964
}
6065

6166
@Test
62-
void successful() throws Throwable {
63-
MethodHandle adapted = ErrnoUtil.adaptSystemCall(DUMMY_HANDLE);
67+
void successfulInt() throws Throwable {
68+
MethodHandle adapted = ErrnoUtil.adaptSystemCall(INT_DUMMY_HANDLE);
6469
int r = (int) adapted.invoke(1, 0);
6570
assertEquals(1, r);
6671
}
6772

6873
private static final int EACCES = 13; /* Permission denied */
6974

7075
@Test
71-
void error() throws Throwable {
72-
MethodHandle adapted = ErrnoUtil.adaptSystemCall(DUMMY_HANDLE);
76+
void errorInt() throws Throwable {
77+
MethodHandle adapted = ErrnoUtil.adaptSystemCall(INT_DUMMY_HANDLE);
7378

7479
int r = (int) adapted.invoke(-1, EACCES);
7580
assertEquals(-EACCES, r);
7681
}
7782

83+
@Test
84+
void successfulLong() throws Throwable {
85+
MethodHandle adapted = ErrnoUtil.adaptSystemCall(LONG_DUMMY_HANDLE);
86+
long r = (long) adapted.invoke(1, 0);
87+
assertEquals(1, r);
88+
}
89+
90+
@Test
91+
void errorLong() throws Throwable {
92+
MethodHandle adapted = ErrnoUtil.adaptSystemCall(LONG_DUMMY_HANDLE);
93+
94+
long r = (long) adapted.invoke(-1, EACCES);
95+
assertEquals(-EACCES, r);
96+
}
97+
98+
@Test
99+
void invariants() throws Throwable {
100+
assertThrows(NullPointerException.class, () -> ErrnoUtil.adaptSystemCall(null));
101+
102+
MethodHandle noSegment = MethodHandles.lookup()
103+
.findStatic(TestErrnoUtil.class, "wrongType",
104+
MethodType.methodType(long.class, long.class, int.class));
105+
106+
var noSegEx = assertThrows(IllegalArgumentException.class, () -> ErrnoUtil.adaptSystemCall(noSegment));
107+
assertTrue(noSegEx.getMessage().contains("does not have a MemorySegment as the first parameter"));
108+
109+
MethodHandle wrongReturnType = MethodHandles.lookup()
110+
.findStatic(TestErrnoUtil.class, "wrongType",
111+
MethodType.methodType(short.class, MemorySegment.class, long.class, int.class));
112+
113+
var wrongRetEx = assertThrows(IllegalArgumentException.class, () -> ErrnoUtil.adaptSystemCall(wrongReturnType));
114+
assertTrue(wrongRetEx.getMessage().contains("does not return an int or a long"));
115+
116+
}
117+
78118
// Dummy method that is just returning the provided parameters
79119
private static int dummy(MemorySegment segment, int result, int errno) {
80120
ERRNO_HANDLE.set(segment, 0, errno);
81121
return result;
82122
}
83123

124+
// Dummy method that is just returning the provided parameters
125+
private static long dummy(MemorySegment segment, long result, int errno) {
126+
ERRNO_HANDLE.set(segment, 0, errno);
127+
return result;
128+
}
129+
130+
private static long wrongType(long result, int errno) {
131+
return 0;
132+
}
133+
134+
private static short wrongType(MemorySegment segment, long result, int errno) {
135+
return 0;
136+
}
137+
84138
}

0 commit comments

Comments
 (0)