jextract generates downcallHandles where I need upcallStubs

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Dec 15 12:45:04 UTC 2020


On 15/12/2020 12:13, Sebastian Stenzel wrote:
> Hi Maurizio,
>
> thank you for the detailed response! What you write makes a lot of 
> sense. I got confused by the fact that for each of the fields of said 
> struct there is an equivalent downcall (!) method defined in fuse.h as 
> well [1]. You're right, of course, that I'm free to ignore any 
> generated upcall stub if I don't need it.
I noticed the downcall later - with quite a similar name, and got 
similarly puzzled by it! But yes, they are different entities.
>
> Regarding the fields in a struct: If I understand you correctly, there 
> is (currently) no way other than to manually create a stub for such 
> methods.
>
> I have no idea how such structs look like for the compiler and if the 
> "type information" for such fields get lost, but is there any chance, 
> jextract can generate a FunctionDescriptor when encountering _any_ 
> function-like expression? This would not only save a lot of time and 
> prevent mistakes, but would fail early at compile time after 
> re-extracting FunctionDescriptors in case of an api-breaking upstream 
> library update.

Aha - now I get the issue - back on my simple example:



```
cat foo.h
struct Foo {
   int (*foo)(float, double);
};
```


While I see that jextrract is generating getter/setters for the struct, 
I don't see the usual functional interface being generated - which would 
allow you to create a callback in a very straightforward fashion (e.g. 
with a lambda expression).

This is, I think, the bit of ingredient that's missing - and I think 
it's a plain bug: while jextract looks at regular function to see if 
their signature mentions a function pointer, the same is not done for 
struct fields.

We'll fix this.

Thanks for bearing with me.

Maurizio


>
> Thanks again!
> Sebastian
>
> [1]: 
> https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L817 
> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L817__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4lBJaiuRI$>
>
>> On 15. Dec 2020, at 12:31, Maurizio Cimadamore 
>> <maurizio.cimadamore at oracle.com 
>> <mailto:maurizio.cimadamore at oracle.com>> wrote:
>>
>> Hi Sebastian,
>> I had a look at the header you mention, and I also tried to extract 
>> it on my machine.
>>
>> What I see in that header is a struct, which defines several function 
>> pointer fields.
>>
>> Let's focus on a simpler example:
>>
>> ```
>> cat foo.h
>> struct Foo {
>>   int (*foo)(float, double);
>> };
>> ```
>>
>> When extracting such an header, jextract will see a struct with one 
>> field - only, that field will have a function pointer type. But it's 
>> still a field - so jextract will generate getter/setters for that 
>> field. You get no downcall at all for this - this is not a function - 
>> there is no shared symbol for it anywhere, so talking about downcall 
>> here is inappropriate.
>>
>> With fuse.h the same thing is happening - and I see a lot of 
>> getters/setters being generated for the various pieces of 
>> functionality declared in the fuse_operations struct - e.g:
>>
>> ```
>> public static  MemoryAddress readdir$get(MemorySegment seg) { ... }
>>
>> public static void readdir$set(MemorySegment seg, MemoryAddress x) { 
>> ... }
>>
>> ```
>>
>> This means that the "readdir" field of fuse_operation can be get/set 
>> using these accessors; to do both get/set you need to pass the memory 
>> segment corresponding to fuse_operation (the first argument in the 
>> accessor). Note that the type to get/set is a MemoryAddress, which is 
>> the readdir function pointer.
>>
>> So, the way this works is (I think):
>>
>> 1) you create a readdir implementation using upcallStub - this gives 
>> you a segment
>> 2) you set the above implementation on fuse_operation.readdir, using 
>> the set accessor above (to turn the segment into an address, just 
>> call the MemorySegment::address method)
>>
>> The same structure seems to be present here:
>>
>> https://github.com/libfuse/libfuse/blob/fuse_2_9_5/example/fusexmp.c 
>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/example/fusexmp.c__;!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4l0Y0tSk0$>
>>
>> Where a struct fuse_operation is allocated and then all its fields 
>> are set to some functions declared in that example. If you want to do 
>> the same using Panama you will have to create all the function 
>> pointers to set on fuse_operations using CLinker::upcallStub.
>>
>> Now, it's true that the function pointer type behind "readdir" takes 
>> its own function pointer (filler), but that shouldn't be a concern 
>> when creating the upcall for (1) - a function pointer is just some 
>> MemoryAddress parameter that the upcall receives (and that the 
>> runtime of the libfuse library will provide). And, it is true that, 
>> in this case jextract will also generate functional interfaces for 
>> e.g. the filler upcall, but you don't have to use them in this case 
>> (since the filler function pointer will never be generated by you - 
>> but by the library) - of course jextract sees a function pointer and 
>> generates the corresponding functional interface, it has no way to 
>> know which party will allocate the function pointer.
>>
>> I hope this helps.
>>
>> Maurizio
>>
>>
>>
>> On 15/12/2020 08:50, Sebastian Stenzel wrote:
>>> Hi all,
>>>
>>> I'm pretty new to this topic and therefore I may misunderstand what 
>>> is going on here, but I'm pretty certain that jextract swaps 
>>> downcalls and upcalls in some scenarios:
>>>
>>> I jextracted [0] libfuse, which mainly consists of the 
>>> fuse_operations struct [1], which contains a set of file system 
>>> operations that need to be implemented by library users. These 
>>> methods are then called by fuse. I.e. this is a typical upcall 
>>> scenario (native code calls java code). Or am I mistaken?
>>>
>>> However, jextract thinks otherwise: While I get correct 
>>> downcallHandles for methods declared on "top level" in the .h file, 
>>> such as fuse_main() [2], everything inside of aforementioned struct 
>>> is reversed. Take for example readdir() [3], which gets called by 
>>> fuse, so the library user can list directory contents. For this 
>>> purpuse, fuse passes a "filler" callback as a parameter to this 
>>> function. But with jextract will generate a downcallHandle for 
>>> readdir and an upcallStub for the filler.
>>>
>>> Tested with build 16-panama+3-385.
>>>
>>> Might it be possible that guessing the "direction" of a call is not 
>>> yet supported for methods inside of structs? Is it even _possible_ 
>>> to always predict this direction correctly? If not, is this too much 
>>> of a restriction and shouldn't it rather be the developers choice 
>>> what to do with a generated FunctionDescriptor?
>>>
>>> Regards,
>>> Sebastian
>>>
>>> [0]: jextract --source -l fuse -d someDir -t com.example.pkg 
>>> -C-D_FILE_OFFSET_BITS=64 -C-DFUSE_USE_VERSION=29 --filter fuse.h 
>>> /usr/local/include/fuse/fuse.h
>>> [1]:https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L88 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L88__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4lwfG-bfs$><https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L88 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L88__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4lwfG-bfs$>>
>>> [2]:https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L766 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L766__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4lkJHXQlQ$><https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L766 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L766__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4lkJHXQlQ$>>
>>> [3]:https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L304 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L304__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4li8C-7DA$><https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h#L304 
>>> <https://urldefense.com/v3/__https://github.com/libfuse/libfuse/blob/fuse_2_9_5/include/fuse.h*L304__;Iw!!GqivPVa7Brio!OLoMSc1W4YZlzJZB1Au71dTyx1Vi2tSn9hC4cFFNxN9lonhyi4U6BpH9LNsOpC4li8C-7DA$>>
>


More information about the panama-dev mailing list