Commit 4db0d49
committed
Implement bind_class macro under examples/bind-class-test
This implements a `bind_class!()` macro via `macro_rules` that can be
used like:
```
bind_class! {
"foo.bar.Baz" as JTest2 {
api = CustomJTest2API,
raw = jthrowable,
as = [ string = JString, JObject ],
native_trait = CustomJTest2Natives,
native_trait_impl = CustomJTest2NativesImpl,
native_error_policy = ThrowRuntimeExAndDefault,
priv = JTest2Priv,
impl = {
load_class = |env, load_context, initialize| {
load_context.load_class_for_type::<JTest2>(initialize, env)
},
init_priv = |env, class, load_context | {
println!("Priv init called for JTest2");
Ok(JTest2Priv)
}
},
constructors = {
constructor1 = { sig = (), vis = pub(self) },
constructor2 = { sig = (a: jint) },
},
static_native_methods = {
static_native_method1 = { name = "nativeStaticFoo", sig = (arg0: java.lang.String) -> jint },
},
native_methods = {
native_method1 = { sig = (arg0: java.lang.String) -> jint },
native_method2 = {
sig = (arg0: java.lang.String) -> jint,
//fn = my_native_method2_impl,
error_policy = LogErrorAndDefault,
vis = pub
},
},
static_methods = {
static_method1 = { name = "createInstance", sig = () -> JString },
},
methods = {
method1 = { name = "getValue", sig = () -> ([[jint]]) },
method2 = { sig = (a: jint, b: java.lang.String as JString, c: jboolean) -> JString },
},
static_fields = {
static_field1 = { sig = (java.lang.String) },
},
fields = {
field1 = { name = "value", sig = jint },
field_name = { sig = JString },
},
}
}
```
Apart from some issues with handling the mangling of native methods
(which is tricky to manage within a macro_rules macro) this all works
but _sheesh_ it was pretty complex to get this working, with lots of
backtracking to work within the limitations of macro_rules.
Some of the challenges:
We can't heavily rely on tt-munching without hitting recursion limits,
so this has a verbose workaround for finding properties without
recursion.
In general the lack of tail call optimisation for tt munching in
macro_rules really constrains the syntax that can practically be
supported by the macro.
The lazy expansion of sub-macros (where you have to ensure that code
is valid at each level of expansion) is difficult to manage and leads to
awkward auto-complete expansions with rust-analyzer (if you ask it to
fill out a blank implementation of the trait for native methods then it
will contain various macro calls that are supposed to be hidden.
Not being able to decompose metavars back into tokens is awkward to
manage, while code is forced to keep everything as token trees as long
as possible and only bind into a metavariable during later codegen
steps.
In the end I think this is too complex to use in practice but I guess
I learned a bunch about abusing Rust macro_rules.
The thing that kind of broke the camels back is that I wanted to add
support for doc attributes, which would have blown up the complexity
for accessing properties without recursion.
TODO: Re-write as a proc macro instead!1 parent 3dc5f79 commit 4db0d49
File tree
5 files changed
+4141
-0
lines changed- examples/bind-class-test
5 files changed
+4141
-0
lines changed
0 commit comments