jextract generation for functional interfaces
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Mon Apr 7 15:09:12 UTC 2025
I like this. The use of record components should probably preserve the
fact that the upcall method handle is treated as a constant.
This seems related to this PR under review:
https://git.openjdk.org/jextract/pull/279
So we should make sure to decide what code we want to generate (as that
PR will make function pointer support even more verbose).
Cheers
Maurizio
On 07/04/2025 13:42, Duncan Gittins wrote:
> I've been using jextract successfully for some time now and it has
> made it very easy to call into various Windows. Great work.
>
> One area which seems a bit verbose is the handling of the functional
> interface callbacks for upcalls. For example, the qsort compare
> callback is as follows (I've shortened the jextract version here):
>
> publicclassQsortCompareFunc {
>
> QsortCompareFunc() { }
>
> /** The function pointer signature, expressed as a functional interface */
>
> publicinterfaceFunction {
>
> intapply(MemorySegment _x0, MemorySegment _x1);
>
> }
>
> privatestaticfinalFunctionDescriptor $DESC= FunctionDescriptor.of(
>
> C_h.C_INT,
>
> C_h.C_POINTER,
>
> C_h.C_POINTER
>
> );
>
> /** The descriptor of this function pointer */
>
> publicstaticFunctionDescriptor descriptor() {
>
> return$DESC;
>
> }
>
> privatestaticfinalMethodHandle UP$MH=
> C_h.upcallHandle(QsortCompareFunc.Function.class, "apply", $DESC);
>
> /**
>
> * Allocates a new upcall stub, whose implementation is defined by
> {@code fi}.
>
> * The lifetime of the returned segment is managed by {@code arena}
>
> */
>
> publicstaticMemorySegment allocate(QsortCompareFunc.Function fi, Arena
> arena) {
>
> returnLinker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena);
>
> }
>
> privatestaticfinalMethodHandle DOWN$MH=
> Linker.nativeLinker().downcallHandle($DESC);
>
> /**
>
> * Invoke the upcall stub {@code funcPtr}, with given parameters
>
> */
>
> publicstaticintinvoke(MemorySegment funcPtr,MemorySegment _x0,
> MemorySegment _x1) {
>
> try{
>
> return(int) DOWN$MH.invokeExact(funcPtr, _x0, _x1);
>
> } catch(Throwable ex$) {
>
> thrownewAssertionError("should not reach here", ex$);
>
> }
>
> }
>
> }
>
>
> I made a few changes in jextract to make the callbacks generate
> interface rather than a class, adding a helper record Upcall to do
> most of the actions:
>
> publicinterfaceQsortCompareFunc {
>
> /** The function pointer signature, expressed as a functional interface */
>
> intapply(MemorySegment _x0, MemorySegment _x1);
>
> staticfinalC_h.Upcall<QsortCompareFunc> UPCALL=
> newC_h.Upcall<>(QsortCompareFunc.class, "apply", FunctionDescriptor.of(
>
> C_h.C_INT,
>
> C_h.C_POINTER,
>
> C_h.C_POINTER
>
> ));
>
> // A new upcall stub is allocated by:
>
> // MemorySegment funcPtr = QsortCompareFunc.UPCALL.allocate(fi,arena)
>
> /**
>
> * Invoke the upcall stub {@code funcPtr}, with given parameters
>
> */
>
> publicstaticintinvoke(MemorySegment funcPtr,MemorySegment _x0,
> MemorySegment _x1) {
>
> try{
>
> return(int) UPCALL.downcall().invokeExact(funcPtr, _x0, _x1);
>
> } catch(Throwable ex$) {
>
> thrownewAssertionError("should not reach here", ex$);
>
> }
>
> }
>
> }
>
>
> This reduces the definition somewhat, as long as the top level header
> has a record to handle all callback interfaces:
>
> publicrecordUpcall<FunctionInterface>(FunctionDescriptor descriptor,
> MethodHandle upcall, MethodHandle downcall) {
>
> publicUpcall(Class<FunctionInterface> fi, String name,
> FunctionDescriptor descriptor) {
>
> this(descriptor, upcallHandle(fi, name, descriptor),
> Linker.nativeLinker().downcallHandle(descriptor));
>
> }
>
> /** Find Java method handle of interface for upcall from native ->Java */
>
> publicstaticMethodHandle upcallHandle(Class<?> fi, String name,
> FunctionDescriptor fdesc) {
>
> try{
>
> returnMethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType());
>
> } catch(ReflectiveOperationException ex) {
>
> thrownewAssertionError(ex);
>
> }
>
> }
>
> /// Allocate java stub for upcall
>
> publicMemorySegment allocate(FunctionInterface javaImpl, Arena scope) {
>
> returnLinker.nativeLinker().upcallStub(upcall.bindTo(javaImpl),
> descriptor, scope);
>
> }
>
> }
>
> Worth considering?
>
> Duncan
>
>
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20250407/16edc8be/attachment-0001.htm>
More information about the panama-dev
mailing list