1

By native I mean written in C++ or C

I'm making a programming language based off Java, in that it has a VM and a language to bytecode compiler.

Implementing the language's features, such as for loops, variables, arithmetic and so on, isn't a problem for me; however, executing native functions like Java can is.

I need the native functions in order to make it possible for programs written in my language to create windows, interface with hardware and the OS, and do just about anything that isn't simple mathematics.

I've heard about the JNI, and it definitely seems like something I'd want, however, I am not sure how to implement something like that.

As my VM is implemented in C++, I know that I could have it #include hpp files of my native functions at compile-time, and then it could dynamically load dll's or so's, however, this doesn't really seem like a good solution because you'd have to recompile the VM every time you'd want it to be able to execute another native function.

The problem comes down to this: how can a C++ program (the VM), dynamically (at runtime, as instructed by bytecode, to be more precise) load libraries with C++ functions, and then execute those functions without them being predeclared in some header file?

4

2 回答 2

1

Look at libffi. It provides methods to call any function given a function address and a calling signature.

How you figure out what that signature should be depends on your context. You can infer a wide range of calls based on argument types. JNA infers native call signatures from explicit Java interfaces, method declarations, or dynamic call arguments.

Going beyond simple function invocations to handle constructors, memory management, and object method dispatching is more complex, but is still based the same basic principles.

于 2012-12-02T23:08:04.927 回答
0

One topic I have not seen discussed is how to pass arguments from your new language VM function calls to the C++ stack, and how to pass the return value from the C++ function back into your VM.

Lets say you want to make the pow(3) function available in your new language. As a reminder, the pow() signature is

double pow ( double base, double power )

The simplest way is something like this

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

But that does not sound like what you are after. Incorporating dlopen() & dlsym() gets your something like

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 );
}

But that is even worse. You still need a stub function per C++ function you want your language to be able to access.

It sounds like you want your language to have something like

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

I do not know how to implement that in C++. Varags supports fetching C++ arguments of any type. But there is no API for pushing C++ arguments of arbitrary type.

于 2012-12-04T01:01:29.470 回答