JDK-18 Foreign Functions : 如何调用非静态函数?

JDK-18 Foreign Functions : How to upCall non-static functions?

我一直在研究 JDK-18 中的 jdk.incubator.foreign 内容。这很不错。比 JNI 快得多。快一个数量级。外部内存的东西比 UNSAFE 的东西更好(也许稍微快一点)。等不及发货了。

有一件事我想不通:如何 upCall 到非静态 JVM 函数?

如果我不能,我如何传递一些上下文来回传递,以便我可以将上下文转换为 java 静态函数中的正确 instance/type?在 java 中,如何从 this 创建一个 ValueLayout.ADDRESS?反之亦然?

我能弄清楚如何做到这一点的唯一方法是保留实例化 classes 的列表,并在列表中往返索引。这似乎是一个黑客。

也许 JVM 保留随时不受限制地移动 JVM 内存的权利,所以也许这是不可能的?如果是这样,推荐的模式是什么?

---编辑-添加代码示例---

public class RoundTripExample {

    private String name;
    RoundTripExample(String _name) {
        this.name = _name;
    }

    public void callback() {
        System.out.println("Called from native"+name);
    }

    public static void main(String[] args) throws Throwable {
        var fName = new File("../zig/zig-out/lib/libnativeFuncs.so").getCanonicalFile();
        System.load(fName.toString());

        var instance =new RoundTripExample("round trip name");

        CLinker link = CLinker.systemCLinker();

        SymbolLookup symLook = SymbolLookup.loaderLookup();

        ResourceScope scope = ResourceScope.newSharedScope();

        var nativeFunc = link.downcallHandle(
                symLook.lookup("zigCallTest").get(),
                FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS)
        );
        var handle =
                MethodHandles.lookup().findVirtual(
                        RoundTripExample.class,
                        "callback",
                        MethodType.methodType(void.class)
                );
        handle.bindTo(instance);
        var upcall = link.upcallStub(
                handle,
                FunctionDescriptor.ofVoid(ValueLayout.ADDRESS),
                ZigStubs.scope
        );

        nativeFunc.invoke(upcall);

    }
}

假设这是方法,问题是创建 upCall 句柄。您只能将句柄传递给采用 LONG、INT、ADDRESS 等的函数。class 的实例不是其中任何一个。

如果您需要传递的上下文对于目标函数来说是常量,您可以使用 MethodHandles.insertArguments 将其绑定到目标方法句柄,然后使用生成的方法句柄创建上调存根。

如果目标函数的上下文不是常量,那么使用列表并将索引传递给所需对象的想法是一个很好的方法。这本质上是一种将对象转换为整数的方法,方法是将其插入列表,然后通过再次在列表中查找返回对象。

没有安全的方法可以将对象转换为纯本机地址,因为正如您所说,对象可能会被 JVM 移动。


在示例中,使用 bindTo 看起来可行。在这种情况下,您忘记分配 bindTo:

的结果
handle = handle.bindTo(instance);

此外,您必须删除从本机代码传递的地址参数,因为您的 Java 代码似乎没有使用它:

handle = MethodHandles.dropArguments(handle, 0, MemoryAddress.class);
var upcall = link.upcallStub(
    handle,
    FunctionDescriptor.ofVoid(ValueLayout.ADDRESS),
    ZigStubs.scope
);