use prebindgen_registry::{Conversions, TypeEntry};
use super::*;
pub(crate) fn sentinel_for_wire(wire: &syn::Type) -> TokenStream {
if let syn::Type::Tuple(t) = wire {
if t.elems.is_empty() {
return quote!(());
}
}
if let syn::Type::Path(tp) = wire {
if let Some(last) = tp.path.segments.last() {
let name = last.ident.to_string();
return match name.as_str() {
"jboolean" | "jbyte" | "jchar" | "jshort" | "jint" | "jlong" => quote!(0 as #wire),
"jfloat" | "jdouble" => quote!(0.0 as #wire),
"JObject" | "JString" | "JByteArray" | "JClass" => {
quote!(jni::objects::JObject::null().into())
}
_ => quote!(unsafe { std::mem::zeroed::<#wire>() }),
};
}
}
if matches!(wire, syn::Type::Ptr(_)) {
return quote!(std::ptr::null());
}
quote!(unsafe { std::mem::zeroed::<#wire>() })
}
pub(crate) fn primitive_input(key: &TypeKey) -> Option<(syn::Type, syn::Expr)> {
let key = key.as_str().to_string();
Some(match key.as_str() {
"bool" => (
syn::parse_quote!(jni::sys::jboolean),
syn::parse_quote!(*v != 0),
),
"i32" => (syn::parse_quote!(jni::sys::jint), syn::parse_quote!(*v)),
"i64" => (syn::parse_quote!(jni::sys::jlong), syn::parse_quote!(*v)),
"u8" => (
syn::parse_quote!(jni::sys::jint),
syn::parse_quote!(::core::primitive::u8::try_from(*v).map_err(|_| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"u8 input out of range: {}",
*v
))
})?),
),
"u16" => (
syn::parse_quote!(jni::sys::jint),
syn::parse_quote!(::core::primitive::u16::try_from(*v).map_err(|_| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"u16 input out of range: {}",
*v
))
})?),
),
"u32" => (
syn::parse_quote!(jni::sys::jlong),
syn::parse_quote!(::core::primitive::u32::try_from(*v).map_err(|_| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"u32 input out of range: {}",
*v
))
})?),
),
"u64" => (
syn::parse_quote!(jni::sys::jlong),
syn::parse_quote!(*v as ::core::primitive::u64),
),
"f64" => (syn::parse_quote!(jni::sys::jdouble), syn::parse_quote!(*v)),
"String" => (
syn::parse_quote!(jni::objects::JString),
syn::parse_quote!({
let s = env.get_string(v).map_err(|e| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"decode_string: {}",
e
))
})?;
s.into()
}),
),
"Vec < u8 >" => (
syn::parse_quote!(jni::objects::JByteArray),
syn::parse_quote!({
env.convert_byte_array(v).map_err(|e| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"decode_byte_array: {}",
e
))
})?
}),
),
_ => return None,
})
}
pub(crate) fn primitive_output(key: &TypeKey) -> Option<(syn::Type, syn::Expr)> {
let key = key.as_str().to_string();
Some(match key.as_str() {
"bool" => (
syn::parse_quote!(jni::sys::jboolean),
syn::parse_quote!(v as jni::sys::jboolean),
),
"i32" => (
syn::parse_quote!(jni::sys::jint),
syn::parse_quote!(v as jni::sys::jint),
),
"i64" => (
syn::parse_quote!(jni::sys::jlong),
syn::parse_quote!(v as jni::sys::jlong),
),
"u8" | "u16" => (
syn::parse_quote!(jni::sys::jint),
syn::parse_quote!(v as jni::sys::jint),
),
"u32" | "u64" => (
syn::parse_quote!(jni::sys::jlong),
syn::parse_quote!(v as jni::sys::jlong),
),
"f64" => (
syn::parse_quote!(jni::sys::jdouble),
syn::parse_quote!(v as jni::sys::jdouble),
),
"String" => (
syn::parse_quote!(jni::objects::JString),
syn::parse_quote!({
env.new_string(v.as_str()).map_err(|e| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"encode_string: {}",
e
))
})?
}),
),
"Vec < u8 >" => (
syn::parse_quote!(jni::objects::JByteArray),
syn::parse_quote!({
env.byte_array_from_slice(v.as_slice()).map_err(|e| {
<__JniErr as ::core::convert::From<String>>::from(format!(
"encode_byte_array: {}",
e
))
})?
}),
),
_ => return None,
})
}
pub(crate) fn composed_inner_input(inner: &TypeEntry<KotlinMeta>, wire: TokenStream) -> syn::Expr {
let converter = inner.converter_ident();
if inner.pre_stages.is_empty() {
return syn::parse2(quote!(#converter(env, #wire)?))
.expect("single-stage input call is a valid expression");
}
let mut body = quote! {
let __inner_s0 = #converter(env, #wire)?;
};
let mut previous = format_ident!("__inner_s0");
for (order, (_, stage)) in inner.input_stage_order().enumerate() {
let stage_fn = &stage.function.sig.ident;
let next = format_ident!("__inner_s{}", order + 1);
body.extend(quote! {
let #next = #stage_fn(env, #previous)
.map_err(|__e| <__JniErr as ::core::convert::From<String>>::from(__e.to_string()))?;
});
previous = next;
}
body.extend(quote!(#previous));
syn::parse2(quote!({ #body })).expect("composed input chain is a valid expression")
}
pub(crate) fn composed_inner_output(
inner: &TypeEntry<KotlinMeta>,
value: TokenStream,
) -> syn::Expr {
let converter = inner.converter_ident();
if inner.pre_stages.is_empty() {
return syn::parse2(quote!(#converter(env, #value)?))
.expect("single-stage output call is a valid expression");
}
let mut body = TokenStream::new();
let mut previous = value;
for (order, (_, stage)) in inner.output_stage_order().enumerate() {
let stage_fn = &stage.function.sig.ident;
let next = format_ident!("__inner_s{}", order);
body.extend(quote! {
let #next = #stage_fn(env, #previous)
.map_err(|__e| <__JniErr as ::core::convert::From<String>>::from(__e.to_string()))?;
});
previous = quote!(#next);
}
body.extend(quote!(#converter(env, #previous)?));
syn::parse2(quote!({ #body })).expect("composed output chain is a valid expression")
}
pub(crate) fn option_input(
t1: &prebindgen_registry::flat::TypeRef,
registry: &impl Conversions<KotlinMeta>,
emit: &prebindgen_registry::Emit,
) -> Option<(syn::Type, syn::Expr, Niches)> {
let inner_entry = registry.input_entry(t1)?;
let t1_spelled = emit.spell(t1);
let inner_wire = inner_entry.destination.clone();
let inner_decode = composed_inner_input(inner_entry, quote!(v));
if let Some((slot, rest)) = inner_entry.niches.clone().carve() {
let pred = &slot.matches;
let returns_owned_object = inner_entry.metadata.is_direct_handle();
let body: syn::Expr = if returns_owned_object {
syn::parse_quote!({
if #pred {
None
} else {
Some(unsafe { OwnedObject::from_raw(*v as *const #t1_spelled).clone() })
}
})
} else {
syn::parse_quote!({
if #pred { None } else { Some(#inner_decode) }
})
};
return Some((inner_wire, body, rest));
}
if is_jni_primitive(&inner_wire) {
let unbox_method = jni_unbox_method(&inner_wire);
let unbox_sig = jni_unbox_sig(&inner_wire);
let getter = jni_unbox_getter(&inner_wire);
let getter_id = format_ident!("{}", getter);
let inner_decode = composed_inner_input(inner_entry, quote!(&__unboxed));
let body: syn::Expr = syn::parse_quote!({
if !v.is_null() {
let __unboxed: #inner_wire = env
.call_method(&v, #unbox_method, #unbox_sig, &[])
.and_then(|val| val.#getter_id())
.map(|__x| __x as #inner_wire)
.map_err(|e| <__JniErr as ::core::convert::From<String>>::from(format!("Option unbox: {}", e)))?;
Some(#inner_decode)
} else {
None
}
});
let wire: syn::Type = syn::parse_quote!(jni::objects::JObject);
return Some((wire, body, Niches::empty()));
}
None
}
pub(crate) fn option_output(
t1: &prebindgen_registry::flat::TypeRef,
registry: &impl Conversions<KotlinMeta>,
) -> Option<(syn::Type, syn::Expr, Niches)> {
let inner_entry = registry.output_entry(t1)?;
let inner_wire = inner_entry.destination.clone();
let inner_encode = composed_inner_output(inner_entry, quote!(value));
if let Some((slot, rest)) = inner_entry.niches.clone().carve() {
let none_value = &slot.value;
let body: syn::Expr = syn::parse_quote!({
match v {
Some(value) => #inner_encode,
None => #none_value,
}
});
return Some((inner_wire, body, rest));
}
if let Some(helper) = box_helper_for_wire(&inner_wire) {
let inner_encode = composed_inner_output(inner_entry, quote!(value));
let body: syn::Expr = syn::parse_quote!({
match v {
Some(value) => {
let __raw: #inner_wire = #inner_encode;
::prebindgen_jni_runtime::#helper(env, __raw)
.map_err(|e| <__JniErr as ::core::convert::From<String>>::from(format!("Option box: {}", e)))?
}
None => jni::objects::JObject::null(),
}
});
let wire: syn::Type = syn::parse_quote!(jni::objects::JObject);
return Some((wire, body, Niches::empty()));
}
None
}
pub(crate) fn is_jobject_wire(wire: &syn::Type) -> bool {
if let syn::Type::Path(tp) = wire {
if let Some(last) = tp.path.segments.last() {
return last.ident == "JObject";
}
}
false
}
pub(crate) fn is_jobject_shaped_wire(wire: &syn::Type) -> bool {
crate::jni::wire_access::is_jni_reference_wire(wire)
}
pub(crate) fn default_niches_for_wire(wire: &syn::Type) -> Niches {
if is_jobject_shaped_wire(wire) {
Niches::one(
syn::parse_quote!(jni::objects::JObject::null().into()),
syn::parse_quote!(v.is_null()),
)
} else {
Niches::empty()
}
}
pub(crate) fn enum_input_body(
ext: &Declarations,
registry: &impl Conversions<KotlinMeta>,
e: &prebindgen_registry::flat::Enum,
) -> (syn::Type, syn::Expr) {
let ident = &e.name;
let ident_name = ident.to_string();
let source_module = ext.fn_module(registry, ident);
let arms = e
.discriminant_values()
.unwrap_or_else(|name| {
panic!(
"enum `{}` variant `{name}` has a non-literal discriminant; use a literal \
integer value (e.g. `= 1`) or an implicit discriminant",
e.name
)
})
.into_iter()
.map(|(variant, value)| {
let lit = proc_macro2::Literal::i64_unsuffixed(value);
quote! { #lit => #source_module::#ident::#variant, }
});
let body: syn::Expr = syn::parse_quote!({
match *v as i64 {
#(#arms)*
other => {
return ::core::result::Result::Err(
<__JniErr as ::core::convert::From<String>>::from(
format!("invalid {} discriminant: {}", #ident_name, other)
)
);
}
}
});
(syn::parse_quote!(jni::sys::jint), body)
}
pub(crate) fn enum_output_body(
_ext: &Declarations,
_e: &prebindgen_registry::flat::Enum,
) -> (syn::Type, syn::Expr) {
let body: syn::Expr = syn::parse_quote!({ v as jni::sys::jint });
(syn::parse_quote!(jni::sys::jint), body)
}
pub(crate) fn nullable_kind_for(
outer_wire: &syn::Type,
inner_ty: &prebindgen_registry::flat::TypeRef,
registry: &impl Conversions<KotlinMeta>,
) -> NullableKind {
let inner_dest = registry
.input_entry(inner_ty)
.map(|e| e.destination.clone())
.expect(
"nullable_kind_for: Option<_> input handler reached here only after option_input \
returned Some, so the inner's input entry must exist",
);
if outer_wire == &inner_dest {
NullableKind::Niche
} else {
NullableKind::Boxed
}
}
pub(crate) fn nullable_kind_for_output(
outer_wire: &syn::Type,
inner_ty: &prebindgen_registry::flat::TypeRef,
registry: &impl Conversions<KotlinMeta>,
) -> NullableKind {
let inner_dest = registry
.output_entry(inner_ty)
.map(|e| e.destination.clone())
.expect(
"nullable_kind_for_output: Option<_> output handler reached here only after \
option_output returned Some, so the inner's output entry must exist",
);
if outer_wire == &inner_dest {
NullableKind::Niche
} else {
NullableKind::Boxed
}
}
pub(crate) fn owned_object_prerequisite_items() -> Vec<syn::Item> {
vec![
syn::parse_quote!(
#[allow(dead_code)]
pub(crate) struct OwnedObject<T: ?Sized> {
ptr: *const T,
}
),
syn::parse_quote!(
impl<T: ?Sized> std::ops::Deref for OwnedObject<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr }
}
}
),
syn::parse_quote!(
impl<T: ?Sized> std::ops::DerefMut for OwnedObject<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.ptr as *mut T) }
}
}
),
syn::parse_quote!(
impl<T: ?Sized> OwnedObject<T> {
#[allow(dead_code)]
pub(crate) unsafe fn from_raw(ptr: *const T) -> Self {
Self { ptr }
}
}
),
]
}