如何从VM调用本机函数

How to call native functions from a VM?

本文关键字:本机 函数 调用 VM      更新时间:2023-10-16

我所说的原生语言是指用C++或C编写的

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

实现该语言的特性,如for循环、变量、算术等等,对我来说不是问题;然而,执行像Java这样的本机函数是可行的。

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

我听说过JNI,它看起来肯定是我想要的,然而,我不确定如何实现这样的东西。

由于我的虚拟机是用C++实现的,我知道我可以在编译时让它成为我本机函数的#includehpp文件,然后它可以动态加载dllso,然而,这似乎不是一个好的解决方案,因为每次你希望它能够执行另一个本机函数时,你都必须重新编译虚拟机。

问题归结为:C++程序(VM)如何动态地(更准确地说,按照字节码的指示,在运行时)加载带有C++函数的库,然后在没有在某些头文件中预先声明这些函数的情况下执行这些函数?

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

你如何弄清楚签名应该是什么取决于你的上下文。您可以根据参数类型推断出各种各样的调用。JNA从显式Java接口、方法声明或动态调用参数推断本机调用签名。

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

我没有讨论过的一个主题是如何将新语言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++参数。但是没有API可以推送任意类型的C++参数。