Binding a single function symbol with [foreign]

Maurizio Cimadamore maurizio.cimadamore at
Fri Sep 7 12:59:01 UTC 2018

On 07/09/18 13:20, Jorn Vernee wrote:
> Yes, I think that would work. As an API point, in that case you could 
> add an overload that also takes the `LayoutType` to cast to, and 
> appends a call to `cast` to the bound method handle i.e.:
>     MethodHandle bind(MethodType mt, Function f) {...}
>     MethodHandle bindWithCast(MethodType mt, Function f, LayoutType<?> 
> lt) {
>         MethodHandle bound = bind(mt, f);
>         bound = collectArguments(insertArguments(MH_PointerCast, 1, 
> lt), 0, bound);
>         return bound;
>     }
> And if the overload without the `LayoutType` is used while the method 
> type returns a `Pointer` throw an IAE?
This is a good idea to allow for more user-friendly behavior.
> I guess the `LayoutType` of a returned `Pointer` would be 
> `LayoutType.ofVoid` before the cast? Or is there a generic fall-back 
> carrier type? Maybe a direct `ByteBuffer` that wraps the returned 
> pointer, and uses spill-to-heap for non-pointer returns?
LayoutType.ofVoid is what I was thinking.
> Though the solution I was thinking of yesterday was to use a more 
> informative type instead of `MethodType`, that also carries the 
> generic type information. Something using 
> `java.lang.reflect.ParameterizedType` maybe? In that case the right 
> `LayoutType` for the returned `Pointer` could be immediately derived 
> from that, without the need for the extra cast?
This is probably the wrong path - there's no API to construct 
java.lang.reflect.Type(s) in a succint way, so asking something like 
that to users would feel like a step backwards to me.

> Jorn
> Maurizio Cimadamore schreef op 2018-09-07 09:52:
>> On 06/09/18 23:41, Maurizio Cimadamore wrote:
>>> Of course you can infer a 'default' carrier information from a 
>>> layout, but in the general case you can't - think of structs 
>>> (modeled as classes) and function pointers (modeled as functional 
>>> interfaces) - how do you know whether something is a Foo or a Bar, 
>>> if both Foo and Bar are structs with same fields?
>> Pulling more on this string as I kept thinking about this: adding
>> carrier info will help to distinguish a toplevel Foo from a toplevel
>> Bar - e.g. let's make this more concrete:
>> @NativeStruct("[ i32(get=x) ](foo)")
>> interface Foo extends Struct<Foo> {
>>    int x();
>> }
>> @NativeStruct("[ i32(get=y) ](bar)")
>> interface Bar extends Struct<Bar> {
>>    int y();
>> }
>> These two struct declarations are isomorphic - you can use one in
>> place of another; since in a .so/.dynlib file there's no info about
>> struct names (well in the absence of debugging symbol which I assume
>> to be the norm), if you want to dynamically bind to a native function
>> whose descriptor is:
>> ( [ i32 ])v
>> What should the binder do? E.g. what should be the Java type
>> associated with the argument of this function? Here's where the
>> MethodType is handy - as it could help the inference process
>> understanding whether we want our method handle to accept a Foo or a
>> Bar thingie.
>> But, I realized, this trick doesn't have a lot of mileage: the next
>> example up is with pointers - so let's assume the function descriptor
>> is now changed to:
>> ( u64: [ i32 ] ) v
>> Ok, now we have a pointer to a thingie with one 32-bit signed int
>> inside. Is that Pointer<Foo> or Pointer<Bar> ? This time,
>> unfortunately, the MethodType won't help - class types in method types
>> are erased, so the method type for this will be something like
>> (Pointer)void
>> Which is not enough for our inference process to resolve the ambiguity.
>> I believe the next best thing here is for our process to end up with
>> some Pointer<?> - that is, a pointer whose layout info is composed of
>> the following info:
>> - a layout (namely, "[ i32 ]")
>> - no carrier info
>> Note that, since there's no carrier, attempting to call get() on such
>> a Pointer will fail with exception. Now, since the pointer here is in
>> an argument position, it's not a big deal - e.g. the binder doesn't
>> need to produce one, so we're fine (e.g. the pointer will never be
>> dereferenced). But if the pointer was in a return position, as in:
>> ( ) u64: [ i32 ]
>> This would now be an issue, as the returned Pointer<?> will not
>> support any dereference  operation:
>> Pointer<?> p = mh.invoke();
>> p.get(); //exception!
>> So the right way to go about this would be to cast the result to the
>> right LayoutType - e.g.
>> Pointer<?> p = mh.invoke();
>> p = p.cast(NativeTypes.INT32);
>> int x = p.get(); //ok!
>> In other words, I think the invocation game can always be decomposed
>> into two stages:
>> 1) the first step, works only on erased types and produces types that
>> are correct *up to the generic layer*
>> 2) the second step, is a fixup step that takes info from source
>> signatures (or in other, more explicit ways, as above) and turns
>> generic carriers 'right' (typically with a cast)
>> Back to your original question of providing binding to separate method
>> handle symbol, I now think the answer is yes, *provided* that what you
>> mean is (1) - e.g. that you are willing to make up for losses of
>> generic type info resulting from the lack of source information that
>> directs the binding process.
>> Does this help?
>> Cheers
>> Maurizio

More information about the panama-dev mailing list