Binding a single function symbol with [foreign]

Jorn Vernee jbvernee at xs4all.nl
Fri Sep 7 12:20:10 UTC 2018


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?

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?

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?

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