Skip to content

Commit 97a72f3

Browse files
author
duke
committedMar 29, 2021
Automatic merge of foreign-memaccess+abi into foreign-jextract
2 parents 944c8ae + d70c1e4 commit 97a72f3

File tree

11 files changed

+96
-78
lines changed

11 files changed

+96
-78
lines changed
 

‎src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
/**
2929
* Represents a type which is <em>addressable</em>. An addressable type is one which can be projected down to
3030
* a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment},
31-
* {@link MemoryAddress}, {@link LibraryLookup.Symbol} and {@link CLinker.VaList}.
31+
* {@link MemoryAddress} and {@link CLinker.VaList}.
3232
*
3333
* @apiNote In the future, if the Java language permits, {@link Addressable}
3434
* may become a {@code sealed} interface, which would prohibit subclassing except by
35-
* explicitly permitted types, such as {@link MemorySegment}, {@link MemoryAddress}, {@link LibraryLookup.Symbol}
35+
* explicitly permitted types, such as {@link MemorySegment}, {@link MemoryAddress}
3636
* and {@link CLinker.VaList}.
3737
*
3838
* @implSpec

‎src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/LibraryLookup.java

+26-42
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,22 @@
4040
* A native library lookup. Exposes a lookup operation for searching symbols, see {@link LibraryLookup#lookup(String)}.
4141
* A given native library remains loaded as long as there is at least one <em>live</em> library lookup instance referring
4242
* to it.
43-
* All symbol instances (see {@link LibraryLookup.Symbol}) generated by a given library lookup object contain a strong reference
44-
* to said lookup object, therefore preventing library unloading; in turn method handle instances obtained from
45-
* {@link CLinker#downcallHandle(Addressable, MethodType, FunctionDescriptor)}) also maintain a strong reference
46-
* to the addressable parameter used for their construction. This means that there is always a strong reachability chain
47-
* from a native method handle to a lookup object (the one that was used to lookup the native library symbol the method handle
48-
* refers to); this is useful to prevent situations where a native library is unloaded in the middle of a native call.
49-
* <p><a id = "var-symbols"></a></p>
50-
* In cases where a client wants to create a memory segment out of a lookup symbol, the client might want to attach the
51-
* lookup symbol to the newly created segment, so that the symbol will be kept reachable as long as the memory segment
52-
* is reachable; this can be achieved by creating the segment using the {@link MemoryAddress#asSegmentRestricted(long, ResourceScope)}.
53-
* restricted segment factory, as follows:
43+
* All instances generated by a given library lookup object contain a strong reference to said lookup object,
44+
* therefore preventing library unloading. For {@link #lookup(String, MemoryLayout) memory segments} obtained from a library lookup object,
45+
* this means that clients can safely dereference memory associated with lookup symbols, as follows:
5446
* <pre>{@code
55-
LibraryLookup defaultLookup = LibraryLookup.defaultLookup();
56-
LibraryLookup.Symbol errno = defaultLookup.lookup("errno");
57-
MemorySegment errnoSegment = errno.address().asSegmentRestricted(4, ResourceScope.ofShared(errno, Cleaner.create()));
47+
* LibraryLookup defaultLookup = LibraryLookup.defaultLookup();
48+
* MemorySegment errnoSegment = defaultLookup.lookup("errno", MemoryLayouts.JAVA_INT).get();
49+
* int errno = MemoryAccess.getInt(errnoSegment);
5850
* }</pre>
5951
* <p>
52+
* For {@link #lookup(String) memory addresses} obtained from a library lookup object,
53+
* since {@link CLinker#downcallHandle(Addressable, MethodType, FunctionDescriptor) native method handles}
54+
* also maintain a strong reference to the addressable parameter used for their construction, there is
55+
* always a strong reachability chain from a native method handle to a lookup object (the one that was used to lookup
56+
* the native library symbol the method handle refers to). This is useful to prevent situations where a native library
57+
* is unloaded in the middle of a native call.
58+
* <p>
6059
* To allow for a library to be unloaded, a client will have to discard any strong references it
6160
* maintains, directly, or indirectly to a lookup object associated with given library.
6261
*
@@ -66,39 +65,24 @@
6665
public interface LibraryLookup {
6766

6867
/**
69-
* A symbol retrieved during a library lookup. A lookup symbol has a <em>name</em> and can be projected
70-
* into a memory address (see {@link #name()} and {@link #address()}, respectively).
71-
*
72-
* @apiNote In the future, if the Java language permits, {@link Symbol}
73-
* may become a {@code sealed} interface, which would prohibit subclassing except by
74-
* explicitly permitted types.
75-
*
76-
* @implSpec
77-
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
68+
* Looks up a symbol with given name in this library. The returned memory address maintains a strong reference to this lookup object.
69+
* @param name the symbol name.
70+
* @return the memory address associated with the library symbol (if any).
7871
*/
79-
interface Symbol extends Addressable {
80-
/**
81-
* The name of this lookup symbol.
82-
* @return the name of this lookup symbol.
83-
*/
84-
String name();
85-
86-
/**
87-
* The memory address of this lookup symbol. If the memory associated with this symbol needs to be dereferenced,
88-
* clients can obtain a segment from this symbol's address using the {@link MemoryAddress#asSegmentRestricted(long, Runnable, ResourceScope)},
89-
* and making sure that the created segment maintains a <a href="LibraryLookup.html#var-symbols">strong reference</a> to this symbol, to prevent library unloading.
90-
* @return the memory address of this lookup symbol.
91-
*/
92-
@Override
93-
MemoryAddress address();
94-
}
72+
Optional<MemoryAddress> lookup(String name);
9573

