use prebindgen_registry::{types_util::result_ok_type, Conversions};
use super::*;
use crate::jni::trait_impl::{
build_through_erased_wrappers, build_through_wrappers, read_through_erased_wrappers,
};
pub(crate) fn emit_jni_function_wrapper(
ext: &Declarations,
f: &prebindgen_registry::flat::Function,
registry: &Registry<KotlinMeta>,
emit: &prebindgen_registry::Emit,
) -> TokenStream {
emit_jni_function_wrapper_with_callee(ext, f, registry, None, emit)
}
pub(crate) fn const_getter_fn(
c: &prebindgen_registry::flat::Constant,
) -> prebindgen_registry::flat::Function {
let ident = format_ident!("const_get_{}", c.name.to_string().to_lowercase());
prebindgen_registry::flat::Function::synthetic_getter(ident, c.ty.clone())
}
pub(crate) fn reject_handle_const(ext: &Declarations, c: &prebindgen_registry::flat::Constant) {
reject_handle_key(
ext,
&crate::util::head_type(&c.ty).key(),
"const",
&c.name.to_string(),
);
}
pub(crate) fn reject_handle_constant_type(
ext: &Declarations,
ty: &prebindgen_registry::flat::TypeRef,
what: &str,
name: &str,
) {
let mut ty = ty;
loop {
if let Some(inner) = ty
.borrow_target()
.or_else(|| ty.optional_inner())
.or_else(|| ty.sequence_elem())
{
ty = inner;
continue;
}
break;
}
reject_handle_key(ext, &ty.key(), what, name);
}
fn reject_handle_key(ext: &Declarations, key: &TypeKey, what: &str, name: &str) {
let is_handle = ext.types.get(key).is_some_and(|cfg| cfg.is_opaque());
assert!(
!is_handle,
"{what} `{name}`: type `{}` is a declared opaque handle — a shared closeable Kotlin `val` is \
not supported. Expose a `#[prebindgen]` factory function returning the constant and \
declare it as a companion constructor instead.",
key.as_str()
);
}
pub(crate) fn validate_constant_fn(ext: &Declarations, f: &prebindgen_registry::flat::Function) {
assert!(
f.params.is_empty(),
"constant fn `{}`: takes {} parameter(s) — a function-backed constant must be nullary \
(declare it with `.fun(...)` instead if it is a real function)",
f.name,
f.params.len()
);
assert!(
f.ret.fallible_parts().is_none(),
"constant fn `{}`: returns a `Result` — a function-backed constant must be \
infallible (declare it with `.fun(...)` instead if it can fail)",
f.name
);
reject_handle_constant_type(ext, &f.ret, "constant fn", &f.name.to_string());
}
pub(crate) fn const_expr_getter_fn(
kotlin_name: &str,
ty: &syn::Type,
registry: &impl Conversions<KotlinMeta>,
) -> prebindgen_registry::flat::Function {
let ident = format_ident!("const_get_{}", kotlin_name.to_lowercase());
let ret = registry.reading_of(ty).unwrap_or_else(|| {
panic!(
"constant_expr `{kotlin_name}`: type `{}` is not a type this binding crosses — \
declare it, or name one that is",
quote::ToTokens::to_token_stream(ty),
)
});
prebindgen_registry::flat::Function::synthetic_getter(ident, ret)
}
pub(crate) fn validate_constant_expr(ext: &Declarations, kotlin_name: &str, ty: &syn::Type) {
assert!(
result_ok_type(ty).is_none(),
"constant expr `{kotlin_name}`: type is a `Result` — an expression constant must be \
infallible (declare a real function with `.fun(...)` instead if it can fail)"
);
let mut ty = ty.clone();
loop {
if let syn::Type::Reference(r) = &ty {
ty = (*r.elem).clone();
continue;
}
if let Some(inner) = option_inner_type(&ty).or_else(|| vec_inner_type(&ty)) {
ty = inner;
continue;
}
break;
}
reject_handle_key(ext, &TypeKey::from_type(&ty), "constant expr", kotlin_name);
}
pub(crate) fn emit_jni_function_wrapper_with_callee(
ext: &Declarations,
f: &prebindgen_registry::flat::Function,
registry: &Registry<KotlinMeta>,
callee: Option<syn::Expr>,
emit: &prebindgen_registry::Emit,
) -> TokenStream {
let original_ident = &f.name;
let mut wire_params: Vec<TokenStream> = Vec::new();
let mut prelude: Vec<TokenStream> = Vec::new();
let mut call_args: Vec<TokenStream> = Vec::new();
let plan = ext
.fn_plan(registry, f)
.unwrap_or_else(|e| panic!("{}", e.message(original_ident)));
let wrapper_ident = syn::Ident::new(&plan.native_symbol, Span::call_site());
let unfold_plan = registry.unfold_plans().get(original_ident);
let error_plan = registry.error_plans().get(original_ident);
let is_convert = matches!(&plan.output, FnOutputPlan::Value(v) if v.is_convert);
let output_entry = match &plan.output {
FnOutputPlan::Value(v) => Some(
registry
.output_entry(&v.target_ty)
.expect("output entry validated at plan build"),
),
FnOutputPlan::Unfold(_) => None,
};
let wire_ty = plan.output.wire_ty();
let wire_return = annotate_jobject_with_lifetime(&wire_ty, "a").to_token_stream();
let on_err = sentinel_for_wire(&wire_ty);
for param in &plan.params {
let (wp, pre, call_arg) =
emit_input_param(ext, registry, original_ident, param, &on_err, emit);
wire_params.extend(wp);
prelude.extend(pre);
call_args.push(call_arg);
}
let raw_call = match &callee {
Some(e) => quote!(#e),
None => {
let call_module = ext.fn_module(registry, original_ident);
quote!(#call_module::#original_ident(#(#call_args),*))
}
};
let call_expr: TokenStream = if is_convert {
use prebindgen_registry::unfold::UnfoldShape;
let uplan = unfold_plan.expect("is_convert ⇒ plan");
let leaf = &uplan.leaves[0];
let by_ref = uplan.by_ref;
let qualify = |id: &syn::Ident| -> syn::Path { ext.fn_module(registry, id) };
let compose = |base: TokenStream, base_is_ref: bool| -> Option<TokenStream> {
let hoisted = bind_hoists(&qualify, &uplan.hoists, &base, base_is_ref);
let stmts = &hoisted.stmts;
let reached = match hoisted.rebase(&leaf.path) {
Some((local, rest, consuming)) => {
reach_leaf_flat(&qualify, leaf, &rest, quote!(#local), false, consuming)
}
None => {
reach_leaf_flat(&qualify, leaf, &leaf.path, base.clone(), base_is_ref, false)
}
};
if stmts.is_empty() && reached.to_string() == base.to_string() {
return None;
}
Some(quote!({ #stmts #reached }))
};
match &uplan.shape {
UnfoldShape::Optional((), _) => match compose(quote!(__inner), by_ref) {
Some(inner) => quote!({
let __cvsrc = #raw_call;
__cvsrc.map(|__inner| #inner)
}),
None => raw_call.clone(),
},
_ => match compose(quote!(__cvsrc), by_ref) {
Some(v) => quote!({
let __cvsrc = #raw_call;
#v
}),
None => raw_call.clone(),
},
}
} else if let Some(ep) = error_plan {
let eze_idents: Vec<syn::Ident> = (0..ep.leaves.len())
.map(|i| format_ident!("__eze{}", i))
.collect();
let ze_fail = |msg: TokenStream| -> TokenStream {
quote! {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &#msg);
return #on_err;
}
};
let (ze_stmts, ze_args) = encode_plan_leaves(
ext,
registry,
ep,
&eze_idents,
"e!(__de),
&ze_fail,
emit,
);
quote! {
match #raw_call {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__de) => {
#ze_stmts
signal_domain_error(
&mut env, &__domain_sink,
&__DSINK_MID, __DSINK_FQN, __DSINK_DESCR,
&[#(#ze_args),*],
);
return #on_err;
}
}
}
} else {
raw_call
};
let mut builder_param: Option<TokenStream> = None;
let output_phase: TokenStream = if let FnOutputPlan::Unfold(u) = &plan.output {
let uplan = unfold_plan.expect("Unfold output ⇒ unfold plan present");
builder_param = Some(unfold_builder_param(u.iterable_fold));
let delivered = match error_plan {
Some(_) => f.ret.fallible_parts().map_or(&f.ret, |(ok, _)| ok),
None => &f.ret,
};
let call_expr =
read_through_erased_wrappers(delivered, call_expr.clone()).unwrap_or_else(|| {
panic!(
"`{original_ident}` returns `{}`, whose leaves are delivered to a builder: \
the value has to be moved out of `{}` to be decomposed, and that wrapper \
does not permit it (a `Cow` payload cannot be moved through `Deref`). \
Reserved rather than refused for good: rebuilding through every \
transparent wrapper is #292 item 3 — until then, spell the return \
without it.",
delivered,
delivered.erased_wrappers().join("<"),
)
});
emit_unfold_delivery(
ext,
registry,
uplan,
u.iface.as_deref(),
&call_expr,
&on_err,
emit,
)
} else {
let output_entry = output_entry.expect("normal path has an output entry");
let mut phase: TokenStream = quote! { let __out = #call_expr; };
let mut prev_out: TokenStream = quote!(__out);
for (i, stage) in output_entry.output_stage_order() {
let stage_fn = &stage.function.sig.ident;
let next_ident = format_ident!("__out_s{}", i);
phase.extend(quote! {
let #next_ident = match #stage_fn(&mut env, #prev_out) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
});
prev_out = quote!(#next_ident);
}
let conv_out = output_entry.converter_ident().clone();
phase.extend(quote! {
match #conv_out(&mut env, #prev_out) {
::core::result::Result::Ok(__w) => __w,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
#on_err
}
}
});
phase
};
let error_ifaces = plan.onerror_iface.as_ref().unwrap_or_else(|| {
panic!(
"jnigen: cannot derive the onError handler interface for `{}`",
original_ident
)
});
let bsink_fqn_lit = syn::LitStr::new(&error_ifaces.binding.raw_slash_fqn(), Span::call_site());
let bsink_descr_lit = syn::LitStr::new(&error_ifaces.binding.descr, Span::call_site());
let (domain_setup, domain_sink_param) = match &error_ifaces.domain {
Some(dsink) => {
let dfqn = syn::LitStr::new(&dsink.raw_slash_fqn(), Span::call_site());
let ddescr = syn::LitStr::new(&dsink.descr, Span::call_site());
(
quote! {
#[allow(non_upper_case_globals)]
static __DSINK_MID: ::prebindgen_jni_runtime::CachedIfaceMethod =
::prebindgen_jni_runtime::CachedIfaceMethod::new();
const __DSINK_FQN: &str = #dfqn;
const __DSINK_DESCR: &str = #ddescr;
},
quote!(__domain_sink: jni::objects::JObject<'a>,),
)
}
None => (quote!(), quote!()),
};
let sinks_setup = quote! {
#[allow(non_upper_case_globals)]
static __SINK_MID: ::prebindgen_jni_runtime::CachedIfaceMethod =
::prebindgen_jni_runtime::CachedIfaceMethod::new();
const __SINK_FQN: &str = #bsink_fqn_lit;
const __SINK_DESCR: &str = #bsink_descr_lit;
#domain_setup
};
quote! {
#[no_mangle]
#[allow(non_snake_case, unused_mut, unused_variables, dead_code)]
pub unsafe extern "C" fn #wrapper_ident<'a>(
mut env: jni::JNIEnv<'a>,
_class: jni::objects::JClass<'a>,
#(#wire_params,)*
#builder_param
__error_sink: jni::objects::JObject<'a>,
#domain_sink_param
) -> #wire_return {
#sinks_setup
#(#prelude)*
#output_phase
}
}
}
fn unfold_builder_param(iterable_fold: bool) -> TokenStream {
if iterable_fold {
quote!(__acc: jni::objects::JObject<'a>, __fold: jni::objects::JObject<'a>,)
} else {
quote!(__builder: jni::objects::JObject<'a>,)
}
}
#[allow(clippy::type_complexity)]
fn emit_input_param(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
original_ident: &syn::Ident,
param: &PlanParam,
on_err: &TokenStream,
emit: &prebindgen_registry::Emit,
) -> (Vec<TokenStream>, Vec<TokenStream>, TokenStream) {
let leaf = match ¶m.form {
ParamForm::Expanded(leaves) => {
let fold = registry
.expansion_plans()
.get(&(original_ident.clone(), param.ident.clone()))
.expect("ParamForm::Expanded ⇒ expansion plan present");
return emit_expanded_param(ext, registry, fold, leaves, ¶m.ident, on_err, emit);
}
ParamForm::Single(leaf) => &**leaf,
};
let arg_ident = ¶m.ident;
let arg_ty = ¶m.ty;
let mut wire_params: Vec<TokenStream> = Vec::new();
let mut prelude: Vec<TokenStream> = Vec::new();
match &leaf.kind {
InputKind::FlattenStruct(plan) => {
for leaf in &plan.leaves {
let pid = &leaf.native_ident;
let pty = &leaf.native_wire_ty;
wire_params.push(quote!(#pid: #pty));
}
let (decode, call_arg) = render_flat_input_decode(plan, arg_ident, on_err, emit);
prelude.push(decode);
(wire_params, prelude, call_arg)
}
InputKind::OptionScalar(sp) => {
let pid = &sp.present_ident;
let vid = &sp.value_ident;
let vwire = &sp.value_wire;
wire_params.push(quote!(#pid: jni::sys::jboolean));
wire_params.push(quote!(#vid: #vwire));
let conv = &sp.inner_conv;
let tmp = format_ident!("__{}_val", arg_ident);
let built = build_through_wrappers(
&sp.arg_wrappers,
quote! {
if #pid != 0u8 {
let #tmp = match #conv(&mut env, &#vid) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
::core::option::Option::Some(#tmp)
} else {
::core::option::Option::None
}
},
)
.expect("an option-scalar plan is built only for a buildable spelling");
prelude.push(quote! {
let #arg_ident = #built;
});
(wire_params, prelude, quote!(#arg_ident))
}
InputKind::VecBuild {
elem,
by_ref,
elem_wrappers,
} => {
let elem = emit.spell(elem);
let handle_ident = format_ident!("{}_handle", arg_ident);
wire_params.push(quote!(#handle_ident: jni::sys::jlong));
if *by_ref {
prelude.push(quote!(
let #arg_ident = unsafe { &*(#handle_ident as *const Vec<#elem>) };
));
} else {
let taken = quote!(unsafe {
::core::mem::take(&mut *(#handle_ident as *mut Vec<#elem>))
});
let taken = if elem_wrappers.is_empty() {
taken
} else {
let wrapped = build_through_wrappers(elem_wrappers, quote!(__e))
.expect("vec_build_elem accepted this element spelling");
quote!(
#taken
.into_iter()
.map(|__e| #wrapped)
.collect::<Vec<_>>()
)
};
let taken = build_through_erased_wrappers(&leaf.reading, taken)
.expect("vec_build_elem accepted this run spelling");
prelude.push(quote!(let #arg_ident = #taken;));
}
(wire_params, prelude, quote!(#arg_ident))
}
InputKind::Handle { direct: true }
if !matches!(
arg_ty.kind(),
prebindgen_registry::flat::TypeKind::Ref { .. }
) =>
{
let entry = registry
.input_entry(arg_ty)
.expect("plan classified Handle ⇒ entry present");
let wire_ident = if matches!(&entry.destination, syn::Type::Ptr(_)) {
format_ident!("{}_ptr", arg_ident)
} else {
arg_ident.clone()
};
wire_params.push(quote!(#wire_ident: jni::sys::jlong));
let arg_ty = emit.spell(arg_ty);
prelude.push(quote!(
if #wire_ident == 0 || (#wire_ident & 1) == 1 {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, "Operation on a closed native handle.");
return #on_err;
}
let #arg_ident: #arg_ty = unsafe {
*std::boxed::Box::from_raw(#wire_ident as *mut #arg_ty)
};
));
(wire_params, prelude, quote!(#arg_ident))
}
InputKind::Callback { .. }
| InputKind::Handle { .. }
| InputKind::Unsigned64 { .. }
| InputKind::Plain => {
let entry = registry.input_entry(&leaf.reading).unwrap_or_else(|| {
panic!(
"{}",
PlanError::Unresolved {
ty: Box::new(leaf.reading.clone())
}
.message(original_ident)
)
});
emit_plain_decode(entry, arg_ident, arg_ty, on_err)
}
}
}
fn emit_plain_decode(
entry: &prebindgen_registry::TypeEntry<KotlinMeta>,
arg_ident: &syn::Ident,
arg_ty: &prebindgen_registry::flat::TypeRef,
on_err: &TokenStream,
) -> (Vec<TokenStream>, Vec<TokenStream>, TokenStream) {
use prebindgen_registry::flat::TypeKind;
fn is_mut_ref(t: &prebindgen_registry::flat::TypeRef) -> bool {
matches!(t.kind(), TypeKind::Ref { mutable: true, .. })
}
fn opt_ref_mut(t: &prebindgen_registry::flat::TypeRef) -> Option<bool> {
let TypeKind::Optional(inner) = t.kind() else {
return None;
};
match inner.kind() {
TypeKind::Ref { mutable, .. } => Some(*mutable),
_ => None,
}
}
let mut wire_params: Vec<TokenStream> = Vec::new();
let mut prelude: Vec<TokenStream> = Vec::new();
let wire = &entry.destination;
let conv = entry.converter_ident().clone();
let wire_ident = if matches!(wire, syn::Type::Ptr(_)) {
format_ident!("{}_ptr", arg_ident)
} else {
arg_ident.clone()
};
let wire_with_lifetime = annotate_jobject_with_lifetime(wire, "a");
wire_params.push(quote!(#wire_ident: #wire_with_lifetime));
let decode_call = if matches!(wire, syn::Type::Ptr(_)) {
quote!(#conv(&mut env, #wire_ident))
} else {
quote!(#conv(&mut env, &#wire_ident))
};
let arg_mut: TokenStream = if is_mut_ref(arg_ty) || matches!(opt_ref_mut(arg_ty), Some(true)) {
quote!(mut)
} else {
quote!()
};
if entry.pre_stages.is_empty() {
prelude.push(quote!(
let #arg_mut #arg_ident = match #decode_call {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
} else {
let stage0_ident = format_ident!("__{}_s0", arg_ident);
prelude.push(quote!(
let #stage0_ident = match #decode_call {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
let mut prev = stage0_ident;
let n = entry.pre_stages.len();
for (idx, stage) in entry.input_stage_order() {
let stage_fn = &stage.function.sig.ident;
let is_last = idx == 0;
let out_ident = if is_last {
arg_ident.clone()
} else {
format_ident!("__{}_s{}", arg_ident, n - idx)
};
let bind_mut: TokenStream = if is_last { arg_mut.clone() } else { quote!() };
prelude.push(quote!(
let #bind_mut #out_ident = match #stage_fn(&mut env, #prev) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
prev = out_ident;
}
}
let call_arg = match arg_ty.kind() {
TypeKind::Ref { mutable: true, .. } => quote!(&mut #arg_ident),
TypeKind::Ref { .. } => quote!(&#arg_ident),
_ if matches!(opt_ref_mut(arg_ty), Some(false)) => {
quote!(#arg_ident.as_deref())
}
_ if matches!(opt_ref_mut(arg_ty), Some(true)) => {
quote!(#arg_ident.as_deref_mut())
}
_ => quote!(#arg_ident),
};
(wire_params, prelude, call_arg)
}
pub(crate) fn emit_expanded_param(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
plan: &prebindgen_registry::expand::FoldPlan,
leaves: &[PlanLeaf],
orig_param: &syn::Ident,
on_err: &TokenStream,
emit: &prebindgen_registry::Emit,
) -> (Vec<TokenStream>, Vec<TokenStream>, TokenStream) {
let mut wire_params: Vec<TokenStream> = Vec::new();
let mut prelude: Vec<TokenStream> = Vec::new();
let mut leaf_locals: Vec<syn::Ident> = Vec::new();
debug_assert_eq!(plan.leaves.len(), leaves.len());
for (leaf, classified) in plan.leaves.iter().zip(leaves) {
let leaf_ty = &leaf.ty;
let leaf_ty_tokens = emit.spell(leaf_ty);
let lookup_entry = || {
registry.input_entry(&leaf.ty).unwrap_or_else(|| {
panic!(
"{}",
PlanError::UnresolvedLeaf {
ty: Box::new(leaf.ty.clone()),
param: orig_param.clone(),
}
.message(orig_param)
)
})
};
let local = format_ident!("__exp_{}", leaf.name);
if let InputKind::FlattenStruct(flat) = &classified.kind {
for flat_leaf in &flat.leaves {
let ident = &flat_leaf.native_ident;
let wire = &flat_leaf.native_wire_ty;
wire_params.push(quote!(#ident: #wire));
}
let (decode, _) = render_flat_input_decode(flat, &local, on_err, emit);
prelude.push(decode);
leaf_locals.push(local);
continue;
}
if let InputKind::OptionScalar(sp) = &classified.kind {
let present_ident = &sp.present_ident;
let value_ident = &sp.value_ident;
let value_wire = &sp.value_wire;
let inner_conv = &sp.inner_conv;
wire_params.push(quote!(#present_ident: jni::sys::jboolean));
wire_params.push(quote!(#value_ident: #value_wire));
let built = build_through_wrappers(
&sp.arg_wrappers,
quote! {
if #present_ident != 0u8 {
let __v = match #inner_conv(&mut env, &#value_ident) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
::core::option::Option::Some(__v)
} else {
::core::option::Option::None
}
},
)
.expect("an option-scalar plan is built only for a buildable spelling");
prelude.push(quote!(
let #local: #leaf_ty_tokens = #built;
));
leaf_locals.push(local);
continue;
}
let is_consume = matches!(classified.kind, InputKind::Handle { direct: true })
&& !matches!(
leaf_ty.kind(),
prebindgen_registry::flat::TypeKind::Ref { .. }
);
if is_consume {
let wire_ident = format_ident!("{}_ptr", leaf.name);
wire_params.push(quote!(#wire_ident: jni::sys::jlong));
prelude.push(quote!(
if #wire_ident == 0 || (#wire_ident & 1) == 1 {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, "Operation on a closed native handle.");
return #on_err;
}
let #local: #leaf_ty_tokens = unsafe {
*std::boxed::Box::from_raw(#wire_ident as *mut #leaf_ty_tokens)
};
));
leaf_locals.push(local);
continue;
}
let entry = lookup_entry();
let wire = &entry.destination;
let conv = entry.function.sig.ident.clone();
let wire_ident = if matches!(wire, syn::Type::Ptr(_)) {
format_ident!("{}_ptr", leaf.name)
} else {
leaf.name.clone()
};
let wire_with_lifetime = annotate_jobject_with_lifetime(wire, "a");
wire_params.push(quote!(#wire_ident: #wire_with_lifetime));
let decode_call = if matches!(wire, syn::Type::Ptr(_)) {
quote!(#conv(&mut env, #wire_ident))
} else {
quote!(#conv(&mut env, &#wire_ident))
};
if entry.pre_stages.is_empty() {
prelude.push(quote!(
let #local = match #decode_call {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
} else {
let stage0 = format_ident!("{}_s0", local);
prelude.push(quote!(
let #stage0 = match #decode_call {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
let n = entry.pre_stages.len();
let mut prev = stage0;
for (idx, stage) in entry.pre_stages.iter().enumerate().rev() {
let stage_fn = &stage.function.sig.ident;
let out_ident = if idx == 0 {
local.clone()
} else {
format_ident!("{}_s{}", local, n - idx)
};
prelude.push(quote!(
let #out_ident = match #stage_fn(&mut env, #prev) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__e.to_string());
return #on_err;
}
};
));
prev = out_ident;
}
}
leaf_locals.push(local);
}
let qualify = |id: &syn::Ident| -> syn::Path {
let m = ext.fn_module(registry, id);
syn::parse_quote!(#m::#id)
};
let fold_expr = prebindgen_registry::expand::emit_fold(plan, &leaf_locals, &qualify);
let folded = format_ident!("__folded_{}", orig_param);
prelude.push(quote!(
let #folded = match #fold_expr {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
let __je = <__JniErr as ::core::convert::From<::std::string::String>>::from(__e);
signal_binding_error(&mut env, &__error_sink, &__SINK_MID, __SINK_FQN, __SINK_DESCR, &__je.to_string());
return #on_err;
}
};
));
let call_arg = match (plan.produces_option(), plan.by_ref) {
(true, true) => quote!(#folded.as_ref()),
(false, true) => quote!(&#folded),
(_, false) => quote!(#folded),
};
(wire_params, prelude, call_arg)
}