implementing native methods
nikomatsakis opened this issue · comments
We should support implementing native methods:
#[duchess::provide_native_fn(foo.Bar::baz)]
fn baz(jvm: &mut Jvm<'_>, arg1: Arg1, arg2: Arg2, ...) { }
This would
- generate the user's function unmodified
- find the class
foo.Bar
and, within that, the method namedbaz
(this is leveraging theMethodSelector
added in #60) - verify that it is a native method
- generate the
extern "C" #[no_mangle]
function that the JNI will actually call. This can setup thejvm
and invoke the user's function, invokingto_rust
on each of the argument types or something like that
Some thoughts:
- Invoking
to_rust
isn't exactly right. We'll need some kind of trait for either converting to the Java type or to a Rust type. - We should probably provide the
jvm
so permit local references and the like for better efficiency. We have to then prevent people from doingJvm::with
, presumably by setting some sort of flag or acquiring the lock on our own. - Alternatively we could not provide
jvm
and just have people take&Foo
for local references. They could then usJvm::with
internally and/or work with global references. This might be nicer. We would want some way for the outer function to "relinquish" it's&mut Jvm
during a closure so that, within that closure,Jvm::with
can be called. The idea would roughly be that if duchess is started via a JNI callback, it takes the running JVM and installs it as the global one.
Hmm, I'm surprised to find I like the 2nd one. It lets people write foo.to_rust().execute()
, which I think will be common. It seems a bit more composable.
This will be awesome---I think there's some good use cases for cache callbacks and webserver hooks here.
One implementation note, we should think through how our Jvm::with
lock works in the interleaved case (duchess -> java -> duchess
). I'm not seeing a blocker, but I think we'll need to distinguish a top-of-the-stack duchess frame from the Java-invoked frames.
Yes. I have in mind something like this:
- The "jvm lock" for a thread conceptually can reside either "with the JVM" or in the user's code.
- When you invoke
Jvm::with
, you take the lock. - When you invoke JNI methods, you give it back to the JVM while they execute.
- When it calls you, you get it back.
Given that the JNI methods all take an &mut Jvm
this is how the user will experience it already. I'm not sure though if we have to do anything on our side. I have to re-read the Jvm
launching code, I kind of forget how it works.
Basic support is implemented in #93