9674
/**
97-
* Looks up a symbol with given name in this library. The returned symbol maintains a strong reference to this lookup object.
75+
* Looks up a symbol with given name in this library. The returned memory segment has a size that matches that of
76+
* the specified layout, and maintains a strong reference to this lookup object. This method can be useful
77+
* to lookup global variable symbols in a foreign library.
9878
* @param name the symbol name.
99-
* @return the library symbol (if any).
79+
* @param layout the layout to be associated with the library symbol.
80+
* @return the memory segment associated with the library symbol (if any).
81+
* @throws IllegalArgumentException if the address associated with the lookup symbol do not match the
82+
* {@link MemoryLayout#byteAlignment() alignment constraints} in {@code layout}.
10083
*/
101-
Optional<Symbol> lookup(String name);
84+
@NativeAccess
85+
Optional<MemorySegment> lookup(String name, MemoryLayout layout);
10286

10387
/**
10488
* Obtain a default library lookup object.

‎src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LibrariesHelper.java

+25-14
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@
2929

3030
import java.io.File;
3131
import jdk.incubator.foreign.LibraryLookup;
32+
import jdk.incubator.foreign.MemoryLayout;
33+
import jdk.incubator.foreign.MemorySegment;
3234
import jdk.incubator.foreign.ResourceScope;
3335
import jdk.internal.loader.NativeLibraries;
3436
import jdk.internal.loader.NativeLibrary;
37+
import jdk.internal.reflect.CallerSensitive;
38+
import jdk.internal.reflect.Reflection;
3539

3640
import java.lang.ref.Reference;
3741
import java.lang.ref.WeakReference;
@@ -103,29 +107,36 @@ static LibraryLookupImpl lookup(Supplier<NativeLibrary> librarySupplier, String
103107
//Todo: in principle we could expose a scope accessor, so that users could unload libraries at will
104108
static class LibraryLookupImpl implements LibraryLookup {
105109
final NativeLibrary library;
106-
final ResourceScope scope;
110+
final MemorySegment librarySegment;
107111

108112
LibraryLookupImpl(NativeLibrary library, ResourceScope scope) {
109113
this.library = library;
110-
this.scope = scope;
114+
this.librarySegment = MemoryAddress.NULL.asSegmentRestricted(Long.MAX_VALUE, scope);
111115
}
112116

113117
@Override
114-
public Optional<Symbol> lookup(String name) {
118+
public Optional<MemoryAddress> lookup(String name) {
115119
try {
116120
Objects.requireNonNull(name);
117121
MemoryAddress addr = MemoryAddress.ofLong(library.lookup(name));
118-
return Optional.of(new Symbol() { // inner class - retains a link to the scope
119-
@Override
120-
public String name() {
121-
return name;
122-
}
123-
124-
@Override
125-
public MemoryAddress address() {
126-
return addr;
127-
}
128-
});
122+
return Optional.of(librarySegment.asSlice(addr).address());
123+
} catch (NoSuchMethodException ex) {
124+
return Optional.empty();
125+
}
126+
}
127+
128+
@Override
129+
@CallerSensitive
130+
public Optional<MemorySegment> lookup(String name, MemoryLayout layout) {
131+
Reflection.ensureNativeAccess(Reflection.getCallerClass());
132+
try {
133+
Objects.requireNonNull(name);
134+
Objects.requireNonNull(layout);
135+
MemoryAddress addr = MemoryAddress.ofLong(library.lookup(name));
136+
if (addr.toRawLongValue() % layout.byteAlignment() != 0) {
137+
throw new IllegalArgumentException("Bad layout alignment constraints: " + layout.byteAlignment());
138+
}
139+
return Optional.of(librarySegment.asSlice(addr, layout.byteSize()));
129140
} catch (NoSuchMethodException ex) {
130141
return Optional.empty();
131142
}

‎test/jdk/java/foreign/StdLibTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ static class StdLibHelper {
192192
MethodType.methodType(int.class, MemoryAddress.class, VaList.class),
193193
FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST));
194194

195-
final static LibraryLookup.Symbol printfAddr = lookup.lookup("printf").get();
195+
final static MemoryAddress printfAddr = lookup.lookup("printf").get();
196196

197197
final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
198198

‎test/jdk/java/foreign/TestDowncall.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import jdk.incubator.foreign.CLinker;
3737
import jdk.incubator.foreign.FunctionDescriptor;
3838
import jdk.incubator.foreign.LibraryLookup;
39+
import jdk.incubator.foreign.MemoryAddress;
3940
import jdk.incubator.foreign.MemoryLayout;
4041

4142
import java.lang.invoke.MethodHandle;
@@ -57,7 +58,7 @@ public class TestDowncall extends CallGeneratorHelper {
5758
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
5859
public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
5960
List<Consumer<Object>> checks = new ArrayList<>();
60-
LibraryLookup.Symbol addr = lib.lookup(fName).get();
61+
MemoryAddress addr = lib.lookup(fName).get();
6162
MethodType mt = methodType(ret, paramTypes, fields);
6263
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
6364
Object[] args = makeArgs(paramTypes, fields, checks);
@@ -84,7 +85,7 @@ public void testDowncall(int count, String fName, Ret ret, List<ParamType> param
8485
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
8586
public void testDowncallNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
8687
List<Consumer<Object>> checks = new ArrayList<>();
87-
LibraryLookup.Symbol addr = lib.lookup(fName).get();
88+
MemoryAddress addr = lib.lookup(fName).get();
8889
MethodType mt = methodType(ret, paramTypes, fields);
8990
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
9091
Object[] args = makeArgs(paramTypes, fields, checks);
@@ -107,7 +108,7 @@ public void testDowncallNoScope(int count, String fName, Ret ret, List<ParamType
107108
}
108109
}
109110

110-
Object doCall(LibraryLookup.Symbol addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable {
111+
Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable {
111112
MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor);
112113
Object res = mh.invokeWithArguments(args);
113114
return res;

‎test/jdk/java/foreign/TestIntrinsics.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import java.util.ArrayList;
4242
import java.util.List;
4343

44+
import jdk.incubator.foreign.MemoryAddress;
4445
import jdk.incubator.foreign.MemoryLayout;
4546
import org.testng.annotations.*;
4647

@@ -83,7 +84,7 @@ interface AddIdentity {
8384
}
8485

8586
AddIdentity addIdentity = (name, carrier, layout, arg) -> {
86-
LibraryLookup.Symbol ma = lookup.lookup(name).orElseThrow();
87+
MemoryAddress ma = lookup.lookup(name).orElseThrow();
8788
MethodType mt = methodType(carrier, carrier);
8889
FunctionDescriptor fd = FunctionDescriptor.of(layout, layout);
8990

@@ -93,7 +94,7 @@ interface AddIdentity {
9394
};
9495

9596
{ // empty
96-
LibraryLookup.Symbol ma = lookup.lookup("empty").orElseThrow();
97+
MemoryAddress ma = lookup.lookup("empty").orElseThrow();
9798
MethodType mt = methodType(void.class);
9899
FunctionDescriptor fd = FunctionDescriptor.ofVoid();
99100
tests.add(abi.downcallHandle(ma, mt, fd), null);
@@ -108,7 +109,7 @@ interface AddIdentity {
108109
addIdentity.add("identity_double", double.class, C_DOUBLE, 10D);
109110

110111
{ // identity_va
111-
LibraryLookup.Symbol ma = lookup.lookup("identity_va").orElseThrow();
112+
MemoryAddress ma = lookup.lookup("identity_va").orElseThrow();
112113
MethodType mt = methodType(int.class, int.class, double.class, int.class, float.class, long.class);
113114
FunctionDescriptor fd = FunctionDescriptor.of(C_INT, C_INT, asVarArg(C_DOUBLE),
114115
asVarArg(C_INT), asVarArg(C_FLOAT), asVarArg(C_LONG_LONG));
@@ -123,7 +124,7 @@ interface AddIdentity {
123124
C_SHORT, C_SHORT);
124125
Object[] args = {1, 10D, 2L, 3F, (byte) 0, (short) 13, 'a'};
125126
for (int i = 0; i < args.length; i++) {
126-
LibraryLookup.Symbol ma = lookup.lookup("invoke_high_arity" + i).orElseThrow();
127+
MemoryAddress ma = lookup.lookup("invoke_high_arity" + i).orElseThrow();
127128
MethodType mt = baseMT.changeReturnType(baseMT.parameterType(i));
128129
FunctionDescriptor fd = baseFD.withReturnLayout(baseFD.argumentLayouts().get(i));
129130
Object expected = args[i];

‎test/jdk/java/foreign/TestLibraryLookup.java

+25-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
*/
3030

