@@ -4151,6 +4151,27 @@ fn struct_input_body(
41514151 let java_path = handle_field_fqn ( ext, h) . replace ( '.' , "/" ) ;
41524152 let sig = format ! ( "L{};" , java_path) ;
41534153 let tmp_ident = format_ident ! ( "__{}_jobj" , fname_ident) ;
4154+ // Struct fields are owned, so a non-`Option` handle field owns
4155+ // its native object: decode by consuming (`Box::from_raw` → owned
4156+ // `T`), mirroring `struct_output_body`'s `Box::into_raw`. The
4157+ // borrow converter would yield `OwnedObject<T>`, which can't
4158+ // populate an owned field. `Option<_>` handle fields keep the
4159+ // niche-aware converter (jlong 0 ⇒ `None`).
4160+ let field_ty = & field. ty ;
4161+ let field_is_option = matches ! (
4162+ field_ty,
4163+ syn:: Type :: Path ( p) if p. path. segments. last( )
4164+ . map( |s| s. ident == "Option" ) . unwrap_or( false )
4165+ ) ;
4166+ let decode = if field_is_option {
4167+ quote ! { let #fname_ident = #field_conv( env, & #raw_ident) ?; }
4168+ } else {
4169+ quote ! {
4170+ let #fname_ident: #field_ty = unsafe {
4171+ * std:: boxed:: Box :: from_raw( #raw_ident as * mut #field_ty)
4172+ } ;
4173+ }
4174+ } ;
41544175 field_preludes. push ( quote ! {
41554176 let #tmp_ident: jni:: objects:: JObject = env. get_field( v, #camel, #sig)
41564177 . and_then( |val| val. l( ) )
@@ -4162,7 +4183,7 @@ fn struct_input_body(
41624183 . and_then( |val| val. j( ) )
41634184 . map_err( |e| <__JniErr as :: core:: convert:: From <String >>:: from( format!( #err_prefix, e) ) ) ?
41644185 } ;
4165- let #fname_ident = #field_conv ( env , & #raw_ident ) ? ;
4186+ #decode
41664187 } ) ;
41674188 field_init. push ( quote ! ( #fname_ident) ) ;
41684189 continue ;
0 commit comments