<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>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.</p>
<p>Something similar can be achieved with a simple object store [1]<br>
</p>
<p> static final ObjectStore STORE = new ObjectStore();<br>
<br>
...<br>
<br>
Object objectToPass = ...;<br>
int token = STORE.allocate(objectToPass);<br>
// pass 'token' to native code<br>
<br>
... later:<br>
<br>
int token = ...; // from native code<br>
Object obj = STORE.resolve(token); // turn back into object<br>
<br>
... when done:<br>
<br>
STORE.free(token);<br>
</p>
<p>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.<br>
</p>
<p>HTH,<br>
Jorn<br>
<br>
[1] :<br>
<br>
Simple example of an object store:<br>
</p>
<pre style="background-color:#ffffff;color:#080808;font-family:'JetBrains Mono',monospace;font-size:10.5pt;"><span style="color:#0033b3;">public class </span><span style="color:#000000;">ObjectStore </span>{
<span style="color:#0033b3;">private final </span><span style="color:#000000;">Deque</span><<span style="color:#000000;">Node</span>> <span style="color:#871094;">freeList </span>= <span style="color:#0033b3;">new </span>ArrayDeque<>();
<span style="color:#0033b3;">private final </span><span style="color:#000000;">List</span><<span style="color:#000000;">Node</span>> <span style="color:#871094;">store </span>= <span style="color:#0033b3;">new </span>ArrayList<>();
<span style="color:#0033b3;">public int </span><span style="color:#00627a;">allocate</span>(<span style="color:#000000;">Object </span>obj) {
<span style="color:#0033b3;">if </span>(!<span style="color:#871094;">freeList</span>.isEmpty()) {
<span style="color:#000000;">Node n </span>= <span style="color:#871094;">freeList</span>.pop();
<span style="color:#000000;">n</span>.<span style="color:#871094;">obj </span>= obj;
<span style="color:#0033b3;">return </span><span style="color:#000000;">n</span>.<span style="color:#871094;">index</span>;
}
<span style="color:#0033b3;">int </span><span style="color:#000000;">index </span>= <span style="color:#871094;">store</span>.size();
<span style="color:#871094;">store</span>.add(<span style="color:#0033b3;">new </span>Node(<span style="color:#000000;">index</span>, obj));
<span style="color:#0033b3;">return </span><span style="color:#000000;">index</span>;
}
<span style="color:#0033b3;">public </span><span style="color:#000000;">Object </span><span style="color:#00627a;">resolve</span>(<span style="color:#0033b3;">int </span>token) {
<span style="color:#0033b3;">return </span><span style="color:#871094;">store</span>.get(token).<span style="color:#871094;">obj</span>;
}
<span style="color:#0033b3;">public void </span><span style="color:#00627a;">free</span>(<span style="color:#0033b3;">int </span>token) {
<span style="color:#000000;">Node n </span>= <span style="color:#871094;">store</span>.get(token);
<span style="color:#000000;">n</span>.<span style="color:#871094;">obj </span>= <span style="color:#0033b3;">null</span>;
<span style="color:#871094;">freeList</span>.push(<span style="color:#000000;">n</span>);
}
<span style="color:#0033b3;">private static class </span><span style="color:#000000;">Node </span>{
<span style="color:#0033b3;">final int </span><span style="color:#871094;">index</span>;
<span style="color:#000000;">Object </span><span style="color:#871094;">obj</span>;
<span style="color:#0033b3;">public </span><span style="color:#00627a;">Node</span>(<span style="color:#0033b3;">int </span>index, <span style="color:#000000;">Object </span>obj) {
<span style="color:#0033b3;">this</span>.<span style="color:#871094;">index </span>= index;
<span style="color:#0033b3;">this</span>.<span style="color:#871094;">obj </span>= obj;
}
}
}</pre>
<div class="moz-cite-prefix">On 02/12/2022 18:02, Maurizio
Cimadamore wrote:<br>
</div>
<blockquote type="cite" cite="mid:bd44b8c8-6106-a0b9-e790-6f4fd0622db6@oracle.com">
<br>
On 02/12/2022 16:44, Gregory Klyushnikov wrote:
<br>
<blockquote type="cite">But since you say the decision to not
allow native Java object handles was intentional, I'll try it.
<br>
</blockquote>
<br>
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.
<br>
<br>
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).
<br>
<br>
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.
<br>
<br>
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.
<br>
<br>
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.
<br>
<br>
Now, in some cases, but not all, you can achieve a similar effect
with the new scoped value feature:
<br>
<br>
<a class="moz-txt-link-freetext" href="https://openjdk.org/jeps/429">https://openjdk.org/jeps/429</a>
<br>
<br>
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.
<br>
<br>
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.
<br>
<br>
Anyway, I thought I'd mention the connection, in case it's useful
(if not for this use case, for others).
<br>
<br>
Cheers
<br>
Maurizio
<br>
<br>
<br>
<br>
</blockquote>
</body>
</html>