@@ -892,6 +892,43 @@ pub fn box_as_ireference(
892892
893893//
894894
895+ /// Boxes a plain object shaped like one of the struct types `IPropertyValue` supports
896+ /// (`{X,Y}`, `{Width,Height}`, `{X,Y,Width,Height}`, `{Duration}`, `{UniversalTime}`).
897+ fn box_struct_shape ( env : & Env , obj : & JsObject ) -> Option < windows:: core:: IInspectable > {
898+ use windows:: Foundation :: { DateTime , Point , Rect , Size , TimeSpan } ;
899+ let names = obj. get_property_names ( ) . ok ( ) ?;
900+ let len = names. get_array_length ( ) . ok ( ) ?;
901+ let mut keys: Vec < String > = Vec :: with_capacity ( len as usize ) ;
902+ for i in 0 ..len {
903+ let k: JsString = names. get_element ( i) . ok ( ) ?;
904+ keys. push ( k. into_utf8 ( ) . ok ( ) ?. as_str ( ) . ok ( ) ?. to_owned ( ) ) ;
905+ }
906+ keys. sort ( ) ;
907+ let get = |name : & str | obj. get_named_property :: < JsUnknown > ( name) . ok ( ) ;
908+ let f = |name : & str | {
909+ get ( name)
910+ . and_then ( |v| coerce_f64 ( env, & v) )
911+ . filter ( |n| !n. is_nan ( ) )
912+ . unwrap_or ( 0.0 ) as f32
913+ } ;
914+ let i = |name : & str | get ( name) . map ( |v| js_to_i64 ( env, & v) ) . unwrap_or ( 0 ) ;
915+ let keys: Vec < & str > = keys. iter ( ) . map ( String :: as_str) . collect ( ) ;
916+ let boxed = match keys. as_slice ( ) {
917+ [ "X" , "Y" ] => PropertyValue :: CreatePoint ( Point { X : f ( "X" ) , Y : f ( "Y" ) } ) ,
918+ [ "Height" , "Width" ] => PropertyValue :: CreateSize ( Size { Width : f ( "Width" ) , Height : f ( "Height" ) } ) ,
919+ [ "Height" , "Width" , "X" , "Y" ] => PropertyValue :: CreateRect ( Rect {
920+ X : f ( "X" ) ,
921+ Y : f ( "Y" ) ,
922+ Width : f ( "Width" ) ,
923+ Height : f ( "Height" ) ,
924+ } ) ,
925+ [ "Duration" ] => PropertyValue :: CreateTimeSpan ( TimeSpan { Duration : i ( "Duration" ) } ) ,
926+ [ "UniversalTime" ] => PropertyValue :: CreateDateTime ( DateTime { UniversalTime : i ( "UniversalTime" ) } ) ,
927+ _ => return None ,
928+ } ;
929+ boxed. ok ( ) ?. cast ( ) . ok ( )
930+ }
931+
895932pub fn napi_parse_pointer ( env : & Env , arg : & JsUnknown ) -> Result < NativeValue , AnyError > {
896933 let vt = value_type ( arg) ?;
897934
@@ -941,6 +978,15 @@ pub fn napi_parse_pointer(env: &Env, arg: &JsUnknown) -> Result<NativeValue, Any
941978 }
942979 }
943980
981+ if vt == ValueType :: Object {
982+ let obj: JsObject = unsafe { arg. cast ( ) } ;
983+ if let Some ( inspectable) = box_struct_shape ( env, & obj) {
984+ let ptr = inspectable. as_raw ( ) as * mut c_void ;
985+ std:: mem:: forget ( inspectable) ;
986+ return Ok ( NativeValue { pointer : ptr } ) ;
987+ }
988+ }
989+
944990 let pointer = if vt == ValueType :: External {
945991 ptr_from_external ( env, arg) . ok_or_else ( || {
946992 type_error ( "Invalid FFI pointer type, expected null, External, or { handle: External|null }" )
@@ -1143,7 +1189,10 @@ pub fn append_struct_field_bytes(
11431189 value : & JsUnknown ,
11441190 native_type : & NativeType ,
11451191) {
1146- let num = coerce_f64 ( env, value) . unwrap_or ( 0.0 ) ;
1192+ let num = match value. get_type ( ) {
1193+ Ok ( ValueType :: Undefined | ValueType :: Null ) => 0.0 ,
1194+ _ => coerce_f64 ( env, value) . unwrap_or ( 0.0 ) ,
1195+ } ;
11471196 match native_type {
11481197 NativeType :: F64 => buf. extend_from_slice ( & num. to_le_bytes ( ) ) ,
11491198 NativeType :: F32 => buf. extend_from_slice ( & ( num as f32 ) . to_le_bytes ( ) ) ,
@@ -1157,6 +1206,12 @@ pub fn append_struct_field_bytes(
11571206 NativeType :: I8 => buf. extend_from_slice ( & ( num as i8 ) . to_le_bytes ( ) ) ,
11581207 NativeType :: U8 => buf. push ( num as u8 ) ,
11591208 NativeType :: Bool => buf. push ( if coerce_bool ( env, value) { 1u8 } else { 0u8 } ) ,
1209+ // Ownership moves into the bytes; the struct reader releases it, call args leak it.
1210+ NativeType :: String if matches ! ( value. get_type( ) , Ok ( ValueType :: String ) ) => {
1211+ let hstring = ManuallyDrop :: new ( hstring_from_js_value ( env, value) ) ;
1212+ let raw: usize = unsafe { std:: mem:: transmute_copy ( & * hstring) } ;
1213+ buf. extend_from_slice ( & raw. to_le_bytes ( ) ) ;
1214+ }
11601215 _ => buf. extend ( std:: iter:: repeat ( 0u8 ) . take ( native_type. size ( ) ) ) ,
11611216 }
11621217}
@@ -1455,6 +1510,19 @@ pub fn try_unbox_property_value(env: &Env, raw: *mut c_void) -> Option<JsUnknown
14551510 let g = unsafe { pv. GetGuid ( ) } . ok ( ) ?;
14561511 env. create_string ( & format ! ( "{g:?}" ) ) . ok ( ) . map ( |v| as_unknown ( env, v) )
14571512 }
1513+ PropertyType :: Point => boxed_struct ( env, "Windows.Foundation.Point" , & unsafe { pv. GetPoint ( ) } . ok ( ) ?) ,
1514+ PropertyType :: Size => boxed_struct ( env, "Windows.Foundation.Size" , & unsafe { pv. GetSize ( ) } . ok ( ) ?) ,
1515+ PropertyType :: Rect => boxed_struct ( env, "Windows.Foundation.Rect" , & unsafe { pv. GetRect ( ) } . ok ( ) ?) ,
1516+ PropertyType :: TimeSpan => boxed_struct ( env, "Windows.Foundation.TimeSpan" , & unsafe { pv. GetTimeSpan ( ) } . ok ( ) ?) ,
1517+ PropertyType :: DateTime => boxed_struct ( env, "Windows.Foundation.DateTime" , & unsafe { pv. GetDateTime ( ) } . ok ( ) ?) ,
14581518 _ => None ,
14591519 }
14601520}
1521+
1522+ /// A boxed struct value (`IReference<Point>` etc.) as the same plain object a struct return gives.
1523+ fn boxed_struct < T > ( env : & Env , type_name : & str , value : & T ) -> Option < JsUnknown > {
1524+ let decl = metadata:: meta_data_reader:: MetadataReader :: find_by_name ( type_name) ?;
1525+ crate :: napi_engine:: ns_proxy:: create_struct_object_from_raw ( env, & decl, value as * const T as * const u8 )
1526+ . ok ( )
1527+ . map ( |o| as_unknown ( env, o) )
1528+ }
0 commit comments