[foreign] programmatic access to native libraries w/o binder

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed Nov 28 15:32:28 UTC 2018


On 28/11/2018 15:27, Jorn Vernee wrote:
> Looking good!
>
>> * having to create a NativeMethodType when I just want to pass the
>> list of layout types leads to more verbose code
>>
>> * we might need NativeTypes.POINTER - doing NativeTypes.VOID.pointer()
>> is an explicit workaround, but a tad verbose
>
> Reading through the code, I prefer both of these actually, since it 
> makes the code more visually explicit as to what's happening. The `new 
> NativeType(...)` call also gives a visual grouping to the arguments. 
> Though I admit, I like being very descriptive when writing code.
>
> Tbh, I mostly expect the NativeMethodType to be constructed 
> automatically, e.g. by deriving the signature from a mangled symbol 
> name, so I'm not sure how much verbosity will be a problem in practice.
>
> To save on characters I'd suggest using `import static NativeTypes.*`, 
> and perhaps we could add a convenience overload of upcallStub which 
> doesn't take the target MethodHandle, and instead tries to infer it 
> from the passed receiver object?
>
>     default UpcallHandle upcallStub(Object receiver, NativeMethodType 
> nmt) {
>         Method m = receiver.getClass().getDeclaredMethods()[0]; // you 
> get the gist
>         return upcallStub(cc, receiver, publicLookup().unreflect(m), 
> nmt);
>     }

It's not about saving characters - I was trying to imagine what a 
typical usage of this API would look like. I assume that people that 
reach low level to SystemABI directly (bypassing the binder) basically 
want to be more in control on how the mapping is done. This could be a 
powerful options for framework writers. But those users, I believe, will 
almost never use the API point that takes a j.l.Method and a Function - 
they would prefer to specific a list of NativeTypes/LayoutTypes directly.

Internally, we can have a NativeMethodType abstraction, since for the 
binder it seems to make sense - I'm wondering how much that should be 
public API, given that low level users will probably see that as a 
somewhat redundant intermediate step.

>
> Btw, now that we're starting to have NativeTypes and NativeMethodType 
> maybe we should consider renaming LayoutType to NativeType as well?

Sure, I was thinking along the same lines

Maurizio

>
> Jorn
>
> Maurizio Cimadamore schreef op 2018-11-28 15:00:
>> Hi,
>> I took the patch at [1] for a spin with jshell. I wanted to see how
>> easy it was to call native functions using the SystemABI interface -
>> e.g. w/o binder and annotated interface. I think I'm pleasantly
>> impressed! I did some changes to the proposed patch as I wanted to put
>> everything SystemABI-specific in a public package (java.foreign) and I
>> also added a method on UpcallHandle to get a pointer. But overall
>> these were minor modification, mostly in order to be able to use this
>> setup on jshell. But let's dive on some code.
>>
>> First, some setup:
>>
>>
>> $ jshell
>> |  Welcome to JShell -- Version 12-internal
>> |  For an introduction type: /help intro
>>
>> jshell> import java.foreign.*
>>
>> jshell> import java.foreign.memory.*
>>
>> jshell> import java.lang.invoke.*
>>
>> jshell> var abi = SystemABI.getInstance()
>> abi ==> jdk.internal.foreign.abi.sysv.x64.SysVx64ABI at 41cf53f9
>>
>> Now that we have an ABI, let's try getpid:
>>
>>
>> jshell> var getpid =
>> abi.downcallHandle(Libraries.getDefaultLibrary().lookup("getpid"), new
>> NativeMethodType(NativeTypes.INT32))
>> getpid ==> MethodHandle()int
>>
>> jshell> (int)getpid.invokeExact()
>> $6 ==> 20308
>>
>>
>> That was easy; let's try a function which actually takes some args:
>>
>>
>> jshell> var pow =
>> abi.downcallHandle(Libraries.getDefaultLibrary().lookup("pow"), new
>> NativeMethodType(NativeTypes.DOUBLE, NativeTypes.DOUBLE,
>> NativeTypes.DOUBLE))
>> pow ==> MethodHandle(double,double)double
>>
>> jshell> (double)pow.invokeExact(2d, 3d)
>> $12 ==> 8.0
>>
>>
>> That works too. Finally, let's see if we can make qsort work - first
>> let's declare the comparison logic:
>>
>>
>> jshell> class Comp {
>>    ...>       int comp(Pointer<Integer> p1, Pointer<Integer> p2) {
>>    ...>          return p1.get() - p2.get();
>>    ...>       }
>>    ...>    }
>> |  created class Comp
>>
>>
>> And allocate the stub:
>>
>>
>>
>> jshell> var up = abi.upcallStub(abi.defaultCallingConvention(), new 
>> Comp(),
>>    ...> MethodHandles.lookup().findVirtual(Comp.class, "comp",
>> MethodType.methodType(int.class, Pointer.class, Pointer.class)),
>>    ...>                         new
>> NativeMethodType(NativeTypes.INT32, NativeTypes.INT.pointer(),
>> NativeTypes.INT.pointer()));
>> up ==> jdk.internal.foreign.invokers.DirectUpcallHandle at dcf3e99
>>
>>
>> And finally, let's call qsort and print the results.
>>
>>
>> jshell> var qsort =
>> abi.downcallHandle(Libraries.getDefaultLibrary().lookup("qsort"),
>>    ...>                         new NativeMethodType(NativeTypes.VOID,
>> NativeTypes.INT.pointer(), NativeTypes.INT, NativeTypes.INT,
>> NativeTypes.VOID.pointer()));
>> qsort ==> MethodHandle(Pointer,int,int,Pointer)void
>>
>> jshell> Scope sc = Scope.newNativeScope()
>> sc ==> jdk.internal.foreign.ScopeImpl$NativeScope at 5fdef03a
>>
>> jshell> var arr = sc.allocateArray(NativeTypes.INT32, new int[] { 2,
>> 6, 4, 8, 1, 10 });
>> arr ==> jdk.internal.foreign.memory.BoundedArray at 311d617d
>>
>> jshell> qsort.invoke(arr.elementPointer(), 6, 4, up.entryPoint());
>> $13 ==> null
>>
>> jshell> System.err.println(Arrays.toString(arr.toArray(int[]::new)));
>> [1, 2, 4, 6, 8, 10]
>>
>> Cool! We have been able to do all this without using a single
>> annotated interface, which suggests that SystemABI might indeed be
>> general enough to build on top of it.
>>
>> There are, I think, some hiccups in the above snippets which I found 
>> annoying:
>>
>> * having to create a NativeMethodType when I just want to pass the
>> list of layout types leads to more verbose code
>>
>> * we might need NativeTypes.POINTER - doing NativeTypes.VOID.pointer()
>> is an explicit workaround, but a tad verbose
>>
>> * as we already know, the upcallStub API point could use some
>> simplification: omit the receiver argument and directly return the
>> pointer; that will also simplify things
>>
>> Maurizio
>>
>> [1] - 
>> https://cr.openjdk.java.net/~henryjen/panama/SystemABI/webrev.01/webrev/


More information about the panama-dev mailing list