use prebindgen_registry::flat::{self, TypeRef};
use super::*;
use crate::jni::trait_impl::{build_through_erased_wrappers, build_through_wrappers};
pub(crate) struct VecBuildElem {
pub elem: TypeRef,
pub by_ref: bool,
pub elem_wrappers: Vec<&'static str>,
}
pub(crate) fn vec_build_elem(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
arg: &TypeRef,
) -> Option<VecBuildElem> {
let (run, by_ref) = match arg.unwrapped().kind() {
flat::TypeKind::Ref {
mutable: false,
inner,
..
} => (&**inner, true),
_ => (arg, false),
};
let wrapped_run = !arg.erased_wrappers().is_empty() || !run.erased_wrappers().is_empty();
if wrapped_run {
if by_ref {
return None;
}
build_through_erased_wrappers(arg, quote!(__probe))?;
build_through_erased_wrappers(run, quote!(__probe))?;
}
let elem = run.sequence_elem()?;
let elem_wrappers = elem.erased_wrappers();
if !elem_wrappers.is_empty() {
if by_ref {
return None;
}
build_through_wrappers(&elem_wrappers, quote!(__probe))?;
}
let elem = elem.unwrapped();
let plan = build_flat_input_plan(ext, registry, &format_ident!("e"), elem)
.ok()
.flatten()?;
if plan.contains_nested
|| plan.leaves.iter().any(|l| {
l.is_present_flag
|| l.handle_target_tail.is_some()
|| l.entry.as_ref().is_none_or(|e| !e.pre_stages.is_empty())
})
{
return None;
}
Some(VecBuildElem {
elem: elem.clone(),
by_ref,
elem_wrappers,
})
}
pub(crate) fn collect_vec_build_elem_types(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
) -> Vec<TypeRef> {
let declared = ext.declared_functions();
let mut seen: std::collections::BTreeMap<String, TypeRef> = std::collections::BTreeMap::new();
for f in registry.flat().functions() {
if !declared.contains(&f.name) {
continue;
}
for p in &f.params {
if let Some(v) = vec_build_elem(ext, registry, &p.ty) {
seen.insert(v.elem.key().as_str().to_string(), v.elem);
}
}
}
seen.into_values().collect()
}
pub(crate) struct VecBuildHelpers {
pub base: String,
pub plan: FlatInputPlan,
}
pub(crate) fn vec_build_helpers(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
elem: &TypeRef,
) -> Option<VecBuildHelpers> {
let plan = build_flat_input_plan(ext, registry, &format_ident!("e"), elem)
.ok()
.flatten()?;
if plan.contains_nested
|| plan.leaves.iter().any(|l| {
l.is_present_flag
|| l.handle_target_tail.is_some()
|| l.entry.as_ref().is_none_or(|e| !e.pre_stages.is_empty())
})
{
return None;
}
let key = elem.key();
let kt_fqn = ext
.types
.get(&key)
.and_then(|c| c.name_spec.as_ref())
.map(|s| ext.fqn_of(s))?;
let short = kt_fqn.rsplit('.').next().unwrap_or(&kt_fqn);
let mut chars = short.chars();
let base_lc = match chars.next() {
Some(f) => format!("{}{}", f.to_lowercase(), chars.as_str()),
None => short.to_string(),
};
Some(VecBuildHelpers {
base: format!("{base_lc}Vec"),
plan,
})
}
pub(crate) fn vec_helper_method_name(ext: &Declarations, base: &str, suffix: &str) -> String {
ext.mangle_jni_method(&format!("{base}{suffix}"))
}
fn vec_helper_symbol(ext: &Declarations, base: &str, suffix: &str) -> String {
ext.native_method_symbol(&vec_helper_method_name(ext, base, suffix))
}
pub(crate) fn build_vec_build_helper_items(
ext: &Declarations,
registry: &Registry<KotlinMeta>,
emit: &prebindgen_registry::Emit,
) -> Vec<syn::Item> {
let mut named: Vec<(String, syn::Item)> = Vec::new();
for elem_reading in collect_vec_build_elem_types(ext, registry) {
let elem = emit.spell(&elem_reading);
let Some(h) = vec_build_helpers(ext, registry, &elem_reading) else {
continue;
};
let new_sym = vec_helper_symbol(ext, &h.base, "New");
let push_sym = vec_helper_symbol(ext, &h.base, "Push");
let free_sym = vec_helper_symbol(ext, &h.base, "Free");
let new_id = syn::Ident::new(&new_sym, Span::call_site());
let push_id = syn::Ident::new(&push_sym, Span::call_site());
let free_id = syn::Ident::new(&free_sym, Span::call_site());
named.push((
new_sym.clone(),
syn::parse_quote!(
#[no_mangle]
#[allow(non_snake_case, unused_variables)]
pub(crate) unsafe extern "C" fn #new_id(
_env: jni::JNIEnv,
_class: jni::objects::JClass,
cap: jni::sys::jint,
) -> jni::sys::jlong {
let __cap = if cap > 0 { cap as usize } else { 0usize };
Box::into_raw(Box::new(Vec::<#elem>::with_capacity(__cap))) as jni::sys::jlong
}
),
));
let leaf_params: Vec<TokenStream> = h
.plan
.leaves
.iter()
.filter(|l| !l.is_present_flag)
.map(|l| {
let id = &l.native_ident;
let ty = &l.native_wire_ty;
quote!(#id: #ty)
})
.collect();
let mut decodes: Vec<TokenStream> = Vec::new();
let mut inits: Vec<TokenStream> = Vec::new();
for l in h.plan.leaves.iter().filter(|l| !l.is_present_flag) {
let conv = l.conv.as_ref().expect("non-present leaf has a converter");
let wid = &l.native_ident;
let fid = l.field.clone().expect("non-present leaf has a field");
let tmp = format_ident!("__e_{}", fid);
decodes.push(quote!(
let #tmp = match #conv(&mut env, &#wid) {
::core::result::Result::Ok(__v) => __v,
::core::result::Result::Err(__e) => {
tracing::error!("vecPush: decoding `{}`: {}", stringify!(#fid), __e);
return;
}
};
));
inits.push(quote!(#fid: #tmp));
}
let module = &h.plan.root.struct_module;
let sid = &h.plan.root.struct_ident;
named.push((
push_sym.clone(),
syn::parse_quote!(
#[no_mangle]
#[allow(non_snake_case, unused_mut, unused_variables, dead_code)]
pub(crate) unsafe extern "C" fn #push_id<'a>(
mut env: jni::JNIEnv<'a>,
_class: jni::objects::JClass<'a>,
handle: jni::sys::jlong,
#(#leaf_params,)*
) {
if handle == 0 {
return;
}
#(#decodes)*
let __elem = #module::#sid { #(#inits),* };
let __vec = &mut *(handle as *mut Vec<#elem>);
__vec.push(__elem);
}
),
));
named.push((
free_sym.clone(),
syn::parse_quote!(
#[no_mangle]
#[allow(non_snake_case, unused_variables)]
pub(crate) unsafe extern "C" fn #free_id(
_env: jni::JNIEnv,
_class: jni::objects::JClass,
handle: jni::sys::jlong,
) {
if handle != 0 {
drop(Box::from_raw(handle as *mut Vec<#elem>));
}
}
),
));
}
named.sort_by(|a, b| a.0.cmp(&b.0));
named.into_iter().map(|(_, item)| item).collect()
}