Conversion between Java objects and native pointers

Jorn Vernee jorn.vernee at oracle.com
Sat Dec 3 17:59:03 UTC 2022


Note that in practice JNI also doesn't really allow you to pass objects 
to native code, what it actually passes is object handles. Semantically 
this is just a token that can be turned into an object again by the JNI 
runtime later.

Something similar can be achieved with a simple object store [1]

     static final ObjectStore STORE = new ObjectStore();

...

     Object objectToPass = ...;
     int token = STORE.allocate(objectToPass);
     // pass  'token' to native code

... later:

     int token = ...; // from native code
     Object obj = STORE.resolve(token); // turn back into object

... when done:

     STORE.free(token);

Of course this is a simple example, but this is the basic principle: 
store your object in some store, and have a handle/token that you pass 
to native code instead.

HTH,
Jorn

[1] :

Simple example of an object store:

public class ObjectStore {
     
     private final Deque<Node> freeList =new ArrayDeque<>();
     private final List<Node> store =new ArrayList<>();
     
     public int allocate(Object obj) {
         if (!freeList.isEmpty()) {
             Node n =freeList.pop();
             n.obj = obj;
             return n.index;
         }
         
         int index =store.size();
         store.add(new Node(index, obj));
         return index;
     }
     
     public Object resolve(int token) {
         return store.get(token).obj;
     }
     
     public void free(int token) {
         Node n =store.get(token);
         n.obj =null;
         freeList.push(n);
     }
     
     private static class Node {
         final int index;
         Object obj;

         public Node(int index,Object obj) {
             this.index = index;
             this.obj = obj;
         }
     }
}

On 02/12/2022 18:02, Maurizio Cimadamore wrote:
>
> On 02/12/2022 16:44, Gregory Klyushnikov wrote:
>> But since you say the decision to not allow native Java object 
>> handles was intentional, I'll try it.
>
> Some background: if we start allowing Java instances being passed to 
> native function, then we have to start worrying about the same class 
> "spoofing" issues that JNI suffers from.
>
> That is, you can crete an handle for Foo in one class loader, then the 
> handle is stored by the native code, and passed again to Java code in 
> _another_ class loader, thus violating classloader constraints (maybe 
> the class definition for Foo in that second class loader is different).
>
> For these reasons, JNI restricted library loading to a _single_ class 
> loader. This restriction solves the spoofing issues, but comes at a 
> price: any attempt to load same library from two loaders will fail. 
> Frameworks have in the past resorted to various "tricks" (such as 
> renaming libraries) to workaround this limitation.
>
> Since Panama doesn't allow passing Java objects _directly_ to native 
> functions (you can pass memory segments, but they are either passed by 
> reference - a long - or deconstructed into multiple primitive values 
> which are then passed separately), it is not subject to same library 
> loading limitations. That is, if you load a library with 
> SymbolLookup::libraryLookup you can load it as many times as you like, 
> from _any_ classloader you like. Which is nice to have.
>
> Of course I understand where you are coming from with this use case: 
> you want to store an object pointer in the native code. We have 
> considered APIs to allow developers to do that (e.g. temporarily turn 
> a Java object into a native memory segment, controlled by a given 
> memory session). But, as shown above, there are some safety 
> consideration when going down that path.
>
> Now, in some cases, but not all, you can achieve a similar effect with 
> the new scoped value feature:
>
> https://openjdk.org/jeps/429
>
> E.g. you could "bind" a ScopedValue object before the native call 
> takes place, so that the receiver class is set to some known instance. 
> The method handle you upcall to would read the ScopedValue, fetch the 
> receiver instance, and dispatch to that.
>
> This would be more efficient than using a Map (as ScopedValues have 
> caches) - but your usage has to be "structured" for ScopedValue to be 
> usable.
>
> Anyway, I thought I'd mention the connection, in case it's useful (if 
> not for this use case, for others).
>
> Cheers
> Maurizio
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20221203/14e7060b/attachment-0001.htm>


More information about the panama-dev mailing list