3131
import jdk.incubator.foreign.LibraryLookup;
32+
import jdk.incubator.foreign.MemoryAccess;
3233
import jdk.incubator.foreign.MemoryAddress;
34+
import jdk.incubator.foreign.MemoryLayout;
35+
import jdk.incubator.foreign.MemoryLayouts;
36+
import jdk.incubator.foreign.MemorySegment;
3337
import jdk.internal.foreign.LibrariesHelper;
3438
import org.testng.annotations.Test;
3539

@@ -66,10 +70,9 @@ public void testInvalidLookupPath() {
6670

6771
@Test
6872
public void testSimpleLookup() throws Throwable {
69-
LibraryLookup.Symbol symbol = null;
73+
MemoryAddress symbol = null;
7074
LibraryLookup lookup = LibraryLookup.ofLibrary("LookupTest");
7175
symbol = lookup.lookup("f").get();
72-
assertEquals(symbol.name(), "f");
7376
assertEquals(LibrariesHelper.numLoadedLibraries(), 1);
7477
lookup = null;
7578
symbol = null;
@@ -78,7 +81,7 @@ public void testSimpleLookup() throws Throwable {
7881

7982
@Test
8083
public void testInvalidSymbolLookup() throws Throwable {
81-
LibraryLookup.Symbol symbol = null;
84+
MemoryAddress symbol = null;
8285
LibraryLookup lookup = LibraryLookup.ofLibrary("LookupTest");
8386
assertTrue(lookup.lookup("nonExistent").isEmpty());
8487
assertEquals(LibrariesHelper.numLoadedLibraries(), 1);
@@ -87,13 +90,29 @@ public void testInvalidSymbolLookup() throws Throwable {
8790
waitUnload();
8891
}
8992

93+
@Test
94+
public void testVariableSymbolLookup() throws Throwable {
95+
LibraryLookup lookup = LibraryLookup.ofLibrary("LookupTest");
96+
MemorySegment segment = lookup.lookup("c", MemoryLayouts.JAVA_INT).get();
97+
assertEquals(MemoryAccess.getInt(segment), 42);
98+
lookup = null;
99+
segment = null;
100+
waitUnload();
101+
}
102+
103+
@Test(expectedExceptions = IllegalArgumentException.class)
104+
public void testBarVariableSymbolLookup() throws Throwable {
105+
LibraryLookup lookup = LibraryLookup.ofLibrary("LookupTest");
106+
lookup.lookup("c", MemoryLayouts.JAVA_INT.withBitAlignment(1 << 16)).get();
107+
}
108+
90109
@Test
91110
public void testMultiLookupSameLoader() throws Throwable {
92-
List<LibraryLookup.Symbol> symbols = new ArrayList<>();
111+
List<MemoryAddress> symbols = new ArrayList<>();
93112
List<LibraryLookup> lookups = new ArrayList<>();
94113
for (int i = 0 ; i < 5 ; i++) {
95114
LibraryLookup lookup = LibraryLookup.ofLibrary("LookupTest");
96-
LibraryLookup.Symbol symbol = lookup.lookup("f").get();
115+
MemoryAddress symbol = lookup.lookup("f").get();
97116
lookups.add(lookup);
98117
symbols.add(symbol);
99118
assertEquals(LibrariesHelper.numLoadedLibraries(), 1);
@@ -151,7 +170,7 @@ public Class<?> loadClass(String name) throws ClassNotFoundException {
151170

152171
static class Holder {
153172
public static LibraryLookup lookup;
154-
public static LibraryLookup.Symbol symbol;
173+
public static MemoryAddress symbol;
155174

156175
static {
157176
try {

‎test/jdk/java/foreign/TestUpcall.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ void setup() {
8888
public void testUpcalls(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
8989
List<Consumer<Object>> returnChecks = new ArrayList<>();
9090
List<Consumer<Object[]>> argChecks = new ArrayList<>();
91-
LibraryLookup.Symbol addr = lib.lookup(fName).get();
91+
MemoryAddress addr = lib.lookup(fName).get();
9292
MethodType mtype = methodType(ret, paramTypes, fields);
9393
try (NativeScope scope = new NativeScope()) {
9494
MethodHandle mh = abi.downcallHandle(addr, scope, mtype, function(ret, paramTypes, fields));
@@ -106,7 +106,7 @@ public void testUpcalls(int count, String fName, Ret ret, List<ParamType> paramT
106106
public void testUpcallsNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
107107
List<Consumer<Object>> returnChecks = new ArrayList<>();
108108
List<Consumer<Object[]>> argChecks = new ArrayList<>();
109-
LibraryLookup.Symbol addr = lib.lookup(fName).get();
109+
MemoryAddress addr = lib.lookup(fName).get();
110110
MethodType mtype = methodType(ret, paramTypes, fields);
111111
MethodHandle mh = abi.downcallHandle(addr, SegmentAllocator.ofDefault(), mtype, function(ret, paramTypes, fields));
112112
Object[] args = makeArgs(ResourceScope.ofDefault(), ret, paramTypes, fields, returnChecks, argChecks);

‎test/jdk/java/foreign/TestVarArgs.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class TestVarArgs {
6161
static final VarHandle VH_IntArray = MemoryLayout.ofSequence(C_INT).varHandle(int.class, sequenceElement());
6262

6363
static final CLinker abi = CLinker.getInstance();
64-
static final LibraryLookup.Symbol varargsAddr = LibraryLookup.ofLibrary("VarArgs")
64+
static final MemoryAddress varargsAddr = LibraryLookup.ofLibrary("VarArgs")
6565
.lookup("varargs").get();
6666

6767
static final int WRITEBACK_BYTES_PER_ARG = 8;

‎test/jdk/java/foreign/libLookupTest.c

+1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@
2929
#endif
3030

3131
EXPORT void f() { }
32+
EXPORT int c = 42;
3233

‎test/jdk/java/foreign/virtual/TestVirtualCalls.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.lang.invoke.MethodHandle;
3838
import java.lang.invoke.MethodType;
3939

40+
import jdk.incubator.foreign.MemoryAddress;
4041
import org.testng.annotations.*;
4142

4243
import static jdk.incubator.foreign.CLinker.*;
@@ -48,9 +49,9 @@ public class TestVirtualCalls {
4849
static final LibraryLookup lookup = LibraryLookup.ofLibrary("Virtual");
4950

5051
static final MethodHandle func;
51-
static final LibraryLookup.Symbol funcA;
52-
static final LibraryLookup.Symbol funcB;
53-
static final LibraryLookup.Symbol funcC;
52+
static final MemoryAddress funcA;
53+
static final MemoryAddress funcB;
54+
static final MemoryAddress funcC;
5455

5556
static {
5657
func = abi.downcallHandle(

0 commit comments

Comments
 (0)
Please sign in to comment.