[foreign] RFC 8219042: Cross-header layout resolution does not work
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Fri Feb 15 15:47:18 UTC 2019
On 15/02/2019 15:15, Jorn Vernee wrote:
> I think we can say that if a user wants to hand write header
> interfaces, then it's also up to them to make sure that all layouts
> can be resolved. I think we could also help them by documenting the
> lookup process carefully.
>
> 1. sounds like a good option to me, but I'm wondering if this approach
> will run into class file limitations for very large libraries (like
> Windows.h)?
Note that this doesn't mean generating only one class - there can be
many _nested_ classes, but only one root. So unless you run out of
methods, I don't think classfile size will be a concern.
>
> Having the ability to manually map names to classes like you suggest
> in 2. seems a bit overkill to me. Maybe as an intermediate solution we
> could change the Unresolved layout descriptors to denote Java
> signatures, instead of layout names. e.g
> '@NativeFunction("${org.package.MyClass::func}")', similarly for classes.
I've been looking at similar ideas in the past - but ended up discarding
them because they end up being messy. I think it's nice to keep the
layouts Java free - and also, stringifying a class has issues - because
then you have to infer which class loader should be used, and that is
also messy.
As for (2), I think there are many intermediate steps we could take -
for instance, instead of explicit name -> class mapping, we could simply
define a list of 'friends' toplevel classes the resolver has to scan.
So, if you are in header A and you refer to something in header B, you
can say @Friend(B.class), for example.
This will still be miles better than scanning all signatures in the hope
for a match.
Maurizio
> Then we could rely on Java resolution mechanisms.
>
> ---
>
> About the patch;
>
> - ScanType still has code to deal with Java arrays, is this still
> needed? I think that's left from before Array was introduced? Maybe
> now is a good moment to remove it.
>
> - LayoutResolver::scanClassInternal also does a check:
>
> if (nameToGroup.containsKey(name) && nameToGroup.get(name) != l) {
> throw new IllegalArgumentException(name + " cannot be
> redefined");
> }
>
> But I believe that should use `!l.equals(nameToGroup.get(name))` since
> `l` is a freshly created Layout Object.
>
> Jorn
>
> Maurizio Cimadamore schreef op 2019-02-15 14:51:
>> Hi,
>> the following patch is inspired from the work that Jorn posted
>> yesterday [1]. But it takes it a step further: instead of having the
>> various code generators call 'scanMethod' and such, all 'indexing'
>> activity is done before hand, when te resolver is first created.
>>
>> Webrev:
>>
>> http://cr.openjdk.java.net/~mcimadamore/panama/8219042/
>>
>> Now, as pointed out, this approach is very ad-hoc; not only it is
>> inefficient (it's doing a full reflective scan of all methods in all
>> classes of an extracted artifact!), but it's also fragile, as it
>> relies on the Java signature to give away some info as to what a
>> layout name might resolve to. Consider the following case:
>>
>> @NativeHeader
>> interface MyHeader {
>>
>> @NativeFunction("()u64:${foo}")
>> Pointer<?> m();
>>
>> }
>>
>> In this case, despite there's an entry point for 'm', the signature
>> info associated with such an entry is simply insufficient, as there's
>> no way to tell which carrier type is associated with the name 'foo'.
>>
>> It seems to me that there are only two stable solutions in this space:
>>
>> 1) assuming jextract generate an artifact with a single root, we could
>> then index all inner classes under that root, to look for structs
>> (this depends on the jextract library-centric work)
>>
>> 2) if we don't want to make assumption on the layout of the extracted
>> interfaces (which seems hard, especially when considering hand-written
>> interfaces), then the only option is to manually define a resolution
>> context:
>>
>>
>> @NativeHeader
>> @NameContext(@NameMapping("foo", Foo.class))
>>
>> interface MyHeader {
>>
>> @NativeFunction("()u64:${foo}")
>> Pointer<?> m();
>>
>> }
>>
>>
>> (name and shape of the annotations TBD).
>>
>> This way, when the binder creates a resolver, it sets up all the
>> necessary mapping from names to layouts. The binder should also check
>> the validity of the mappings (e.g. check that Foo.class is a struct
>> and that it really defines a layout with name 'foo').
>>
>> This way, no guesswork is needed, everything is deterministic and
>> efficient. And this scales to the context of manually written mutually
>> dependent structs - which is probably not an uncommon case when it
>> comes to message protocols (no jextract there).
>>
>> Thoughts?
>>
>> Maurizio
More information about the panama-dev
mailing list