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