如何从VM调用本机函数?

时间:2012-12-02 17:39:48

标签: java c++ java-native-interface shared-libraries

原生我的意思是用C ++或C

编写

我正在编写一种基于Java的编程语言,因为它有一个VM和一个字节码编译器的语言。

实现语言的功能,例如for循环,变量,算术等,对我来说不是问题;但是,执行像Java这样的本机函数是。

我需要原生函数,以便用我的语言编写的程序可以创建窗口,与硬件和操作系统接口,并做任何非简单数学的事情。

我听说过JNI,这看起来肯定是我想要的东西,但是,我不知道如何实现这样的东西。

由于我的VM是用C ++实现的,我知道在编译时我可以拥有本地函数的h #include hpp文件,然后它可以动态加载dll或{{然而,这并不是一个好的解决方案,因为每次你希望它能够执行另一个本机函数时你必须重新编译VM。

问题归结为:C ++程序(VM)如何动态地(在运行时,如字节码所指示的那样更加精确)使用C ++函数加载库,然后在不预先声明它们的情况下执行这些函数在一些头文件?

2 个答案:

答案 0 :(得分:1)

看看libffi。它提供了在给定函数地址和调用签名的情况下调用任何函数的方法。

如何确定签名应该取决于您的上下文。您可以根据参数类型推断出各种调用。 JNA从显式Java接口,方法声明或动态调用参数中推断出本机调用签名。

超越简单的函数调用来处理构造函数,内存管理和对象方法调度更复杂,但仍然基于相同的基本原则。

答案 1 :(得分:0)

我未讨论的一个主题是如何将新语言VM函数调用中的参数传递给C ++堆栈,以及如何将返回值从C ++函数传递回VM。

假设您希望以新语言提供pow(3)功能。 提醒一下,pow()签名是

double pow ( double base, double power )

最简单的方法是这样的

void
language::pow( VM * pVM )
{
    double arg2 = pVM->PopDouble();
    double arg1 = pVM->PopDouble();
    double result = pow( arg1, arg2 );
    pVM->PushDouble( result );
}

但这听起来并不像你追求的那样。合并dlopen()& dlsym()得到你喜欢的东西

void
language::pow( VM * pVM )
{
    double arg2 = pVM->PopDouble();
    double arg1 = pVM->PopDouble();
    void *handle = dlopen("libm", RTLD_LAZY);
    if (!handle) { /*...return; ...*/ }
    typedef double (* pfPow ) ( double, double );
    pfPow pPow = (pfPow) dlsym(handle, "pow");
    if (!pPow) { /*...return; ...*/ }
    double result = (* pPow )( arg1, arg2 );
    pVM->PushDouble( result );
}

但情况更糟。您仍希望语言能够访问每个C ++函数的存根函数。

听起来你希望你的语言有类似

的东西
double result = eval_double( "libm", "pow", arg1, arg2 );

我不知道如何在C ++中实现它。 Varags支持获取任何类型的C ++参数。但是没有用于推送任意类型的C ++参数的API。